Anda di halaman 1dari 1018

gnu:

Google

Aprenda a criar aplicaes para dispositivos mveis


com o Android SDK

|'iOVa'EC Ricardo R. Lecheta


Conhea tambm
do mesmo autor

A Amazon Web Services (AWS)


possui uma plataforma de
computao em nuvem, repleta
de servios para auxiliar a criao
de aplicaes com alta
disponibilidade e escalabilidade.
O objetivo deste livro
apresentar os principais servios
da AWS, desde o bsico ao
avanado, em uma metodologia
passo a passo e de forma prtica.
Voc vai aprender a criar
servidores virtuais na nuvem para
hospedar seu prprio site, utilizar
balanceadores de carga,
escalonamento automtico,
monitoramento de servios,
bancos de dados na nuvem I
armazenamento de arquivos,
entrega de contedo esttico e
dinmico, controle de permisses
e segurana, servio de e-mails,
servio de la para mensagens
assncronas, mobile push,
controle de custos etc.
DROI
Aprenda a criar aplicaes para dispositivos mveis
com o Android SDK

41 Edio

Ricardo R. Lecheta

Novatec
. ~ . _ . ~ )2/1998.
Copyright ? 2009, 2010. ZOIS. 2015 da Ntwatu l.liIOIl lldl
~ , . t _ pj ~ mt 'idos ela Let 9.610 ele I9/( l _ , ` ` _ _
'ludus txs LIIITIIUS n\t.t`\ lt .\`( P ` P pk' Or m|qu.- P|'0Q$$(), Scm prvll klUIUl'IZ:Igl0. POI' LSLFIIO,
pmibida ii reproduo desta obra. mesmo parul . P CI
do autor e da liditora.

Editor: Rubens Prates


Assistente editorial: Priscila A. Yoshimatsu
Reviso gramatical: Viviane Oshima
Editorao eletrnica: Carolina Kuwabata
Capa: Victor Bittow

ISBN: 978-85-757.2-440-3

Histrico de impresses:

Agosto/2015 Primeira reimpresso


junho/2015 Quarta edio (ISBN: 978-85-7522-440-3)
Outubro/2014 Quarta reimpresso
Abril/2014 Terceira reimpresso
janeiro/2014 Segunda reimpresso
Setembro/2013 Primeira reimpresso
Maro/2013 Terceira edio (ISBN: 978-85-7522-344-4)
junho/2010 Segunda edio (ISBN: 978-85-7522-244-7)
Maro/2009 Primeira edio (ISBN: 85-7522-186-O)
Novatec Editora Ltda.
Rua Lus Antnio dos Santos IIO
02460-000 - So Paulo. SP - Brasil
Tel.: +55 ll 2959-6529
Email: novatec@novatee.com.br
Site: novatec.eom.br
Twitter: twitrer.com/novateeeditora
Facebook: facebookcom/novatec
LinkedIn: Iinledin.com/in/novatec
OG20l50729
Este livro dedicado a toda a minha familia e principalmente aos meus pais,
Pedro e Maria Luisa, 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 a 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 co completo quando estamos juntos e voc
a dona do meu corao. Te amo.
H
Sumrio

Agradecimentos ........

Sobre o autor .........

Prefcio ...... . ................... .... . ...........

Captulo 1 - Introduo ao Android ..................


1.1 Introduo ..............................................
1.2 Open Handset Alliance e o Android ........
13 Sistema operacional Linux ................
1.4 Cdigo aberto e livre ..............................
15 Mquina virtual Dalvik .................................
1.6 Mquina virtual ART (Android Runtime) ...........
1.7 Conhecendo um pouco mais sobre o Android ....
1.8 Android Developer Challenge ...........................
1.9 Google Play ...............................
1.10 T-Mobile G1 ....................................................
1.11 Google Nexus ...................................................
1.2 Um pouco sobre a histria e verses do Android
1.13 Android 15 (Cupcake) ......................................
1.14 Android 1.6 (Donut) ............................. . ..
1.15 Android 2.0 e 2.1 (Eclair) ........
1.16 Android 2.2 (Froyo) ...............
1.17 Android 23 (Gingerbread) ...............
1.18 Android 3.0 (Honeycomb) ..................
1.19 Android 4.0 (Ice Cream Sandwich) ......
1.20 Android 4.1 (]elly Bean) ...................
1.21 Android 4.4 (KitKat) .....................................
1.22 Android 5.0 (Lollipop) ....................................
1.23 Google I/O 2015 e o anncio do Android M .....

Captulo 2 I Congurao do ambiente de desenvolvimento.


2.1 Android SDK .....................................................
2.2 Requisitos de software e sistema .......
8 ..... ..43
23 Plataforma (verso do Android) ...... _______44
Google Android - 4 Ed

2.4Android Studio ............. ......... .MN47


2.5 Instalando os pacotes pe o _ ......... AXMLI... I'-USO
2.6 Intel Hardware Accelerated Execut1on_Man8ef (H 51
2.7 Criando um projeto no Android Studio ..................... W-S
2.8 Criando um oemulador
2.9 Executando (AVD)
projeto no emu a ........................
or ....... . .......... UHWO
. ....... 61
2,10 Algumas janelas importantes do Android Studio ........ HHW3
2.11 Aplicaes na tela principal (Home) -----~- --~~~ ''' 6 4
2.12 Entendendo um pouco mais sobre o emulador ....... ....... 6 6
2.13 ADB (Android Debug Bridge) .................................... ....... 6 8
2.14 Informaes bsicas sobre a resoluo do emulador ....... ....... 7 O
2.15 Como fazer o download dos exemplos do livro .......... ------

Capitulo 3 1 Conceitos bsicos do Android ............... ~------- 7 2


3.1 Estrutura do projeto no Android Studio ....... ------- 7 2
3.2 Arquivo AndroidManifest.xml ............... ------- 7
33 Classe MainActivity ............................... --~---- 7 8
3.4 Arquivo de layout activity_main.xml ......... ----~-- 8 0
3.5 Arquivo strings.xml ............................................................. ....... 83
3.6 Classe R ................................................................................. ....... 83
3.7 Informaes sobre como acessar recursos de texto e imagem ....... ....... 84
3.8 Arquivo build.gradle .............................................................. ....... 86
3.9 LogCat - Escrevendo mensagens de log ........ 88
3.10 Tratamento de eventos ............................ 90

Captulo 4 n Activity ..........

4.1 Activity ................................................................. 96


4.2 Classes FragmentActivity e AppCompatActivity ......................... 97
43 Ciclo de vida de uma activity ...................................................... 99
4.4 Ciclo de vida avanado - o que acontece ao rotacionar o celular? 105
4.5 Navegao entre telas e inicializao de uma nova activity . 106
4.6 Mais detalhes sobre a classe Bundle e como assar ^
O bsico sobre _ b P p3I'8lfl'l[I`OS ...... ......
action ar e como voltar para tela ameno r ........... ,,___,
1.14
115
4.8 Links teis ..................... -ooo..-ouooo..-.~--zsz. .
117

Caltulo 5 u Action Bar e temas ........


118
5.1 Introduo Action Bar ...................
5.2 Temas Holo e Material ........... 118

I Ong (alwa _ ...........


53 Projeto de exemplo sobre actil- .,..... .................... . _. 119
opoes de Visualizao dos action butt .................................... 124
127
5.5 Template de icones para os botes da action bar Ys, never, ifRoom) ............
128
Sumrio 9
5.6 Classe android.app.ActionBar ...... ...... 2 9
5.7 SearchView .............................. ....... 13 1
5.8 Action provider ........ ...... 13 3
5.9 Split action bar ............................ ...... 13 5
5.10 Up navigation ............................................................................... ...... 13 7
5.11 Navegao por tabs na action bar ............................................................ 138
5.12 ActionBarCompat - a biblioteca de compatibilidade da action bar ............ 141
5.13 Links teis .................................................................................... ...... 1 46

Captulo 6 I Interface grca - gerenciadores de layout ....... ....... ' 47


6.1 View ............................................................... ....... 1 47
6.2 Classe ViewGroup .......................................................... ....... 1 47
63 Congurando a altura e largura de uma view ..................... ...... 1 48
6.4 Entendendo as constantes wrap_content e match_parent ........ ...... 1 49
6.5 FrameLayout .................................................................... ...... 1 55
6.6 LinearLayout .................................................................... ...... 1 57
6.7 LinearLayout - controle do alinhamento layout_gravity ....... ...... 1 58
6.8 LinearLayout - controle do peso ........................................ ...... 1 59
6.9 TableLayout - uso de uma tabela com linhas e colunas ....... ...... 1 63
6.10 TableLayout e shrinl<Columns - contrao de colunas ...... ...... 1 64
6.11 TableLayout e strechColumns - expanso de colunas ...... ...... 1 65
6.12 TableLayout - criando um formulrio ...................... ...... 1 67
6.13 GridLayout ............................................. ...... 1 68
6.14 RelativeLayout ..................................... 170
6.15 AbsoluteLayout (deprecated) ............................................ ...... 1 73
6.16 Utilizando layouts aninhados para criar telas complexas ...... ...... 1 73
6.17 Criao de um layout pela API- LinearLayout ............... ....... 1 74
6.18 Criao de um layout pela API -TableLayout ............. ...... 1 76
6.19 ScrollView .................................................................... ...... 1 78
6.20 Alguns detalhes sobre a ActionBar e o up navigation ...... ...... 1 79
6.21 Layoutlnflater - inando arquivos XML ........................ ...... 1 80
6.22 Links teis ............................................. ................. ....... 1 8 1

Captulo 7 I Interface grca - View .......... ........ ' 82


71 Arquivo / res/ values/ strings.xml ....... ...... 1 82
7.2 Arquivo XML com as cores .................... ...... 1 83
73 Arquivo XML para criar um estilo CSS ......... ...... ...... ...... 1 8 4
74 Exemplo completo com estilos ................................................. ...... 1 85
75 View - A classe responsvel por desenhar elementos na tela .......... ...... 1 87
76 TextView e EditText - campo de texto para digitar informaes ........ ...... 1 88
77 AutoCompleteTextView ........................................................... ...... 1 89
78 Button e ImageButton ......... ................... _ .. 191
Google Android - 4 @d
10
193
79 CheclBox c ToggleButton ...... ._____ _ 196
71O RadioButton ................................... mm_2OO
7l Spinner ................................................ 202
7_f_2 ?rogressDialog -janela de progresso ........ HHMZOS
713 ProgressBar - barra de pr0grSS0 -~'''''' ___._._ ZQ8
7.4 'oast - alertas rpidos ................. . ........................ 209
75 AlertDialog - alertas para o usurio conrmar ......... .... 2 do
716 Layoutlnater - inflando um arquivo XML ........ ....... 2 io
7l7 List\/iew ............................... _ ......................... ....... 2 L4
7.18 .istView com adapter customizado ............. ........ 2 -8
71.9 GridView ..........................................
7.20 Gallery .......................................... 223
721
7.22 ViewPager
ViewPager ...............
+ TitleStr1p . .................
ou TabStrip . .......
....... .......
---~--2 28
723 ImageSwitcher .................................. ...... ------- 23 O
724 WebVieW ................................................................... ..-~.-- 23 2
725 Movimentando uma imagem pela tela com touch ....... ...-.-~ 23 5
7.26 Desenho manual com a classe Canvas .................. ..----- 23 3
727 Nunca utilize pixels ................................ ..----~ 2 40
Captulo 8 u Fragments ........................................................................... ........ 2 42
8.1 Como surgiram os fragments no Android 3.0 Honeycomb ......... ....... 2 42
8.2 Fragments muito mais do que dividir a tela em duas partes ....... ....... 2 44
83 API de Fragments ................................................................. ....... 2 49
8.4 Hello World fragment ..................................................... ....... 25 O
85 Utilizando fragments com action bar + tabs ....................... ....... 25 5
8.6 Utilizando fragments com action bar + tabs + ViewPager ....... ....... 25 8
8.7 Ciclo de vida de um fragment ............................................ ....... 2 61
8.8 Migrando um projeto que utiliza activity para fragments ....... ....... 2 64
8.9 Criando um layout dividido em partes nos tablets .............. ___.___ 2 72
8.10 Exemplos da API dos fragments ........................... 275
8.11 Back stack ............................................................ 278
8.12 Adicionando botes na action bar pelo fragment ...... 279
8.13 Salvando o estado de um fragment ................. 7
8.14 Vantagens de utilizar os fragments ....... .'..''' 781
8.15 Links teis ........................................ ```````
(aPtuIo 9 u Animaes .........
9.1 Drawable Animation ........ WW284
9.2 Classe Animation ...... '''~~ 2 84
93 View Animation ........ '~----- 2 85
94 AlphaAnimation ........ ~------ 2 87
.......287
Sumrio 11

9.5 RotateAnimation ...... 289


9.6 ScaleAnimation ........... .291
9.7 TranslateAnimation ........ 293
9.8 AnimationSet .............. 296
9.9 AnimationListener .......... ................... 298
9.10 Interpolator .................................................................. 298
9.11 O problema com a API de animaes no Android 2.x ...... 299
9.12 Property Animations ............................................... 30
9.13 Classe ValueAnimator ...................................... .301
9.14 Classe ObjectAnimator ....................................... 303LJ

9.15 ObjectAnimator - animao fade_in/fade_out ......... S15

9.16 ObjectAnimator - animao de movimento ......... 30 16


9.17 ObjectAnimator - animao de rotao ............ 307
9.18 ObjectAnimator - animao de escala ..................... 31"U '8

9.19 AnimatorSet - criando um conjunto de animaes ...... 340 18
9.20 AnimatorListener ...................................................... 31"U F9
4
9.21 ViewPropertyAnimator - animao do jeito fcil ......... -O
9.22 Classe ValueAnimator - outro exemplo ................... 311
9.23 Aplicando animaes no layout ........................ .......3_1
9.24 Aplicando animaes nos fragments ........................... .......312
9.25 Aplicando animaes ao navegar entre activities ............. .......3]_3
9.26 NineOldAndroids - animaes com compatibilidade ........ .......318
9.27 Linl<s teis ................................................................... .......3]9

Eaptulo 10 n Threads, Handler e AsyncTask ........ 321

10.1 Introduo ................................. .......321


10.2 Mtodo sendMessage(msg) .............. .... 324
_0_3 Mtodo post(runnable) ................................. 327
_>.4 Atualizando a view dentro de uma thread ...... ............ 328
z

4
10.5 Agendando tarefas contnuas na activity ..................................... ...... 333
'i121.6 Implementao de um tela Splash Screen para sua aplicao ........ ...... 335
10.7 AsyncTasl< ............................................................................. ...... 337
10.8 Download de imagens com a biblioteca Picasso ...... 342
0.9 Links teis ..........................................................
.

344

(aptulo 11 I Material Design .........


345
11.1 Introduo ................. 345
11.2 Tema Material ......... 346
11.3 Paleta de cores ..................... 347
11.4 Elevao de views .................... 349
11.5 Ripple -feedback ao toque ............ 352
11.6 Floating Action Button (FAB) ......... 355
Google Android - 4' 0d
112
......357
. ....... 371
.7 (;ird\/iew ...................................
RecyclerView ...................... ` ............. ..
.xg ~c0 -Qvgjgt) (Reveal Effect) ................................
.lO lixtraindo as cores de uma gura .................... g ...... ....... . 372
......3o
......39

1.11 Animaes com item compartilhado entre duas activities ...... ..... 3 78
.12 Cmpatibilidade com verses anteriores ........................... ...... 78
7.13 Links teis .............................................. ................... ----
........380
Captulo 12 | Toolbar ................ ---
12.1 Introduo Toolbar ...................................... ---~-- :
12.2 Utilizando a Toolbar como a action bar ................ ...... 8 6
12.3 Utilizando a API da Toolbar (modo standalone) ....... ------ 3
12.4 Links teis ............................_................._......... ...... 3 88

Captulo 13 n Navigation Drawer .......... ....... .... .------- 3 3 9


13.1 Criando o projeto ................................................. ...... 3 89
13.2 Customizando as cores do tema Material ........................ ....... 3 91
133 Criando a activity e o fragment base para o projeto ............... ...... 3 92
13.4 Classe Application - armazenando informaes globais ......... ...... 3 94
23.5 Biblioteca android-utils ..................................................... ...... 3 96
2.3.6 Como 0 Cradle encontrou a biblioteca android-utils ....... ...... 3 97
13.7 Congurando a Toolbar .............................................. ...... 3 98
3.8 Navigation Drawer ................................... __,__, 4 OO
3.9 Criando 0 menu overflow ........................... ,_,_,, 4 10
3.1.0 Navigation Drawer com Material Design ....... ___,___ 4 11
I3.11 Material Design no Navigation Drawer ....... ______ 4 15

.................
}3.12 Criando os fragments do projeto ............. _____,_ 4 18
_3_3 Links teis ...................................... _______ 4 21

24.1 Fragment com WebView .............. ....... 4 22


:4.2 Swipe to Refresh ...................................... 424
:43 Interceptando requisies no WebView ....... . 426
:4.4 Mostrando alertas com o Fragmentljialog .............
,_4.5 1:xecutandoJavaScript ................... 427
;IOmu'Ca9 <10JavaScripr
I ' .osmlndp COd'80 HTML no com a classe
WebView Android
............ 1
-,4.8 Links uteis .................................. ``````......433
433
oouooonunnnuonouz
Sumrio

Captulo 15 - RecycIerView e tabs ............


15.1 Criando as casses de domnio ........
15.2 Criando a lista de carros .............
153 Tabs e VieWPager .............
15.4 Navegao de telas ......
15.5 Links teis ...............

Captulo 16 I Parser de XML, JSON e testes unitrios .......


16.1 Lendo um arquivo local da pasta / res/ raw .......
16.2 Parser de XML .........................................
163 Parser de JSON .....................
16.4 Testes unitrios no Android ......
16.5 Mais informaes .................

Captulo 17 I Web services ........ ....... .......


171 Introduo .........................................................
172 Requisio HTTP para consultar o web service ......
173 Utilizando a classe AsyncTask ...................................
17.4 Biblioteca simples para encapsular a AsyncTask .........
17.5 Atualizao por Pull to Refresh .................. ...........
17.6 Vericando se existe conexo disponvel .......
177 Requisies HTTP com Get e Post ............
17.8 Web services com WSDL .......................
179 Links teis ......................

Captulo 18 - Persistncia .....................................................................................


18.1 Salvando as preferncias do usurio com a classe SharedPreferences
18.2 Activity de configuraes ..............................................................
183 Lendo e salvando arquivos ...........................................................
18.4 Trabalhando com arquivos na memria interna .........................
18.5 Trabalhando com arquivos na memria externa (SD card) .........
18.6 Outros mtodos da classe Context .....................................
18.7 Brincando de fazer cache .............................................
18.8 Banco de dados SQLite ..................................................
18.9 Criao de um banco de dados diretamente com a API ......
18.10 Insero de registros no banco de dados ........................
18.11 Atualizao de registros no banco de dados .......
18.12 Excluso de registros do banco de dados .......
18.13 Busca de registros no banco de dados .........
18.14 Mtodos da classe Cursor .................
18.15 Continuando o projeto dos carros ........
Google Android - 4' Ed5
14
._ . ~ zzdl ............. ....... _ 519
'81o \'isualizando o banco de dados com .i iu rlln:_I :c __,_,_ 520
. 521
18:17 \*isualizando o banco de dados com mn c ici c _ . ---- -~ 523
8.18 Banco de dados versus web service ....... ..... ~ ----- -~-- --~--- - - '
525
18.19 Adicionando ages na action bar .......... ---~
28.20 Editando o nome do carro ..................... sw
1.8.21 Excluindo um carro do banco de dados ...... ` ........ ...... . s;
`.8.22 Atualizando a lista com dados do web service ...... ------ ~
28.23 Modo de execuo da activity launch Mode ..... ------ S #6
28.24 Fazendo backup na nuvem .......................... ...... . O
28.25 Fazendo backup de um arquivo ..... ~--~~-~ 5
iaz 1_n1 mais ..............._............__. ..... . . ~--~-- 541
Captulo 19 u Action bar de contexto e compartilhamento ....... ...---.- 5 42
19.1 Introduo .............................................................. ...... 5 42
19.2 Detectando toques longos - OnLong(IlickListener ....... ...... 5 43
19.3 Ativando o ActionMode na action bar .................... ...... 5 44
19.4 Removendo os carros selecionados ............ ...... 5 51
l9.5 Compartilhando os carros selecionados ................. ...... 5 52
L9. Compartilhando as fotos dos carros selecionados ...... ...... 5 55
19.7 Links teis ............................................................ ...... 5 59

Captulo 20 u Intents ........................................................... ........ S 60


20.1 Intent - envio de uma mensagem ao Android ....... __.___ 5 (-,O
20.2 lntents explcitas e implcitas ........................ ______ 5 (31
20.3 Exemplos de intents nativas ....... '_____ ` 562
20.4 Permisses .............................................................................. ...... 5 68
20.5
20.6Retomando resultados
1ntentFilter de uma intent - startActivityForResult .... ...... S70
....................................................................... 5 68
20.7 Por que a MainActivity declara um <intent-lter>? .. . `577
20.8 Exemplo completo com intent customizada ..... . H `57_
20.9 Vericando se uma intent ser encontrada .'.`.' i 2
20,10 Interceptando aplicaes nativas .... ``'`' 580
20.11 Lendo cdigo de barras ............... `'`'' 581
20.12 Nomenclatura das intents ...... i ``````` S 83
20.13 Links teis ......................... '''~ 5585
85
Cliltulo 21 n Multimdia - udio, vdeo e cmera ____,_

ayer ....................
%;;*2.;f;*.1;;;*..:* e `'''''~
~~~8

;;gf1z~m.zizy@..i......z _..._._... iiziiiw" ~~ Sw
- uma
21.5 intent
do fico eYo cvieonativo
plzVideo\/iew
Cm 0 Cl21SSC er (1 . ........
'd.....
"'`''''.593
_.5 89 . 595
1
600
604
607
610
.612
.62
12 Links teis .................................... . 614

615
22.1 Introduo ............................................. 615
22.2 Google Maps Android API - Verso 2 ........ 616
223 Google Play Services .............................. 616
22.4 Gerando a chave de acesso dos mapas ....... . 617

22.5 Congurando o projeto ............................... 620


22.6 Adicionando o mapa no projeto dos carros ...... 624
22.7 Classe GoogleMap ......................................... 628
22.8 Localizao do mapa -latitude e longitude ........ ....... 629
22.9 CameraPosition - zoom .................................. 630
22.10 Gongurando o tipo do mapa ............... ........631
22.11 Colocando os conceitos em prticas ...... 632
22.12 CameraPosition ................................ 635
22.13 CameraPosition - bearing rotao .... .. 636
22.14 GameraPosition - tilt inclinao 637
22.15 Monitorando os eventos do mapa ......... 638
22.16 Marcadores ...................................................
22.17 Polyline - desenhando uma linha no mapa .....
oo neona 644 639

22.18 Links teis ................................................. 645

.........646
Captulo 23 I Google Play Services e localizao .......
646
23.1 Introduo .........................................
647
23.2 My Location ...................................................
233 Monitorando o GPS ( moda antiga) ................ 647
23.4 Monitorando o GPS (Fused Location Provider) . 649
65 0
23.5 Conectando-se ao Google Play Services ............
. 651
23.6 Obtendo a ltima localizao de forma eciente
655
23.7 API de localizao do Google Play Ser* ices .......
659
23.8 Desenhando uma rota entre dois pontos ....... 661
ouuo

23.9 Buscando um endereo .........................


23.10 Links teis .................... ......
661
Google Android - 4* ed|5
16
......662
Captulo 24 | Broadcastllecelver ....... M )
_!-LI lrurotlugw ~---~---~-~---- --~-~-~-~~'' 1 '''''''`` ' 'I `''''' ,_,,_ f uol
24.2 (`onliur;nulo u|u receiver le lorma t'F~il.l^ll`il ---'- Hom
2-l. t Lonlignrantlo um receiver' tle lorrna Llllllflllgil N37
_z__..
). ` 55 -Q|U urillilar
|,,| mat t';lSl nim` l`L'ml\'L'l`
ll\illl` :':l;lI|t't
----~---~'' '''''`''' ou_' Illtlllllt
'''`'`''''''' ' . .... . M1168
iitieiiati tle um reeeiver ao inieialivar sistema >l`'*""'"'I ''''' '''''
_!-L7 u|t~reepta|\tl tlllll II\&`l1H\l-ZN" SMS ''''^''''''' '''''''''`''''' ``'`'`''' ''''' ( ) 7 O
24,8 (fielo tlt' vltlal ...-~--~~-~---~---~- ---~-~---'--' I '~'''''' ''''' 7
2-l.l(lJelelamlo
2-W Mosrrantlo o traballio Para u|u SCl`VILL'...:.-I
urna noulieaeo para o usuario .......-.-. '''' ' 'ml
----~ _
2-l.|l links uteis ................-.z.--~~--------~ -----~~~~-~-~-'~~'' '```` l 2 /2

Captulo 25 1 Notlcatlon ................................................................................ ...... 6 73


25.1 Porque usar uma notilieaeao para se eomuniear com o ttsuario ..... ..... ( w73
25.2 (riantlo utna nt'ilita;l simples .................-.--~---~~---------------- ----- 74
25.3 *Ieatls-up tuitilieatious ................. ..... .... .-.- --~ 3
25.4 Notilieaies na tela de lwloqueio ........................................ ..... 6 82
255 lelriantlo uma zzz~t~zzzi grande (big view uoriliearions) ...... ..... 84
25.6 (Iriantlo uma notiiea`io com aes ............................... ..... (5 86
25.7 4~Il1t`t'l:tlltl uma notilieago ............._,_.,_,_,,_,_._.,. _,___ 6 88
25.8 Mais informaes sobre a elasse Pentlinglntent ...... _,_,, 6 88
25.9 -ixernplo com noril'ieao e BroatleastReceiver ........ ,,_,_ (w 81)
25.10 Mostrando uma barra de progresso na ntilit'aao _,_,_ ____, 5 )_z
lili l.inls uteis ..._,.,__,__,________,_`_____ ________',__________._'____'_. '`_._ L B

(PllI|0 26 I Il|armManagef ,,_,____,__,__,___________ ________._____________ _ _ 694


2(o.l Por que utilizar um alarme (agendar uma tarefa) _,,__, 694
292- Mmdl-l dll CllSSL' ^llll`lllM\l]|gL'|' _____,_______ ..__'_.'__ iiiii ( u
26.3 ^lzZC|l(llllLl(> um alarmf ,_.,_ _ ``''` ,S
2--4 RCPCl'lI1Ll0 0 alarm ,,,_, i iiliiii '''`` h 97
26.5 (llSSt' (alcnd;|~ _|__`__.___'___ '''''' 7m
26.6 Quando utilizar ou nao urn z.|zz.~z{z'-W" '````` ml
26.7 l.lIll<S fcig ____'''______ iiiili ---~- 7
Cililtulo 27 n Service e Joblnfo ........
llll|`()LlU;\() ______''__._' '_'.. ''' 7 O 4
{enu.ls ut 5fv<,S _'_________ ____ ...__ 704
F(::;));l:|Ulir.'c` Plfifll' LI [1] Srvju '___._ . ---
27:S lit--if E) Pi* WP iiiiiiiii .'''''''' '''`''''` ''''''''' ''~'-- -----..... . iiiii 7 ( ) 6
z z strvigo caxeeutantlo dr.-pois do ,,-
i tela de mm ''''` ''''' 7 (27
......7l.Z
Sumrio

216 Entendendo o ciclo de vida de um servio ........


217 A classe IntentService ....................................
218 Criando um player mp3 ...................................
219 Mtodo bindService(intent,con,ags) ...................................................
2110 Qual mtodo utilizar para iniciar um servio? ......................................
2111 Um servio em execuo contnua no consome muito processamento?
27.12 foblnfo - a nova API do Lollipop ........................................................
2113 Links teis ..................................... ....
Captu o 28 I GCM - Google Cloud Messaging ......... ....
28.1 O que push? ................................... ____
28.2 Como funciona o GCM ....................... ___,
283 Gerando a chave de acesso do GCM ........ ____
28.4 Obtendo o Project Number .............. ....
28.5 Executando o projeto de exemplo ........... ____
28.6 Enviando a mensagem de push .................. .......
28.7 Criando o projeto Android passo a passo ...... ....
28.8 Classe GoogleCloudMessaging ............................ ....
28.9 Congurando o projeto Android ................................ ....
28.10 Criando a activity para fazer o registro no GCM ........ ....
28.11 Links teis ............................................................ .......

Captulo 29 n Salvando o estado da aplicao ....................... ....


29.1 Troca de conguraes - conguration changes ........................... ....
29.2 Salvando o estado com o mtodo onSaveInstanceState(bundle) ...... ....
293 Salvando o estado com o mtodo setRetainInstance(boolean) ......... ....
29.4 A importncia de reter a instncia do fragment ........................... .......
29.5 Manter uma thread executando durante a troca de orientao ........ ....
29.6 Como bloquear a troca de orientao ...................................................
29.7 Dispositivos com teclado ......................................................................
29.8 Configurao android:congChanges e o mtodo onCongurationChanged
29.9 Salvando o estado no projeto dos carros ...............................................
29.10 Links teis ............................................... ..............

Captulo 30 lSUpOfI1d0 diferentes tamanhos de telas ....... ....


30.1 Unidades de medida ..................................
30.2 Tamanho de tela (screen size) ........
303 Proporo da tela (aspect ratio) ........
30.4 Resoluo e densidade da tela .............
30.5 O problema de utilizar pixels .................
30.6 DIP ou DP (density-independent pixel) ..... ....
30.7 Tabela de densidade dos dispositivos ...... .... O
Google Android - 4' 0dl
18 774
a densidade da tela ...... .--- ~ 775
308 Customizando as imagens conforme`
30.9 'I`ralmlhand com a unidade dp no Canvas ....................... 777
_3U.l0'lll1111I1l10 da tela ein dp .....................--z 777
30.11. Dimenses (dimen) ................................ 780
30.12 Qualicadores de recursos para tablets .... ........781
30.13 Links teis ............................................
783
Captulo 31 z Threads avanado - AsyncTask e Loader ....... 783
31.1 O problema com o ProgressDialog ............................... .... ..-.. 788
31.2 Conrrolando a troca de orientaao ao executar uma tas ...... ....-- 793
313 Executando a Async1ask de forma serial ou paralela .......... ....-- 794
31.4 Loader .......................................................................... ..-..-
.......808
31.5 Opinio do autor ........ .......809
31.6 Links teis ..............
Captulo 32 1 Agenda de contatos e content provider ......................................... ......... 8 10

32.2 URI- Immutable URI reference ...............................


323 Exemplos de provedores de contedo nativos ...........
32.4 Lendo os contatos da agenda ...................................
32.5 Como ler todos os telefones e a foto de um contato
32.6 Mostrando os contatos em um ListView ...................
816
32.1 Por que utilizar a classe ContentProvider provedor de contedo ............. 810

.. ........ 814
........821
812

32.7 Utilizando um CursorAdapter ........................... .......824


32.8 Utilizando um CursorLoader ............................... .......827
32.9 Monitorando a fonte de dados com um loader .......... .......828
32.10 Criando um provedor de contedo customizado ..... .......83O
32.11 Classe ContentProvider .......................................... ........831
32.12 Classe esttica Carros ......... .......838
32.13 Links teis ...................... .......84O

Captulo 33 1 SMS ..................................................

33.2 Criando um BroadcastRec ` -- ....... 841


33.1 Enviando SMS por intent ou pela API ____,____
845
333 Links teis ...................... werpara receber um SMS"""" '''''

(3PtuIo 34 n Gestos ................ 849


34.1 Introduo .........................
.......849
34.2 Reconhecendo gestos previamente .......849
343 Detectando gestos comuns, como scroll late of .......857
34.4 Detectando gesto de pinch (Zoom) ra .........
345 Ltnks teis ......................... 861
.......866
19
Sumrio

Captulo 35 I Sensores e Google Fit ................................


867
35.1 Como obter a lista de sensores disponveis ......... 867
35.2 Testando os sensores ................................... 871
353 Sensor de luminosidade ....... .... 876
35.4 Sensor de temperatura ...... 878
35.5 Sensor de proximidade ........................................ 878
35.6 Sensor de acelermetro ........................................... 879
35.7 Movendo uma view pela tela com o acelermetro ......... 886
35.8 Google Fit ........................................................... 889
35.9 Links teis ........ 898

..........899
Captulo 36 - Bluetooth ........................................................
36.1 Vericando se o dispositivo suporta Bluetooth ...... 899
36.2 Ativando o Bluetooth por programao ............. 900
363 Listando os dispositivos pareados ........................ 902
36.4 Buscar novos dispositivos Bluetooth ........................ ........ 902
36.5 Deixando o Bluetooth visvel para ser encontrado ........ ........ 906
36.6 Criando um BluetoothDevice pelo endereo ......... 907
36.7 Chat em Bluetooth ........................................... 908

36.8 Conectando-se ao Bluetooth pela serial .......... .9_8
36.9 Links teis .......................................... .9L9

..........920
Captulo 37 u Reconhecimento de voz ............
920
371 Introduo .....................................................
372 Hello TTS - faa seu Android falar .................... .........921
373 Vericando o idioma e falando em portugus ........ 927
929
37.4 Reconhecimento de voz por intent ..................
932
375 Reconhecimento de voz por um listener ......
376 Links teis .............................................. 935

..........936
Captulo 38 u Gradle ........
936
38.1 Introduo ..............................
937
38.2 Gerenciando dependncias ........
383 Trabalhando com mdulos ........ 938
38.4 Trabalhando com bibliotecas .................................. ........ 941
........946
38.5 Criando uma biblioteca ................................................. ........ 941
38.6 Configurando um servidor Maven Sonatype Nexus
38.7 Publicando no Maven Central .................................... ........ 9 46
38.8 Flavors ................................... ........947
38.9 Classe BuildCong ....... .........951
20
. ............... 953
38.10 Assinando o aplicativo para o build release ...... .. 955
Google Android - 4 ed

38.11 Links teis ...................................................


957
Captulo 39 1 Android Wear .......... 957
39.1 Introduo ............................................... ....... 9 59
39.2 Hello World Wear ............................................. ....... 9 63
393 Conectando o smartphone no Android Wear ........ ------ 965
39.4 Conectando o smartphone no relgio fsico ....... ------ 966
39.5 Noticaes no wear ......................... ; ............ ------ 967
39.6 Noticaes com vrias pginas .................. ------ 968
39.7 Noticaes empilhadas ................... ------
39.8 Noticaes com comandos de voz ....... ------ 970
39.9 Google Play Services e Wearable API ........ ------ 972
39.10 Node API ........................................... ------ 973
39.11 Message API ....................................... ......... ....... 974
39.12 Data API .................................................................. ....... 9 75
39.13 Enviando mensagens entre o smartphone e o Wear ....... ....... 9 76
39.14 Enviando uma foto tirada pela cmera para o wear ....... ....... 9 86
39.15 Criando views e layouts para Wear .......................... ....... 9 89
39.16 Criando cards (cartes) ....................................... ....... 9 90
39.17 Criando listas ............................................ _______ 9 95
39.18 Criando pginas (ViewPager) ........................ _______ 9 97
39.19 Criando pginas em grid (GridViewPager) ....... ,___,_____ 9 99
39.20 Aplicativos em tela cheia (Full-Screen) ........... ________ 1 003
39.21 Animao de conrmao .......................... ________ 1 005
39.22 Alertas de sucesso e erro ........................... 1007
39.23 Interceptando eventos em background ....... 1008
39.24 Localizao e sensores ............................ 1009
39.25 Links teis ................... 1010
Captulo 40 - Google Play ................................... 101 1
o projeto
Assinando O aplicativo el Acorretamente
1Ol4
40.1 Controle da verso de sua aplicao .............
Compilando d d. ..........................
40.4 publicando no Google 518;-.n rol Studio/Gradle ....... ...... 1 013
405 Monetizao com annCiOSmm:.:: .............................. ______ 1 Ol3
40.6 Links teis ........................... '
1016
Agradecimentos

Este livro no teria acontecido sem a ajuda de diversas pessoas, algumas pela
contribuio tcnica e outras pela motivao.
Agradeo a todo o pessoal da Wasys,Tivit e Livetouch pelos incrveis projetos de
mobilidade, e a todo o pessoal tcnico da equipe pelas ideias e sugestes.
Agradeo a toda a comunidade Android pelo feedback e incentivo para continuar
escrevendo e lanar esta nova edio.
Em especial, agradeo ao Rubens Prates, editor da Novatec, por toda a calma e
orientao em todas as etapas da produo deste livro. Seus conselhos foram
fundamentais para tudo isso acontecer.
Agradeo Ana Carolina Prates, da Novatec, por todo o apoio de sempre.
Por ltimo, agradeo a voc pelo interesse em ler esta obra. Espero que a leitura
seja simples e empolgante.

21
Sobre o autor

Ricardo R. Lecheta formado em Cincia da Computao e ps-graduado em


d Sa un,
Gesto do Desenvolvimento de Software pela PUCPR. Tem certificaes
IBM e Rational, entre elas SCMAD (JZME) e SCEA (Arquiteto).
j desenvolveu projetos em JEE e .Net, alm de mobile, para grandes empresas
como Ambev HSBC, Ita, Rede, Renault, Nissan, Coca-Cola, Unimed, Botic
rio, Banco Bonsucesso, Bovespa, UOL, Globo, Mondial, Agncia Estado, Cosan,
Metalfrio, Polcia Federal, entre outras, e nos ltimos anos vem se especializando
na integrao de sistemas legados com a plataforma mobile.
Atualmente trabalha com desenvolvimento e consultoria de tecnologias mobile
para diversas plataformas e pode ser contatado pelo email rlecheta@gmaiI.com e
no facebook.com/ricardolecheta.

22
Prefcio

Assim que a primeira verso do SDK (ambiente de desenvolvimento) do Android


foi lanada, diversos sites da internet sobre tecnologia da informao j anuncia
ram que o Google estava lanando uma nova plataforma completa e totalmente
aberta para dispositivos mveis. A notcia percorreu o mundo, e era s o que se
comentava em todos os lugares. Todos discutiam se o Android ocuparia seu es
pao no mercado e quais seriam suas vantagens sobre os concorrentes. O fato de
ser lanado pelo Google, o gigante da internet, causou ainda mais curiosidade e
expectativa de todos os lados, atraindo a ateno de muita gente, desde usurios
comuns a grandes empresas e desenvolvedores em geral.
Anos depois do lanamento, podemos constatar que realmente o Android veio
para car e vem constantemente revolucionando o mercado de mobilidade. Atu
almente o Android est disponvel para diversas plataformas, como smartphones
e tablets, TV (Google TV), relgios (Android Wear), culos (Google Glass), carros
(Android Auto) etc., e o sistema operacional mvel mais utilizado no mundo.
O objetivo deste livro apresentar ao leitor este novo e fascinante mundo do
Android, que est revolucionando o desenvolvimento de aplicaes para celulares.
Para ler esta obra, recomendado um bom entendimento da linguagem Java e
experincia com o desenvolvimento de aplicaes em geral. Cada captulo do livro
tem um projeto de exemplo, que est disponvel para download gratuitamente
no site wwu/.livroandroid.com.br.

Os captulos 1, 2 e 3 deste livro so introdutrios sobre a arquitetura bsica do


Android e explicam como instalar o SDK e configurar o ambiente de desenvol
vimento no Android Studio. Os captulos 4 a 12 explicam recursos importantes
disponveis na plataforma, fornecendo uma base slida sobre a estrutura de uma
aplicao Android.
Do captulo 13 em diante vamos estudar diversos conceitos de forma prtica, de
senvolvendo o aplicativo dos carros passo a passo. O objetivo que voc aprenda
na prtica por meio de exemplos, variando do bsico ao avanado. O livro tambm
est atualizado para a ltima verso do Android e padres do Material Design.

23
24 Google Android - 4' edio
No nal ainda temos captulos especficos stwbre o novo sistema de builds do
Android (Cradle). desenvolvimento de aplicativos para relogios (Android Wear)
e corno publicar um aplicativo no Google Play
Espero que a leitura deste livro seja simples e ao mesmo tempo empolgante, que
os exemplos e explicaes possam l ief *er uma base slida para desenvolver
ornee
timas aplicaes e que voc possa aproveitar ao mximo o mercado de mobile,
que no para de crescer e est sempre procura de bons prossionais.

Ricardo Lecheta
http://facebook.com/ricardolecheta
https://pl us. googl c. com/ +RicardoLecheta
https://twitter com/rlecheta
` cAPruLo
Introduo ao Android
\___

1.1 Introduo
Nos dias de hoje, ningum consegue car longe de um celular, seja para mandar
um email, tirar uma foto, assistir um vdeo, conversar com os amigos, navegar
na internet, acompanhar as redes sociais etc. Portanto, os smartphones e tablets
atualmente so objetos praticamente inseparveis da maioria das pessoas.
Segundo pesquisas, mais de 3 bilhes de pessoas tm um telefone celular, e o
mercado de aplicativos virou uma febre, rendendo bilhes todos os anos.
Nesse mercado competitivo, temos vrios lados da moeda. Os usurios comuns
buscam um celular com um visual elegante, moderno, de fcil navegao, assim
como uma innidade de aplicativos e recursos. Tanto as empresas quanto os desen
volvedores buscam uma plataforma moderna e gil para desenvolver aplicativos.
Os fabricantes (LG, Motorola, Samsung, HTC, Intel, Sony etc.) precisam de uma
plataforma robusta e rica em funcionalidades para lanar no mercado os seus
produtos. E aqui onde o Android se encaixa, pois ele perfeito para todos os casos.
O Android o sistema operacional mvel do Google e atualmente lder mundial
nesse segmento. No entanto, o sucesso do Android no se deve apenas fora do
Google - por trs do desenvolvimento de toda a plataforma esto gigantes do
mercado de mobilidade, como fabricantes de celulares e operadoras. Esse grupo
que ajuda no desenvolvimento da plataforma chamado de OI-IA (Open Handset
Alliance) e conta com nomes de peso como Intel, Samsung, LG, Motorola, Sony
Ericsson, HTC, Sprint Nextel, ASUS, Acer, Dell, Garmin etc. Existe todo um
ecossistema interessado no desenvolvimento de uma plataforma mvel poderosa e
flexvel, de cdigo-aberto e que atenda s necessidades de todos. Embora o Google
represente grande parte da fora do Android, com certeza a plataforma est hoje
onde est devido ajuda de outras potncias do mercado mvel.

25
Google Android - 4 d

26
' d' vel vara diversas plataformas, como smarfplwnes
Atualmente oAndroid esta 1SPon } _ ~ , . 1 S (Goflgle Glass), carros
6 tablezg, TV (Google TV), relogios (Android,Wear),.ocu
mais utilizado no mundo.

. . zi '- _ 0
eracional movel

aseusseu
(Android Auto) e o sistema op ' bm est ca da vez mais utilizando 0
Vale lembrar que o mercado corporativo tam 0 rca es mveis
mobile, tanto que diversas empresas esto buscando incorpoflf ali l ,veis com

sis e - - _
da a dia ara agilizar seus negocios e integrar as ap ICHOS m
` t mas d) back end Empresas obviamente ortante
visam oespao
lucro; por
em isso, tar1IO
um mund0
05 smartphones quanto os tablets ocupam um imp u
em que a palavra mobilidade est cada vez mais conhecida.
Dentro desse contexto, estamos diante de uma excelente oportunidade, pois o mobile
um grande pilar na rea de tecnologiaisso
e segundo pesquisas
voc no e uma
pode car foradas areas que
dessa.
mais vai crescer nos prximos anos, por
O objetivo deste livro explicar o desenvolvimento de aplicativos para Android,
do bsico ao avanado, com diversos exemplos prticos e dicas de que voce vai
precisar no dia a dia.

A._7
1.2 Open Handset Alliance e o Android
A Open Handset Alliance (OHA) um grupo formado por gigantes do merca
do de telefonia de celulares liderados pelo Google. Entre alguns integrantes do
grupo esto nomes consagrados como Intel, HTC, LG, Motorola, Samsung, Sony
Ericsson, Toshiba, HTC, Huawei, Sprint Nextel, China Mobile, T-Mobile, ASUS,
Acer, Dell, Garmin e muito mais.
Quando 651 livro f0i SCfif0, O grupo era formado por 84 integrantes de peso
e voce pode verificar a lista completa e atualizada em: wwwopenhandsetalliance.

No st ' ' ' - ~ , _


com/oha__members.html.

1 e da OHA existe uma otima descriao do que e essa aliana. O texrg ggt

um d - .
em ingls e vou apenas traduzir uma breve citao aqui. Hoje, existem 1,5 bilho
de aparelhos de televiso em uso em todo o mundo e 1 bilho de pessoas tm
lar tornando o a arelho One Ce u
acesso internet. No entanto, quase 3 bilhes de pessoas tm um telef 1

- , construir um aparelho cel l ' . ,

, . or ' '
mllndo Dessa folma .OS produtos de consumo mais bem-sucedidos do
pessoas
de inmeras u arem
superior
ecno o ` ' - _
todo melhoraria
o mu . a vida
formado por empresas lderes emt ndp. A Open Handset Alliance um grupo
para muar xpaC er..movel
iencia . , gldemovel
todos osque compartilham essa viso
consumidores
Assim, o objetivo do grupo denir uma lataf
lares para deixar os consumidores mais s-
is eiftos
_ ma unica
com e abertanal.
o produto paraOutrg
celu
Captulo 1 i Introduo ao Android 27
objetivo principal dessa aliana criar uma plataforma moderna e exvel para
o desenvolvimento de aplicaes coorporativas. O resultado dessa unio, como
voc j deve saber, foi o nascimento do Android.
Todos acabam se beneficiando com os avanos alcanados pelo grupo OHA e a
plataforma do Android: os fabricantes de celulares, os usurios comuns e, claro,
as empresas em geral e os desenvolvedores de aplicaes.
Os usurios de celulares so extremamente favorecidos com tudo isso. Hoje em
dia, todos querem um celular com um bom visual, de fcil usabilidade, com
tela touch screen, cmera, msicas, jogos, GPS, acesso internet e muito mais,
e o celular cada vez mais ocupa um espao importante na vida das pessoas. O
Android foi criado justamente para agradar esses usurios, possibilitando que
encontrem todos os recursos esperados em apenas um aparelho. O mundo da
tecnologia est sempre em evoluo, e a OHA tem como objetivo principal manter
uma plataforma-padro na qual todas as novas tendncias do mercado estejam
englobadas em uma nica soluo.
Para os fabricantes de celulares, o fato de existir uma plataforma nica e consoli
dada uma grande vantagem para criar novos aparelhos. A grande vantagem para
eles que a plataforma tambm livre e de cdigo aberto. A licena do Android
flexvel e permite que cada fabricante possa realizar alteraes no cdigo-fonte
para customizar seus produtos, e, o melhor de tudo, sem necessidade de com
partilhar essas alteraes com ningum. O Android tambm free' portanto, os
fabricantes podem usar e abusar dele sem precisar pagar por isso.
O fato de o Android ser de cdigo aberto contribui muito para seu aperfeioamen
to, uma vez que desenvolvedores de todos os lugares do mundo podem contribuir
para seu cdigo-fonte, adicionando novas funcionalidades ou simplesmente
corrigindo falhas.
j os desenvolvedores de aplicaes podem desfrutar de uma plataforma de de
senvolvimento moderna com diversos recursos incrveis, com tudo o que h de
mais moderno. Este o tema deste livro: o desenvolvimento de aplicaes com o
Android. E aqui voc vai entender o porqu de toda essa revoluo.

1.3 Sistema operational Linux


O sistema operacional do Android baseado no kernel do Linux, que respon
svel por gerenciar a memria, os processos, threads, segurana dos arquivos e
pastas, alm de redes e drivers.
28
_ ~operaCl0
- - ocesso no sistema ' nal. Google Android - 4 2d

Al uns deles
~ - 'bir odem
uma te a para 0cxi
uSU_ .A_.
Cada aplicativo no Android disparq um novo primo e Outros podem car em
.g- qo em gundo plano por tempo indeterminado. Diversos processos eapll
Uicwg P AL
cativos os simultaneamente,
podem ser executade o kernel
I _do sistema operacional
o responsvel por realilf odo o controle de mem0fl21
, . z - decidir encerrar al um
Caso necessario, o proprio sistema oper8Cl0U3l Pf>d<{ _ _ g O

. _ , z - a vez
processo para libera r memria e recursos, e talvez ate reiniciar o mesmo proCeSS
d situa
posteriormente quan o a o
estiver controlada.
Toda a segurana do Android baseada na segurana do Linux. No Android,
cada aplicaao e executada em um unico processoe cada processo porlsu .
contm um a thread dedicada. Para cada aplicao instalada no celular e criado
um usurio no sistema operacional para ter acesso a sua estrutura de diretrios.
Dessa forma, nenhum outro usurio pode ter acesso a essa aplicaao.

1.4 Cdigo aberto e livre

O Android a primeira plataforma para aplicaes mveis completamente livre


e de cdigo aberto (open source), o que representa uma grande vantagem com
petitiva para sua evoluo, uma vez que diversas empresas e desenvolvedores do
mundo podem contribuir para melhorar a plataforma.

Para os fabricantes de celulares, isso tambm uma grande vantagem, uma vez que
e possivel utilizar o sistema operacional do Android em seus celulares sem ter de
pagar por isso. Alm disso, a licena Apache Software Foundation (ASF) permite
que alteraes sejam efetuadas no cdigo-fonte para criar produtos customizados
sem precisar compartilhar as alteraes com ningum.

Voce pode obtermaisinformaes e at fazer o download do cdigo-fonte do


Android no seguinte site: http://sourcaandroid.com/.

S ,
-,.m _ .
1.5 Mquina virtual Dalvik
Provavelmente voc '
J abs que
aplicaes para o Android. 3 lmgugem Java e utilizada para construir as
O fato
uma mquina virtualjava
. a verdade o ueUVM)
e que em seu sistema operacionalN
temos u ' '5' E
no exi t
chamada Dalvik que e otimizada , ~ q . . _ a maquma vlrtual
para XPCUGO em dispositivos mveis.

eAotodos
desenvolver as aplicaes para o Android voc v - .l. _
os seus recursos 2norm
m 1 ' _al uu lzar 3 linguagem Java
ente, mas depois que o bytecode (.c1ass)
Captulo 1 in Introduo ao Android 29
compilado ele convertido para o formato .dex (Dalvik Executable), que representa
a aplicao do Android compilada.
Depois disso, os arquivos .dex e outros recursos como imagens so compactados
em um nico arquivo com a extenso .apk (Android Package File), que representa
a aplicao final, pronta para ser distribuda e instalada. Ao utilizar o ambiente de
desenvolvimento do Android Studio, toda essa compilao e gerao do arquivo
.aple ocorre automaticamente, portanto, no preciso se preocupar com isso.
Atualmente, o sistema de build utilizado o Gradle, o qual independente do
Android Studio e pode ser executado separadamente. Portanto, voc pode com
pilar todo o cdigo por linha de comando se necessrio.

1.6 Mquina virtual ART (Android Runtime)


A partir do Android 4.4 (KitKat) foi criada a mquina virtual ART (Android
Runtime) com o objetivo de substituir a Dalvik, e naquela poca o ART podia
ser ativado opcionalmente nas configuraes. Quando foi lanado o Android 5.0
(Lollipop), o ART se tornou a mquina virtual padro, substituindo a Dalvik.
Uma das melhorias do ART a compilao Ahead-of-time (AOT), que tem o obje
tivo de otimizar o cdigo ao mximo para melhorar o desempenho do aplicativo.
O ART tambm tem um melhor funcionamento do Garbage Collector (GC) e
apresenta melhorias no suporte ao debug de aplicativos.
Na prtica os desenvolvedores ou usurios no so afetados se o sistema est
utilizando a Dalvik ou ART, mas o Google afirma que o ART apresenta um de
sempenho muito melhor.

1.7 Conhecendo um pouco mais sobre o Android


Todo celular tem uma tela inicial com alguns cones e um menu, certo? Todo ce
lular tambm tem uma agenda de contatos e uma tela para fazer a ligao, no ?
Agora, voc j pensou em trocar algumas dessas telas por uma tela customizada
desenvolvida por voc? Com o Android isso possvel. Sua arquitetura muito
flexvel e voc pode integrar aplicaes nativas com sua aplicao, ou at mesmo
substituir qualquer aplicao nativa existente por uma que voc mesmo criou.
isso que muitos fabricantes e operadores fazem ao customizar os aparelhos.
possvel integrar aplicaes de uma forma simples, sejam elas desenvolvidas
por voc, sejam aplicaes nativas. Por exemplo, imagine que sua aplicao
Google Androld - 4 d
30

precise consultar a agenda de contatos para selecioinpr dcte1;l:iI:1;l<>a2ll;;: (


Whats/\PP). e lol dPl5 Visuahzar O cndercgo ce ld d l bem, mas
existe a agenda de contatos e o Google MHP5 O Andrf io fila Sa qm Sm;
ser que possivel utiliza-los e integra-los em nossas apllC3%05- l;5P~ ft um;
jmcgmo uma das palavras-chaves em aplicaoes coorporativas, c a arqui c
do Android foi criada justamente pensando nisso.

, - ~ ' ~ ~ ' ' re


Nota: o Android tem muitos diferenciais in
(I`SSl'lS G Uml ll'qLlll[Ul'Ll

realmente flexivel focada na integraao de aplicaoes. Nao existe diferena em


uma aplicao nativa e uma desenvolvida por voce.

Falando em integrao, existe uma classe que o corao do Android, chamada


de Intent, a qual vamos estudar no livro. Essa classe nada mais e do que uma men
sagem enviada ao sistema operacional informando nossa inteno de realizar
determinada tarefa.
Ento, no sistema operacional do Android, mensagens so disparadas para todos
os lados, identificadas pela classe Intent. Conforme o contedo da mensagem,
ela pode ser interceptada por qualquer aplicao interessada a m de realizar a
tarefa que for necessria. Por exemplo, se voc deseja abrir uma aplicao nativa
como o browser ou abrir uma nova tela de sua aplicao, a nica coisa que voc
precisa fazer criar esse objeto Intent e congurar o contedo de sua mensagem
corretamente para ser interpretado pelo sistema operacional.
Outro ponto forte do Android que seu sistema operacional baseado no Linux, o
qual se encarrega de gerenciar a memria e os processos. Isso permite que diversas
aplicaes possam ser executadas ao mesmo tempo, de forma que as aplicaes

claro ` . _
em segundo plano consigam executar sem atrapalhar a atividade do usurio
enquanto ele est acessando a internet ou atendendo uma ligao. 3
_ I qfl 020 podemos nos esquecer dos recursos visuais e todas as APIs
_ er abertoque
e totalmente customizado
disponiveis, mas o fato de o Android s '
diferencial vale a pena ressaltar 6 um

1.8 Android Developer Challenge


Agora vamos falar L . un ouco da h' ' ' ~ _ .
. u , Oog ecome `ou in ~ ' ..
verde. Para PromoveroPl)ndroid o Glstmlla do Slsttma Operacional do mbozinho
3 lI'uueira verso do SDK foi lzulgatlu tambmgf ` vwtlnd pcddo e* asslm que
Android Develoher (hallen 3
vt; /Al) ` t u .ol mais
A ('l\Ul1l ammuad O 10
de U$ f=1m<>S<>
milhes CUCUTSOem prmios.
Captulo 1 I Introduo ao Android 31
Apenas por curiosidade, eu j trabalhava com mobile desde 2001 e foi nesse
momento que me encantei com o Android e comecei a escrever a 13 edio deste
livro, que ficou pronta em 2009, pouco depois da la fase deste concurso terminar.
O prazo para enviar as aplicaes do ADC era 14 de abril de 2008, e o concurso
foi dividido em duas fases. Na primeira fase, as 50 melhores aplicaes recebiam
US$ 25 mil e, na segunda, mais 20 das melhores aplicaes seriam selecionadas
para receber US$ 275 mil, e algumas US$ 100 mil.
Na primeira etapa, as aplicaes foram testadas no prprio emulador do Android,
porque na poca nenhum celular com o Android tinha sido lanado. Isso foi uma
grande sacada do Google para melhorar a plataforma e ajudar a test-la, sendo
que desenvolvedores de todo o mundo estavam interessados em desenvolver as
aplicaes para talvez faturar uma bolada. Esse concurso literalmente agitou o
mundo todo, com isso o Google conseguiu testar o SDK e consolidar seu produto.
A segunda parte do concurso foi anunciada para acontecer somente depois que
o primeiro celular com o Android fosse lanado, dessa vez as aplicaes seriam
testadas em um aparelho real e no mais em um emulador.

1.9 Google Play


Para auxiliar a distribuio das aplicaes do Android, alm da divulgao de
sua nova plataforma, foi criado o site Google Play (https://playgoogle.com), que
inicialmente se chamava Android Market. O objetivo do site fornecer aos desen
volvedores de aplicativos um lugar comum para disponibilizar suas aplicaes.
Para publicar uma aplicao, o desenvolvedor precisa pagar a taxa de US$ 25 (o
pagamento feito uma nica vez por meio de um carto de crdito internacional)
e concordar com os termos de uso. Depois disso, o aplicativo j pode ser publi
cado e instalado pelos usurios. Existem aplicativos que so gratuitos, enquanto
outros so pagos. Uma boa notcia para os desenvolvedores que 70% dos lucros
com os aplicativos vendidos sero repassados para quem os construiu. Para mais
informaes, visite o site do console do desenvolvedor no seguinte endereo.
https://playgoogle.com/apps/publish/

1.10 T-Mobile G1
O T-Mobile G1 desenvolvido pela HTC foi o primeiro celular lanado com a
plataforma do Android e, como esperado, agitou o mercado. A notcia_de seu
\CI
Google Android - 4 d
32
. _ - ~ s ex vectativas de vendas da
lanamento causou um grande impacto e superll 21 1 ddos de pr
HTC: mesmo antes de seu lanamento, todo o estoque para os p
-venda j havia sido esgotad.
a ser vendidos nos Estados UnidoS
Os primeiros celularesS$HTC G1 comearam , _
179. Um fato interessante e que eu termml
no dia 22 de outubro de 2008 por U
G1 l n ado e fiz todos meus estudos
a 1" ediao deste livro antes mesmo de o ser a _ ' _
1 curso de Android que 11lSU`l
somente utilizando o emulador. Lembro que no
alguns alunos tinham comprado o G1 e vieram mf! ITIOSUQC F01 @m0C10"3me'

1.11 Google Nexus


Desde o HTC G1 at os dias de hoje, o Android no parou de evoluir, e na po
ca em que este livro estava sendo escrito o Google havia acabado de lanar seu
smartphone Nexus 6, com Android 5.0 Lollipop, tela Quad HD de 6 polegadas
com 2560 x 144Opx e um processador quad core de 2.7 Ghz; o mais rpido de
todos os smartphones Android j lanados at o momento.
Recentemente, tambm foram lanados os tablets Nexus 7 e Nexus 10 do Google,
com telas de 7 e 10 polegadas. No site da linha Nexus, voc pode encontrar sem
pre os modelos mais atualizados dos smartphones e tablets ociais do Google.
Uma das vantagens de ter um smartphone Nexus porque eles so chamados de
Android puros, ou seja, no contm customizaes. E por serem gerenciados pelo
Google sempre recebem a atualizao de novas verses do sistema operacional de
forma rpida. Para mais informaes sobre a linha Nexus, e uma tima explicao
dos recursos do Android, visite 0 site:
http://www.goog1e.com.br/nexus/.

-._~1e
1.12 Um pouco sobre a histria e verses do Android

A verso 1.0 do Android chegou no mercado em 2008 com o famoso 'l` Mob`l

. - ~ ersao
c.,._ea e `
Gl, 6 depois o Android nao parou mais de evoluir,

Algo interessante e engraado sobre 0 Android que cada nova v ` ' 1


:inda
. pecarmhosamene com
ativa e especu o nome
a to de um
no mer ' doce. Isso gera sempre uma grziandle
, z 9 _oado
sera novoP015 t0d0s do
sabor ficam tentando adivinhar
Android * qu?-1I
Na1edi*
-_
Qdozestedlivro
quer 'expli
o 'que era o A ' . . , _
01 L osisttm. -~..
Pouco d1ferente,P0is o Andr ` 1 1 - ndri)ld` mas agora 3 hlstorm 9 Um
1 Operacional movel mais utilizado no
Captulo 1 il Introduo ao Android 33
mundo; por essa razo, acho conveniente explicar um pouco de sua histria, e o
que cada verso trouxe de novidades para a plataforma.

1.13 Android 1.5 (Cupcake)


Lanado em abril de 2009, o Cupcake (Figura 1.1) trouxe na poca diversas me
lhorias para o sistema operacional, como na parte de cmera, GPS, upload de
fotos e vdeos para o YouTube e Picasa etc.

Figura 1.1 -Android 1.5 (Cupcake).

A principal novidade, porm, foi o lanamento do primeiro Android (HTC Magic)


com apenas o touch screen e o teclado virtual. Foi no Cupcake que nasceram os
widgets, que so miniaplicativos que podem executar na tela inicial.
Fontes:

http://developer android. com/about/versions/android-1.5. html

http://developer android.com/about/versions/android-1.5-highlights.html

1.14 Android 1.6 (Donut)


Lanado em setembro de 2009, o Donut (Figura 1.2) inovou e fez o Android falar
e escutar.

Converter texto em voz o que chamamos de Text-To-Speech (TTS), e o contrrio,


converter voz em texto, chamamos de Speech-To-Text (STT). Com o auxlio das
pesquisas de voz, a home do Android ganhou mais funcionalidades e o usurio
poderia pesquisar na agenda de contatos, na galeria de msicas e na web com a voz.
Google Android - 4 0d
34

~_I. -1
, 'mw ~f

, ~ ' e an o Figura 1.2 -Android 1.6 (Donut).

No Android 1.6 foram criadas as medidas de densidade (ldpi, mdpi, WCP) que Vamos
estudar ao longo do livro, pois foi quando o Android passou a ser utillilzadodpo;
dispositivos de diversas resoluoes e tamanhos de tela. O Android estava c g rcado.
um novo patamar e comeando a ser amplamente utilizado e adotado pelo f1'l
Fontes:

http://developer android.com/about/versions/android-1.6.html

http://developer android.com/about/versions/android-1.6-highlights. html

1.15 Android 2.0 e 2.1 (Eclair)


Lanado em outubro de 2009 e depois atualizado em janeiro de 2010, o Eclair
(Figura 13) trouxe uma interface de usurio diferenciada e adicionou os Live
Wallpapers (plano de fundos animados na tela inicial).

`unt ` - . Figura 1.3 - Android 2.1 (Eclair).

No Eclair foi lanado o suporte a mltiplas contas do Google e sincronizao


(J o com aAPI), assim como diversas melhorias em todo o sistema operacional
como nas cameras, mapas e o suporte ao HTML5
3

Fontes:

hPf//developerandroid.com/about/versions/android2_O_ html

http://developerandroid.com/about/versions/android-2.0-highligh[5 hzml
'1P=//dfvelpfrandfoid. com/about/versions/android-2.1. html
Captulo 1 I Introduo ao Android 35
1.16 Android 2.2 (Froyo)

5
Lanado em maio de 2010, o Froyo (Figura 1.4) trouxe diversas melhorias de de
sempenho para o sistema operacional, como o compilador JIT Uust In Time) e
uma engine de JavaScript mais rpida.

` ,.'' 'i

Figura 14 - Android 2.2 (Froyo).

Nessa verso foram adicionados recursos clssicos como o USB Tethering e Wi~Fi
Hotspot, assim como o suporte ao Flash.
Fontes:

http://developer: aridroid.com/about/versions/android-2.2. html

http://developer aridroid.com/about/versions/android-2.2-highlights.html

1.17 Android 2.3 (Gingerbread)


Lanado em dezembro de 2010, o Gingerbread (Figura 1.5) trouxe novidades na
cmera, pois era possvel alternar entre a cmera frontal e traseira. Tivemos me
lhorias na funcionalidade de copy-paste, pois era possvel tocar o texto e depois
arrastar para controlar a seleo.

Figura 1.5 - Android 2.3 (Gingerbread).


36
_ e ir
. ~1 (- oo
_ .Ile,
. rfoi~ nessa
` = 3versao
um que
glande ganho com
tivemos Urela0
i _ 80
Skgunm ( ' 5 . z , . . 2 1(N *z Field C(_)I`ITlLll1lC8l()n5)
gerenciamento da bateria e surgiu o supor tc ao Nl (
Fontes:
Google Android - 4 Ed

ht tp://clevc1pcn android. com/ahout/versions/android-2.3. html

hit p://de vel open aridmid. ;om/ahut/ versions/and roid-2.3 -hi ghli ghts. html

1.18 Android 3.0 (Honeycomb)


Lanado em fevereiro de 2011, o Honeycomb (Figura 1.6) trouxe um sistema opera
cional totalmente focado nos tablets, com uma experincia de usuario totalmente
diferenciada para telas grandes.
Como Honeycomb, o Android deixou de ter botes fsicos, e os botes de voltar e incio
(home) passaram a fazer parte da barra de navegao dentro da tela com touch screen.
Foi nesta verso que tambm foi criada a action bar, que 0 padro de navegao mais
utilizado nos aplicativos para Android atualmente, e tambm a API de fragments, que
permite criar componentes reutilizveis de cdigo. Ambas as APIs so fundamentais
no desenvolvimento de aplicativos e por isso vamos estud-las em detalhes neste livro.
Fontes:

http://developer:android.com/about/versions/android-3.0. html

hl tp://developen android. com/about/versions/android-3.0highligh ts. html

Figura 1.6 - Android 3.0 (Honeycomb).

1.19 Android 4.0 (Ice Cream Sandwich)


Lanado em outubro de 2Oll o Ice Cream Sand . 1
tafmma de deSenvOlvimcnt,emr Sn1 iones
mpi wicie [figura
ta eisl.7)- unicou
~
r , permitindo eom
21 Pia
que
Captulo 1 1 Introduo ao Android 37
aplicativos para smartphones fossem criados com a action bar e fragments. Com
o ICS, o mesmo sistema operacional agora executava em tablets e smartphones.

Figura 1.7 - Android 4.0 (ICS).

AAPI de fragments utilizada para criar componentes reutilizveis de cdigo, por


isso ela tem muita importncia ao reaproveitar o cdigo de um aplicativo entre
as verses para tablet e smartphone.
Fontes:

http://developer android.com/about/versions/android-4.0. html

http://developer android.com/about/versions/android-4.0-highlights.html

1.20 Android 4.1 (Jelly Bean)


Lanado em junho de 2012, ojelly Bean (Figura 1.8) voltou a trazer ganhos signicatvos

i,. E
'\\
com relao ao desempenho do sistema, e todo o sistema operacional ganhou me

V\'~
'
lhorias no suporte s animaes, deixando a interface mais sensvel ao toque e fluida.
`= ..,`J .` ''
' ie.
~. ;?``
p, -...,., * g._5 _.
.x;._4_ -zzigg . ,Y-,.z,_

=. W
Q f ;>~,~E~<f
_ ifz -*
~:> -z.
EQ*
`~'
.,.. ,,. _ _,
' .;'- fff .~

Figura 1.8 - Android 4.1 (lelly Bean).


33 z - . ~ nter . __ ..azSU
_ , . l wassaiam
As notificaes que so tamosas no Androic 1
~ aisricasw
m
Google Android - 4 edi0

muitos detalhes.
Hmtc

Iztf['//d'v1]er:mdroid.umr/about/vcrsin5/cuybcan'html

1.21 Android 4.4


- . (KIKl
xe oAn roid para
d I Odm
e*
. - , . ~ z z* na ines ~ ~
~
Lanado em Outubro de 2013, O KHKM (hgulra 1.9ti1:(fLrl1 dispositivos com menos
pois conseguiu executaro sistemaoperacio _ _ 8 es
d 512MB de RAM devido s diversas melhorias de desempenho e ot1mlZ
feitas no sistema operacional.

Figura 1.9 - Android 4.4 (KitKat).

O KitKat trouxe aperfeioamentos no Bluetooth, NFC, Print Framework, senso


res, e foi criada a API de Transitions, que possibilitou aos desenvolvedores, no
s criarem interfaces visuais em cenas, como tambm animar a transio entre
uma cena e outra.

Na verdade, a plataforma do Android evolui to rpido, que para um resumo


completo recomendado olhar a documentao ocial.
Fonte:

http://developerandroid.com/about/versions/kitkat. html

1.22 Android 5.0 (Lollipop)


Lanado em novembro d ' '
na int, f ' d z . 6 2Vf<>L01l1pp<Figufz 1.1O) foi o maior release focado
U ace C u5ua Usabllldad, animaes e experincia do usurio.
Captulo 1 I Introduo ao Android 39
1.
L 1 /f
vi ff, j

lc'
Figura 1.10 - Android 5.0 (Lollipop).

Nasceu ento o Material Design, que um guia completo sobre como implementar
o visual, animaes e a interao entre os componentes de um layout, levando
em considerao que o Android se tornou uma plataforma comum para vrios
dispositivos, como smartphones, tablets (Android), Wearables (Android Wear),
culos (Google Glass), TVs (Android TV) e carros (Android Auto).
Isso o mais importante, uma vez que as tcnicas do Material Design no precisam
ser implementadas somente nos smartphones e tablets, pois o Google criou um
padro de design consistente entre vrias plataformas, como mobile, Web, Wear etc.
Dentre outras melhorias, tivemos as noticaes, que agora tambm aparecem
na tela de bloqueio (Lock Screen), e as head~up notications, que aparecem no
topo da tela com alta prioridade. Um exemplo de head~up notications a ligao
que permite atender ou rejeitar uma ligao telefnica diretamente na noticao.
Antigamente, esse recurso no existia e a aplicao da ligao mostrava uma tela
cheia para o usurio decidir se atende ou no a ligao.
Outra novidade interessante foi o projeto Volta, que trouxe ferramentas para
auxiliar a anlise do uso da bateria nos aplicativos. Tambm foi modicada a tela
de aplicativos recentes (Overview Screen), que mostra as ltimas tarefas que esto
sendo executadas, sendo que um aplicativo pode conter uma ou mais tarefas. Foi
criada uma API para os desenvolvedores controlarem esse comportamento. O
Lollipop tambm suporta a OpenGL ES 3.1, trazendo um desempenho superior
nos jogos 2D e 3D.
A plataforma do Android est chegando a outro patamar, e o Google TV tambm
recebeu um grande release. Foi criada a API Leanback para criar interfaces ricas
para TV e o TIF (Android TV Input Framework).
Novamente, so tantas as novidades que recomendo olhar a documentao oficial.
Fonte: http://developer android.com/about/versions/lollipop.html
Google Android - 4 edi

_ , , _ ve
40

1.23 Google I/O 2015 e o anncio do Android M

No Google i/o 2015 foi anunciado o Android M, qlj @_ 21 Pfffvla ]P1faleif';le O


dores da nova verso do Android. A letra M da sequencia 3 letra 6 O ypvgso

. . . - ze
_ z ' e outros.
Google est seguindo essa nomenclatura agora. Porem, oficialmente a nowa mo
do Android deve ser lanada entre outubro e novembro de O15, e somente 6
o esta versao comea com a letra
saberemos qual o novo sabor do Android. Com
M, algumas das suspeitas sao os famosos doces MGIM S, MCHIOS, dmf
Dentre as melhorias j anunciadas f r
no Android M, temos um leitor de imprSS0
digital com sensor biometrico e o Android Pa); uma plataforma aberta para 2
pagamentos por meio de cartes, internet e NFC.
Uma das funcionalidades que achei mais interessante o novo sistema de con
trole de permisses dos aplicativos, o qual possibilitar que 0 usurio conceda a
permisso individualmente. Ser possvel optar por dar acesso cmera enquanto
nega-se a permisso para acessar o GPS, por exemplo.
Outra funcionalidade do Android M muito comentada so os App Links, que
permitem que determinados aplicativos sejam escolhidos como padro ao abrir
links de determinado domnio. Exemplos clssicos so os apps do Tvvitter, Google
+ e Drive, e nesse caso qualquer link desses domnios pode ser aberto diretamente
em seus respectivos aplicativos sem a necessidade de perguntar ao usurio qual
aplicativo deve ser escolhido.

O Google Now tambm recebeu atualizaes e agora ele pode ser chamado
diretamente da tela de bloqueio, pela opo no canto esquerdo inferior da tela.
Enm, agora esperar pela nova verso do Android. No entanto, como desenvol
vedor, voc vai perceber que no Android SDK possvel baixar a verso prvia do
Android M e j ir brincando com o emulador.

Neste livro, vamos aprender a criar aplicativos para Android. Na maioria das
vezes, vou tentar manter a compatibilidade com as vers es antigas, at porque 0

bilidad ~ - . . P
que iremos estudar so os conceitos principais do Android

Felizmente o Google vem fazendo um excelente trabalho no suporte com ati


e com versoes 3m18S, C podemos utilizar novas funcionalidades por meio
de bibliotecas de compatibilidade. U
m exemplo disso a biblioteca de com ati
bilidade V7 que traz 30 AYO 2-1 (EClair) a funcionalidade da action barp
foi criada apenas no Android 3.0 (I-Ioneycombj que
Captulo 1 n Introduo ao Android 41
No livro, sero exploradas diversas APIs de desenvolvimento, do bsico ao avan
ado. Vamos focar boa parte em boas prticas de programao e interface de usu
rio, seguindo sempre as recomendaes (guidelines) do Google. Para isso, ser
desenvolvido, passo a passo durante a leitura, o aplicativo dos carros, explorando
muitos conceitos do Material Design.
Tenho certeza de que voc, ao ler este livro, vai adquirir uma base slida referente
a todos os conceitos do Android, desde o bsico ao avanado. Naturalmente, a
plataforma no para de evoluir, mas estou certo de que no nal da leitura voc
estar apto a acompanhar essa evoluo.
cAPiruLo 2
(ongufao do ambiente
ri
l
J .de desenvolvimento
V #7 _ _.,

_ - ~ ` ' essrio ins


Para iniciar o desenvolvimento de aplicaoes para o Android, e nec, .
talar o SDK que contm o emulador e todas as ferramentas necessarias para O
3

desenvolvimento.
Este captulo aborda a instalao do SDK e a congurao do ambiente de
desenvolvimento no Android Studio. Para validar a configuraao do ambiente,
criaremos um simples projeto para testar o emulador.

2.1 Android SDK

O Android SDK o software utilizado para desenvolver aplicaes no Android,


que tem um emulador para simular o dispositivo, ferramentas utilitrias e uma
API completa para a linguagem Java, com todas as classes necessrias para de
senvolver as aplicaes.

O Android SDK pode ser encontrado neste endereo: http://developer android com/sdk/.

Neste livro vamos utilizar o Android Studio para desenvolver aplicaes para
Android. No se .preocupe com o SDK neste momento, pois vamos baixar o
Android Studio e ele j contm o SDK.

O Android S ` " . .
2.2 Requisitos de software e sistema

tudio e SDK sao suportados nos seguintes sistemas operacionais:


Windows XP, Vista, 7 ou 8 (32 ou 64-bit)
Mac OS X 10.85 ou posterior (somente x8)
Linux (testado no Linux Ubuntu)
42
Captulo 2 I Congurao do ambiente de desenvolvimento 43
A seguir, veja as informaes sobre os ambientes de desenvolvimento suportados:
No mnimo 4GB de memria recomendvel. Minha experincia de que
necessrio no mnimo 8GB de memria.
Pelo menos 1GB livre no disco para instalar o Android SDK, emulador,
imagens de sistema, ferramentas e cache.
JDK 7 (apenas a JRE no o suciente). Caso a mquina j tenha um JDK
inferior instalado, como por exemplo o JDK 1.4, verique se as congura
es da varivel de ambiente PATH e JAVA_HOME do sistema operacional esto
utilizando as verses corretas.

Nota: ao instalar o Java, congure a varivel de ambiente JAVA_HOME do sistema


operacional para apontar para a pasta na qual o Java JDK est instalado.

2.3 Plataforma (verso do Android)


Antes de comearmos a brincar com o Android, importante entendermos o
que API Level. Como existem vrios dispositivos com Android no mercado,
possvel que cada um deles tenha uma verso diferente do sistema operacional.
Por exemplo, o primeiro smartphone Android, o HTC G1, tinha a verso 1.1, e os
novos smartphones esto saindo com as verses mais recentes.
No Android, uma verso do sistema operacional conhecida como plataforma.
Podemos dizer ento que existem diversas plataformas diferentes do Android (1.1,
1.5, 1.6, 2.x, 3.x, 4.x, 5.x etc.).

Cada plataforma tem um cdigo identicador, chamado de API Level. A lista a


seguir mostra a relao entre o cdigo API Level e cada plataforma:
AP _eve 1 - Corresponde plataforma do Android 1.0.
AP _eve 2 - Corresponde plataforma do Android 1.1.
AP _eve 3 - Corresponde plataforma do Android 1.5 (Cupcake).
AP _eve 4 - Corresponde plataforma do Android 1.6 (Donut).
AP _eve 5 - Corresponde plataforma do Android 2.0.
AP _eve 6 - Corresponde plataforma do Android 2.0.1.
AP _eve 7 - Corresponde plataforma do Android 2.1 (Eclair).
Google Androd - 4 @d
44 ' 7 'Fro o).
. ._ . Android 2-~ A Y
` - , 'ddo23 (Cingerbread)
- AP Leve' s ~ (im-rsi1 11 i*\="*' A
~ ~ - latalorma Androi
. ~ . . ' 'd 2.3.3.
. AP Leve 9 -- Corresponde A P
. AP -eve 10 - Corresponde a plataforma do Andfl
- AP
-eve ~ z ~ * Plataforma
11 Corresbonde * " do Android 3.0 (H0YCmb)'
~ AP eve 12 - Corresponde plataforma do Android 3-1
- AP -eve 13 - Corresponde plataforma do Android 3.2.
AP eve 14 - Corresponde plataforma do Android 4.0 (ICC Cfaln Sandwich).
. AP _@ve 15 - Corresponde pataforma do Android 4.0.3.
AP -eve 16 -Corresponde p`_ataforma do Android 4.1 Uelly Bean)
AP -eve 17 -Corresponde pataforma do Android 4.2 Uelly Bean)
AP -eve 18 - Corresponde pataforma do Android 43 Uelly Bean)
AP -eve 19 - Corresponde pataforma do Android 4.4 (KitKat).
AP -eve 20 - Corresponde plataforma do Android 4.4W (KitKat para
wearables).
API Level 21 - Corresponde plataforma do Android 5.0 (Lollipop).
API Level 22 - Corresponde plataforma do Android 5.1 (Lollipop MRI).
API LeveIX - Novas verses do Android vo continuar a contagem...
AAPI Level um nmero identicador valioso para aplicaes Android, uma vez
que, ao desenvolver aplicaes, necessrio denir quais sero os dispositivos
alvos. Dessa forma, se voc sabe que utilizar uma nova API que existe apenas
em smartphones Android 4.x ou superior, ser necessrio denir no projeto que
a API Level mnima suportada a 14 - compatvel com o Android 4.0 ICS.
Para obter a lista atualizada de API Level, visite este site:

http://developenrzndroid.com/guide/topics/manifcst/uses-sdla-clemcnt. html#ApiLevcls

2.4 Android Studio

_ nci G 1 , . . ,
Neste livro vamos adotar o Android Studio, que a IDE oficial de dcsenvolv`
para Android. O Android Studio foi anu ` d .I z lmemo
no Imelhj [DEA da JetBminS' a o no oog e I/O 2013 L c baseado
~ . ~ portantes se com 'ir A - ~
O Android
5111410 apresenta alguns diferenciais im
ECIIPSC, que antigamente era a ferramenta ocial pt ado do
Captulo 2 n Congurao do ambiente de desenvolvimento 45
1. Editor visual mais uido e com mais opes.
2. Sistema de build mais moderno baseado em Gradle (gradlaorg).
3. Diversas utilidades e facilidades ao desenvolver para Android, sendo muito
integrado ao Android SDK.
4. Templates de projetos para smartphones, tablets, relgios etc.
5. Atualizaes e melhorias frequentes.
Uma grande diferena entre o Eclipse e o Android Studio o processo de com
pilao dos projetos. No Eclipse cada projeto compilado do jeito clssico, como
qualquer projeto java dentro do Eclipse. Mas no Android Studio a compilao
feita pelo Gradle, que um moderno sistema de builds. Segundo o site oficial do
Cradle (gradlenorg), ele denido com a seguinte frase: Gradle combina o poder
e a flexibilidade do Ant com o gerenciamento de dependncia e convenes do
Maven, em uma maneira mais ecaz
Se voc no est acostumado com gerenciamento de dependncias, pode achar o
Gradle complicado no incio, mas que tranquilo que durante a leitura do livro
vamos praticar bastante. Com a prtica do dia a dia, voc vai se acostumar com
ele e aos poucos vai perceber suas vantagens.
Agora vamos colocar a mo na massa e baixar o Android Studio; acesse a seguinte
pgina, conforme a figura 2.1.
http://developer android.com/sdle/

Deve|0per5 - Design Develop Distribute Q 5


Training Apr Guides Reference T :mia Google Services Samples

NDK j

P g/:J
d dS d
K

Hip

E ld y
Spp tLb y
Fi

ADK
. .,,_..._.....e._,............. zz zi
Eclipse With ADT ' ~ Other Do.~.=nlrad Options
. Migralmg to Android Sludl
T ke a Survev

Figura 2.1 - Download do Android Studio.


46 _ _ _. - ..~ .` - -'rg ltaiuatr
r\ndroid tudio ~ _ _ 5Dl\
Google Android - 4' ed5

"-. ~.' \-'\ *i`&\I`0'\l1LI`DIeIllll.


_ Sm; -~* c o Android
Durante ;l ll\5f~lSl dtmm A hu I fl . . ~ mi vndido deixar o Android SDK
erioinstaladus. No\vi;.n'd de il1Sl@1l1*)L nu lt rnii 'io io b-ii\~ir'itu~iliza
~*`_
na msm do mimo Pam mo tcrmm Piiiiiismti iakestruturq dc Pastas como
C5 mz Dl\. Depois de tnst ll r ` l' i. 'un tcms 3 pasm dc instalagao
a exibida na figura 1.2. Na parte CSQULTK J L J l ^
o Android SDK foi instalado.
do Android Studio e na direita. onde

OStC:i AI'QUivosdeP'r09fam\s ' Andmid > Android Studio F


|~

Nome

giadie
I lib
K license
L DWQW
tuiic|.m
uceusem
E uoricem
Q unmstallexe

Figura 2.2 - Pasta de instalao Android Studio.


Ao executar 0 Android Studio voc vera um wizard inicial (Figura 23). Na poca em
que este livro estaxa sendo escrito o Android Studio estam na verso LOJ, mas voc ver
que so lanadas novas verses com frequncia, pois a ferramenta est em constante
evolugilo e sempre recebe melhorias. A vantagem que o processo de atualizao
automtico e voc receber uma alerta sempre que existir uma atualizao

\:\/elcoine to Android Studio


hmPa,zm
UI
_
_?-. '- ' Q
Q ~ o|'~t1:|e\n`wetnv'\=w*=ui3-$.tsu. Y; \. ,Q
..~ 2-. um
'3'$"1
r`. .\ _
l. c '
ms..
rw
I ; v' _, .
l...
\H\

(fg ` .\ _` `
.UM

, I?
. ..

gif [gq Y

*\'r~.nn'w..z1e;z'._.,\,m E 'W ' Ff\t1'z'u

Figura2.3-P' ._ .
rirnuru 'xiua do Android Studio
Captulo 2 I Congurao do ambiente de desenvolvimento 47
Antes de criar um projeto, recomendo clicar no boto (ongure do Wizard para
fazer o download e a instalao das plataformas (verses) do Android, embora o
Android Studio j venha com vrios itens pr-instalados. O wizard com as opes
de configuraes pode ser visto na gura 2.4. Nessa pgina do Wizard, clique no
link SDK Manager para abrir o utilitrio de instalao do Android SDK, a explicao
de como continuar est no prximo tpico.

Nota: o Android Studio contm o Android SDK, mas recomendado atualiz


lo utilizando o SDK Manager. Outra maneira de abrir o SDK Manager pelo menu
Tools > Android > SDK Manager caso o Android Studio esteja aberto.

Android Studio -- _
Welcome to Android Studio
Recentlrojects _, C fg
`lffDl' H H V S
An drordUtils _
4% Settings

E,
~...
Plugins

% Import Settings

[ Export Settings

i Project Default:

". .Den St _ 1 "'. ud 25.$64'l36. Chtbifdfpdilti.

Figura 2.4 - Conguraes.

2.5 Instalando os pacotes pelo SDK Manager


Para iniciar o desenvolvimento, necessrio baixar as plataformas do Android,
com o objetivo de criar os emuladores para cada verso do sistema operacional.
Essa instalao feita pelo SDK Manager. Aqui podemos baixar todas as platafor
mas do Android e suas respectivas documentaes, o driver USB do Google para
conectar um dispositivo na USB, as bibliotecas de compatibilidade, biblioteca do
Google Play Services, o acelerador de emulador da Intel (HAXM) etc. '
Google Android - 4' edi0

48 ` tl io tln $Dl' ;Il)CI'fu C Cult] il P.ln.[L:


.~\ natura .2.55.0.1
forma do Android
mostra o utilitzirio
instaladlf
estou mostrando, =1P*`"*lf
.L\:t;c ter uma base. 111115 WW qm No L `
instalados no meu Cvlimmdor Pam
_ _ HU
vrios outros itens _ ~5Dl\ .m1\usp.ll.ll
' _~d"'p . ~ -- instalaao.

Figura 2.5 - SDK Manager


" ___ _ _____.__-__..__ ._. _- --- f

d. z l ` " , ' ~ _ . izam


Nota: quando voc for fazer a instalao, baixe a ltima verso que estiver
ispomve . Os emuladores ARM sao mais lentos Os emuladores Intel 86 util
o acelerador da Intel (ver proximo tpico) e so mais rpidos.

. z get e sim
Instalar verses da plataforma do Android e bibliotecas pelo QDK Mana ' `
ples:voc entender
basta selecionar
queosprecisa
oestar
pacotes ' 'instalado.
desejados e clicar cm Install packages. O importante
Para comear, sempre importante
_ . ua izat os. que so
manteros trs primeiros itens at l` l
referentes ao SDK Tools, pois isso influencia diretamente na com
Android SDK Tools - Ferramenta pilao do cdigo.
s do SDK, como o emulador.

Android SDK Platform-tools - Ferramentas da plataforma do Android.


Captulo 2 (ongurao do ambiente de desenvolvimento 49
Android SDK Build-tools - Ferramenta de compilao. Ele extremamente im
portante, pois a verso que voc baixar aqui ser utilizada para compilar
o projeto com o Gradle. No arquivo buildgradle do projeto especificado
o cdigo da verso do build-tools que utilizado para fazer a compilao.
Na gura 2.5 podemos ver que, abaixo do item 5.1.1 (API Level 22), foram insta
lados vrios componentes:
Documentation for Android SDK - Documentao do SDK.

SDK Platform - Esse o item mais importante, pois a plataforma dessa verso
do Android. No diretrio do SDK ele ser instalado na pasta /sdk/platforms,
a qual contm as classes e APIs dessa verso do Android.
Samples for SDK - Documentao do SDK.
EABI v7a System Image - Imagem do emulador do Android. Voc pode baixar
a verso da Intel X86 ou ARM.
Android TV System Image - Imagem para criar o emulador do Android TV Voc
pode baixar a verso da Intel x86 ou ARM.
Android Wear System Image -Imagem para criar o emulador do Android Wear.
Voc pode baixar a verso da Intel ou ARM. 1
Google APIs System Image - Esse item idntico imagem do emulador conven
cional, mas ainda contm as APIs do Google. Recomenda-se sempre criar
o emulador com o Google APIs.
Mais abaixo na pasta Extras deixei instaladas as seguintes bibliotecas.
Android Support Repository - Repositrio utilizado pelo sistema de build do
Android Studio (Gradle).
Android Support Library - Biblioteca de compatibilidade com suporte s verses
mais antigas do Android. Contm vrias classes que permitem criar aplica
tivos que funcionem de forma coerente em todas as verses do Android.
Google Play Services - Bibliotecas adicionais do Google como Mapas V2, Lo
calizao, Google Fit, Google Drive, GCM (Push) etc.
Google Repository- Repositrio interno utilizado pelo Google.
Google USB Driver - Driver para os smartphones e tablets da linha Nexus do
Google. Esse item necessrio apenas no Windows.
Intel x86 EmulatorAccelerator(HAXM instaIler) - Esse item um acelerador de veloci
dade do emulador para Windows. Depois de baix-lo, necessrio instal-lo.
Com isso possvel criar os emuladores x86 que so mais rpidos.
so Google Android - 4 ed
r(HAXMl
2.6 Intel Hardware Accelerated Execution Manage
A l ` l -
O emulador .do'pornc
' lentido
r'lceUSB
n' z _ . Y ou
mitos descnvolvedorCS
c lunoso sua
` ` ~- I' n.:m/).
optam por desenvolver diretamente com um dispositivo ical plugado na
instalar emuladores de tcrceii'oS.C0l11> 0 (.CHym0fl<> (l1llP>-//WWWKf'")'""l 'I

Para solucionar esse problema de lentido do emulador, a Intel criou um acelerador


para o emulador. Isso e possvel graas ao Intel Hardware Accelcrated hxecution
Manager (HAXM). Com a tecnologia de virtualizaao, o emulador do Androt
consegue executar instrues a cerca de 80% da velocidade nativa do processador
host, o que signica na prtica um emulador cerca de cinco vezes mais rpido. O
Intel HAXM pode ser baixado pelo SDK Manager e tem suporte para os princi
pais sistemas operacionais: Windows, Mac OS e Linux. Depois de baix-lo, voce

r nte!
precisar entrar na pasta /sdk/extras/intel e instalar o software (Figura 2.6), pois e
leito apenas o download do instalador.

1 1 ` `
_ ` Welcome to the |ntet(R) Hardware Accelcrated Exeeutlon Mana er Installer
I' l*"~i~ UL' UL r^'~~1l1'^P^~',*'`H^f-l*^>'z"<=^v-~vto|~~,t.:tIntaIfz-)HAXM- '~ .
Introduction
11!A1Ill1I1l1 ,\
wullliztlo
, _ `v_ 1.
Fl
i 5 ,. r>~.-z~.zz~re
Iptti\

KI

Figura 2.6 -Instalando o acelerador do emulador da Im1_


Vale lembrar u eoHAX '~ .f t
, .. .
tualizao com asiafaz
QIntel
M e compativel
podera izarasima
T-X Caso
um I , x gf
V comcoi~..~~
processadores
`~ .
Intel e suporta vir
86er a instalacao com sucesso, voc

.x~-,.
TvdaInte
1 ~, que
gensx docmuladordoAndroid,/\ndroidWeareGooglc
aparecem no SDK Manager.
x86 Atom System8Ima
0fS, chamados de ~
Fsses emulad
que
sao os itens com o nome Intel

POIS utmzam O ` 86, sao muito mais rapidos que 1 e


emu' ~' . _ Clt' ' .visite
~a ,or
seuEnm
l d Processador ~ o s' `
se ' PUU1
e a memria
cn1pua{O. do ~ _or
accom ost
dpara
L ARM
Vamo
executar Q
noticia. Para mais informaes sobre O H;1Slal12lnte
doda
HAXM,
Intel: uma tima
Captulo 2 I Congurao do ambiente de desenvolvimento 51
https://software.intel.com/pt-br/blogs/2012/O6/25/decole-com-seu-emulador-android
https://soLware.im:el.com/pt-br/android/articles/intel-hardware-acceleratedexecution-manager

2.7 Criando um projeto no Android Studio


Depois de instalar os pacotes do SDK e baixar a verso do Android desejada,
vamos continuar e criar um projeto.
Abra o Android Studio e clique no link New Project do wizard. Feito isso, preencha o
formulrio com o nome do projeto, conforme a gura 21 O campo Application Name
o nome do projeto e o campo Company Domain o domnio de sua empresa. Eu usei
o site do livro como domnio. O item Package name derivado dos campos nome do
projeto e domnio, que nesse caso cou brcomlivroandroid.helloandroidstudio. O
pacote quem identifica o aplicativo no dispositivo e precisa ser nico, pois no
possvel publicar dois aplicativos com o mesmo pacote no Google Play
T?? Create New Project

Configure yaur new project

Application name HelleAndroidSudio Z j


Qompany Domain: j_lijroandroifc5om.bW 4% *ij
Package name: bnccm.l.re:md=:*id.he%!eandroz ci Edit
Project Iocaticru R:\temp\l~lellofndroiclStudio V _ _ O

z 1 f*=*==~* S

Figura 2.7 - Criando um projeto.

Por padro, o Android Studio vai criar os projetos na pasta do usurio no sistema
operacional, no subdiretrio AndroidStudioProjects. Eu particularmente costumo
mudar esse local para outra pasta.
Clique em Next para continuar. Na prxima pgina do wizard, selecione o item
Phone andTabIet para criar um projeto compatvel com smartphones e tablets (Figura
2.8), em seguida selecione no campo Minimum SDK a API Level 21 (Android 5.0), ou a
maior verso que existir na poca que voc estiver lendo o livro. _
S2 u
i f . voz
_ L `_` .,`.) t\-\ V .{< ..
|w_'_` .L Y., .__ zw aew. :-\
_ -.z.;=-\l\W -' '
Google Androld - 4 d5

z-- <'*

. . _ . - x -~ - 1)`l`
Figmu 2.8 - (riando um H'Ol<`fU

Observe que no vvtzard tamlwem reinos opcoes para ttiat pro]LIU> W111 *UI * 1 *
ao Google 'l`\'. Android \\'ear treiogiosi e Google (iias \UL`Ui0S)

Importante: ao criar seu primeiro pioieto .f\ndnid. escolha a maior versao dispomvel
no campo Minimum SDK. assim os exemplos explicados nos proximos topicos vao
funcionar. Se voc selecionar alguma versao antiga do Android. o proieto sera
criado com a biblioteca de compatiiilidade. tuas isso cstudaremos somente depois.

Na proxima pagina do wizard, voc pode selecionar um dos templates disponi


veis. Selecione o template BIanliActivity para criar mu pnojeto vazio com uma activity
simples (Figura 19). Uma activity e tuna classe que vai exibir tuna teia para o
usuario e controlar os eventos. sendo que esse wizard vai criar uma simples tela
com uma mensagem de hello xvorld.

Na proxima pgina do xvivard


.. ` .(Fit1ur~i
_. ~ r7 ~lili'evoti du 1' o
t tratar nome da classe
da activity Digite HainActtvity no campo Attivity Name e os demais campos sero
preenchidos automaticamentc. O campo Layout Name e telerente ao ;u-quim XM{
do layout da activity que sera gerado O campo Title o titulo que sua aplica *io
os itens de menu tu ~ f `- ' - i `
vai ter ao executar no emulador. O campo Menu Resourte Name e o XML que conira
l K M mo inse ridos na action bar.
Captulo 2 Congurao do ambiente de desenvolvimento 53
Q Create New Project
Add an activity to Mobilo

Ada No Activitys
r
{'" ` W "
i

I.. ._ .__ .. ...

Blllt V Blank Activity with Ffagmerrt

Fullscreen Activity Google Maps Activity Google Play Services Activity

Figura 2.9 - Denindo o template do projeto.


Create New Projeat sl
Choose options for your now fik

Croata a nen bknc activity wban action bar.

MW NM L"'*l'"ll: aaaa ____..s_,_L..-L l


LW N~~= lwrzfl ;. L
Tila: liaznmiy
Menukesourcehlame lmenu_m|n _ l

sunzazvuy

Figura 2.10 - Denindo o template do projeto.

Para os arquivos em XML, importante seguir o padro da converso de nomes.


Por exemplo, se sua activity se chamar LognActtvty.java, o arquivo XML vai se
chamar activity_login.xml, sempre comeando com a palavra activity, e o item de
menu se chamar menu_login.xrnl. Como teremos vrios tipos de XML no projeto,
essa conveno facilita encontrar e identicar os arquivos. '
54

1)) ._ z.-. _ -I__zrwtueoro*


'x'ir\ `um
' ` Wok
~*
_ - . ;` inish C a fuardc. l..g>
. .,u _ avrmc
Para finalizar o wizard e criar O Pl.lUO` Lhqm Im- hm IU; fa -tro esta
1 .`..~. '` - -
~ r~
dcpms dc Cnm- O projeto. voce vera uma iancla mlormm it I] .ld [5 J l 1 im
' L t'
sendo compiladopclo(iradlc (l`1gLlI`l2.1l),L]lIL t o sistema eq l Ico
vez t ue voce utilizar o Android tudio esse processo pode L Lm0f t l d *
pois o (iradle vai baixar seus pacotes c dependencias. l ()Ifzll11O, ccrtih 1
que voc possui uma conexo com a mtcrnct.

~ fadlfaurf
gumgmg 'H.ondroidS!udzo' Gradle project mic
Google Android - 4 @d

Cancel

Figura 2.11 - Derzindo o template do projeto.

Aqui exatamente o momento em que um bom computador pode fazer a diferena,


pois o sistema de build do Cradle pode apresentar certa lentido em mquinas
com baixas configuraes. Em meus testes um disco SSD faz bastante diferena
na velocidade de compilao, e claro que, quanto mais memria, melhor.
Depois de criar o projeto, a estrutura de diretrios deve ser como a gura 2.12,
que mostra o editor aberto no arquivo /res/layout/actii/ity_main.xm1_

' .".. .. 430


. LJ. ,\.__L
/
- xv-^ 'p.zz~. -, ....,. , ,. M 9 ,_, .

I " ' |'.=zz.~


*W ` H w- '<.-
.
UP ~'* :fuer un Iv-au um _ -,z . 0 * z- M... VL

.,..f
:-..
.~z.
zw A
"" mig ,n-un-

ez-
.. _.M
,:,,

F1 '

N. ' 1 -_ a-.:,a..1.~_ . . '_~'`A`


Fi u 2.12 - ' - ~
~ a.._fz ,L

8 f Projeto trzado e aberto no editor visual.


a gura eu destaquei com uma seta na es
'ti

. , _. _ ivose asta d ' "


Ira utiliza
qual mostra
querda um item de menu ue
customizar a forma de visualizao
a estrutura 'real
do ro. I Q permite
- de_arquPP Sjeto oa qualroestaeto.
marcada C
como-_ . .
Projeq
r muito a visualizaao Android, que mostra de arm um O tempo VOC
21 enxuta e agrupada os
Captulo 2 I (ongurao do ambiente de desenvolvimento 55
itens do projeto. Veja que tambm destaquei na barra de ferramentas do Android
Studio as principais opes que voc ir trabalhar no dia a dia, que so o boto
de Run/Debug, botes para abrir o SDK Manager e AVD Manager etc. Tambm
destaquei o arquivo app/buildgradle, que onde voc ir configurar informaes
sobre a verso do aplicativo e tambm declarar as dependncias.
Para executar o projeto, clique no boto Run conforme mostra a gura 2.13. Na
gura o combo mostra o valor app, referente ao mdulo app do projeto, pois um
projeto no Android Studio composto de vrios mdulos. Por exemplo, caso voc
crie um projeto com suporte ao Android Wear, existir a opo para executar a
verso mobile ou Wear do projeto.

zzzzz l af: ii. a


eo ~

Figura 2.13 - Barra de ferramentas para executar o projeto.

A gura 2.14 mostra o wizard para escolher o dispositivo que deve executar o pro
jeto. Neste caso ela est mostrando um smartphone Nexus 5 que est conectado
na USB. Agora basta clicar o boto OK para executar o projeto direto no dispositivo.
,Q Choose Device
__hoose a running device

_ _ _ Device __ __ _ eriaINumbe _ State Com atbie


, se Nem: 5 t z .mid 4.4.4u_ . 5f3;saob1iofa_ _

aun:h emulator

z [none]
:ame device for future launches

M
Figura 2.14 - Selecione 0 dispositivo ou emulador

Dica: para depurar em um dispositivo real conectado na USB, necessrio


habilitar nas configuraes a opo Se(urity> Unknown Sources (Segurana > Fontes
desconhecidas) e a opo Deve|0per0pti0n$ > USB Debugging (Opes do desenvolvedor
> Depurao USB). Vale alertar que o menu Developer Options no aparece'por
Google Android ~ 4 d

56
' ~ *f -f habilita-lo,\
f . ' . ~ , " _;
- - ' - ~~ tisoselccionai 1
I Q , `, mwP'
, ~ ' `( ` ' ) ` .
_ _ ~. 2 ,U;nerioi.laia ,
Numbe
P*l no Amlumlfi ( 3 sl c clicar sete vezes seguidas na oP%ao B QC um
_ _ _\ , (1mLI1S.1 L
About phone nas conllill" * ,, . ~ 1m informando qu 21210171 W
AO tum. IML Wu MMM um 0 tions eistar habilitado.
LiL`SL`ll\'UiVCLi()l` c o inenu Developer P *
_. Hello World..N05
._ , . gm a, ,mensagem
f se
Ylcxecutei
_ . . o,proieto
z dis ositivo concc
O iltado pode ser visto na gull 2-13 _ . . , tado na USB,
diretamente no P
m0lm Qu 3 d , Tizr e instalar a aplicao no emulador.
mas no prximo tpico vamos aprcn U I Q

Hello world'

Figura 2.15 - Projeto executando no dispositivo.

Nota: para executar o projeto diretamente no dispositivo, basta conecta-lo na


USB e ativar a opo USB Debugging nas configuraes do dispositivo. Se o driver
for reconhecido, o Android Studio vai permitir executar 0 projeto normalmente.
Dependendo do fabricante, pode ser necessrio instalar algum software para
reconhecer o driver do dispositivo.

dsistem *' ~`. fc~ ~~ , O


2.8 Criando um emulador (AVD)

~ _ < A evice (AVD), ou simpl5_


No Android o emulador chamado de Android Virtual D

e um smartphone oucomtablet Android ~ 6


mente conguraao virtual de um dispositivo. O emulador simula 't congura ~Q
exatamente a mesma plataforma d
a operacional, resoluao de tcla e outras configuraes.
S
Captulo 2 'I Congurao do ambiente de desenvolvimento 57
Para criar um emulador, execute o aplicativo AVD Manager pelo menu Tools >Android >
AVD Manager conforme a figura 2.16.

ng vc; indaw Help A A _

rg.
Isks&Conteds U 6 ,;,
, , Generate Javaoc... 'i`""i'i"""f'f'f" l'`'
f' i; Save Project as Template...
Manage Project Templates...
1, Groovy Console...
-fr *fr "WW" f<f*'
O ooagiz cima Tools 5 Sm Project warn amam Fes
I Open Terminal... '' wi DE/C! M0nt0r A
E Avo Malaga
SDK Manager
Enable non imzgmion

Figura 2.16 - Abrindo 0 AVD Manager


Na primeira vez que voc abrir o AVD Manager, a lista de emuladores estar vazia
(Figura 2.17). Para criar um novo emulador, clique no boto Createavirtualdevite.
AVD Manager

To
Vuhnldevicesailowyouttestyowappcutinnwithouttuvingta
ownthephysicildevicsz

` whefeyouangetup-to~dteinform.ionm
which devcesareactivent|\eAndrodndGnogIePlayeosyst|m.

Ee-Mil
Figura 2.17 - Android Virtual Device Manager

Na primeira pgina do wizard (Figura 2.18), selecione o tipo do dispositivo para


criar o emulador, que pode ser Phone, Tablet, Wear ou TV Veja que voc pode
escolher o tipo do dispositivo e o tamanho de tela que deseja simular; por exem
plo, eu escolhi um Nexus S com a tela de 480x800.
Google Android - 4 0d
S8 Ii.,
ii- _. .||._n

.ii-i--__ _
S__.-.__... -..~--~
-l [ Nexus 3

wa
,,,, Nau; onz_N_s. 3. T' wm mp. Sun nmll
Run WW
Nul FW 0" lr .hm mp. Oomkv Mv!

n NEM 6 536 H|0|2$60 S60dP 000m

Nami `g5' HHQJO uhr

Num 7- 768x1Z) IMP*

,,y Nau, 4 65 720:IZ!> ihdpl

Android Wal Sq... Y5' Zxhzw hdpi

ndmtd Wen no Los' 320630 hdpi

t..
1 QZQ.
Figura 2.18 - Selecionando o tipo do dispositivo.

Na prxima pgina do wizard, selecione a imagem de sistema para criar o emu


lador do Android. Somente sero exibidas na lista as imagens de que voc fez 0
dovimload pelo SDK Manager. A gura 2.19 mostra a imagem do Android 5.0 API
Level 21 do qual z o download. Note que a verso com o Google APIs idntica
imagem padro, mas contm as bibliotecas do Google, portanto recomenda-se
criar o emulador com o Google APIs.
Q' vr
Virtual Device Configuration 'f..,

'~PP 21 ofmubi-v7| Amw mx Lollipop

r 21
i ` ix Ari tem

i ^r:I
*' 5.0.1

O urmubl-vh Google Inc.


vhm wap

ima dvuionduuyagir
'""9" , '7 ~ Su documontwon toi MGOG 5 AH;

l
Figura 2-19 - Criando um AVI).
(aptulo 2 I (ongurao do ambiente de desenvolvimento 59

Dica: o emulador ARM do Android bastante lento, por isso se puder crie
o emulador x86, que tem o acelerador da Intel. Caso seu computador no
suporte a tecnologia de virtualizao da Intel, recomendo instalar o emulador
do Genymotion (genymotioncom).

Na ltima pgina do wizard (Figura 2.20) digite um nome para o emulador e clique
em Finish. Opcionalmente verique as opes avanadas no item ShowAdvanced Settings.
Virtual Device Configuration '

r.. _. _ _
^' ""' U'5**..**''--_. -..W ..ss .. 2 2 lss. _ _ ---W --. ... Avn mm.
] uzzozs 4.o-aoozsoompa cnznglf..
Stmup size
and Sul: A_t
H I l TYIO IIBITIO ol U'|lS VD.

, ' Lollipop Google API; (Google In:.) umubu-v7a Change...

orientmon

ffnumfd
Perforrnance
Qi uzz Host seu
l:I Stovea snapshot for fasterst rt p
'i au na efther me Host GPU p h t

;"z.....".......iii"`;.....,.i`*i ':

z . l..5r+s..l
Figura 2.20 - Criando um AVD.

Depois de criar o AVD o resultado ser como a gura 2.21, que mostra a lista dos
emuladores criados. Para iniciar o emulador, basta selecion-lo na lista e clicar
no boto Run que um tringulo verde. Nessa lista voc tambm pode editar as
conguraes do emulador e at exclui-lo. Para criar mais um emulador com
outra congurao, utilize o boto (reateVirtuaI Device.
Google Android - 43 edi0
60
Avn Mzmaer i

AH M run:l''^"'i Ill MM
; uzwzsmai oooohvi Z' `9l'm 650
M8, .7Y

Figura 2.21 - AVD criado com sucesso.

A gura 2.22 mostra o emulador executando no emulador.

. 5g,54N.,;AP|,21

Figura 2.22 - Emulador do Android.

2.9 Executando o projeto no emulador

tado na USB ou em um emulid '


Ao executar o projeto, possvel instalar o aplicativo em um dispositivo conec
. z or. Para executar o projeto no emulador siga os
seguintes passos:
Captulo 2 I (ongurao do ambiente de desenvolvimento 61
Caso o emulador esteja fechado, selecione o item Launch emulator (Figura 2.23) e
a seguir selecione o emulador desejado. Caso o emulador esteja aberto, ele vai
aparecer na lista abaixo do item (hoosea running device.
E; Choose Device
O Qhooe a running device

* Launch emulator l K V " " l"` "l


nafziatzzauzlazazz _f1wf,S,_~F>_g1 pp pp __
l:i Use same device for future lunches

@~<=*

Figura 2.23 - Executando 0 projeto no emulador

2.10 Algumas janelas importantes do Android Studio


Uma das coisas mais importantes ao estudar uma nova tecnologia aprender
mais detalhes sobre o ambiente de desenvolvimento, que neste caso o Android
Studio. Quanto mais familiaridade voc tiver com as opes da ferramenta, melhor,
mas isso vai depender muito de voc, pois s usando e fuando que se aprende.
Uma janela importante que mostra as mensagens ao executar o projeto no
emulador a 4:Run, conforme mostra a gura 2.24. O nmero 4 do atalho para
utilizar com o Alt+Nmero ou (md+Numero (Mac). Na gura podemos ver os logs que
mostram o aplicativo sendo instalado no emulador. importante aprender a ler
as mensagens desses logs, pois se acontecer algum erro ao executar o projeto esta
janela vai mostrar detalhes importantes.
Outra janela importante a 6:Android (Figura 2.25), a qual mostra os logs do emu
lador ou dispositivo conectado na USB. No Android todos os logs so contro
lados pela ferramenta LogCat. Depois vamos aprender a criar esses logs dentro
do cdigo-fonte, para ajudar a depurar o cdigo. O LogCat mostra todos os logs
do sistema operacional ou apenas do processo ou tag que voc especificar, o que
permite depurar apenas as mensagens que lhe interessam. .
Google Android - 4' 0
62

Figura 2.24 - Logs da execuo do projeto.

Em caso de erro da aplicao, verique os erros e excees na janela do LogCat,


pois tudo que se refere execuo da sua aplicao logado aqui. Por exemplo,
se o aplicativo lanar uma exceo a stack trace com o erro detalhado, ela ser
exibida nestes logs (Figura 2.?_5):

Figura 2.25 - Logs do emulador (LogCat).


Outra janela importante,que
amoGradle
t Console
Cradle (Figura 226). s ra as mensagens do build do
~ . za
Graal: Comolo
_ **)a'-.- I ,gw
.` 'WHY L 2 4 v-ni :ir Z-\.z
Q ~ ;;

M3 WV-I" .z i zizif'
.,W...,,,, J H MF
-l ~I`Y ..^"`'}'\^\Q,. nf- 7 H
a'_~,q 4,1-=[,,.,z`JY_' J
'l`Y"\' J `"'i*1.:_; *' '1; my
n~:ri~ xJ'i'\'~l '11 Y li
`UL~2f! l\if2~iz,, w; ,TP

1 ni i 5-;~_zz,
`l 1 1..

"* *"** *W Gmea; Qgmgk

Figura 216 " [085 do console do Gradle.


Capitulo 2 -.Congurao do ambiente de desenvolvimento 63
E para turbinar o seu desenvolvimento recomendo olhar as dicas do Android
Studio abrindo o wizard Help >Tip ofthe Day. Aqui voc vai encontrar dicas e teclas
de atalhos que vo tornar o desenvolvimento do cdigo muito mais produtivo. A
figura 2.27 mostra uma dica da tecla de atalho (trl+N que facilita encontrar e abrir
qualquer classe Java do projeto.
Tip of the Day A
Did you know ... ?
To open any class in the editor quickly, press Ctr1+N (Navigate 1 Class) and
start typing the name of the class. Choose the dass from a drop-down list
that appears.

Enter class name: _ pf include non-project classes (NN) Y /


i.Q..Q.;;f;;. . .,.,.... 2 ._ s . -J
lnmal (Animal..lilan\nalial -V AMYPTOJEC '
c* z. HetersToInchesConverter MyPro3e~:t lf
You can open any le in your project in a similar way by using Ctr`l+Shift+N `
(Navigate l File)

Cl Show Tips an Startup

l.. f-~=_.;__fl li t il

Figura 2.27 - Dicas do dia.

2.11 Aplicaes na tela principal (Home)


Ao clicar no icone Home, o emulador volta para a tela principal. Nessa tela, todas
as aplicaes instaladas podem ser visualizadas. Por padro o cone do aplicativo
um bonequinho do Android, conforme a gura 2.28.

Figura 2.28 - Tela Home do Android.


Google Android - 4 d
64
ra cada den
Esse cone dcnido no arquivo /fS/mPl"1aP'fxx/C-l""ChenZgfefr esse cone 6
Sd3d (mdpi, hdpi,xl'1dpi,xxl1dpi ctc.). Fique a vonta e para
inserir uma imagem customizada para SCU Pf0Jet0~

ma as co g _ d la
2.12 Entendendo um pouco mais sobre o emulador
l 'mula completamf
U d isas le ais do emulador do Android que e e si
o sistema operacional real, e possvel acessar a estrutura de arquivos o emu
dor. Dessa forma, podemos visualizar os arquivos apks das aplicaes instaladas,
mulador. Toda essa estrutura de
alm de excluir, enviar e recuperar arquivos do e '
diretrios do emulador pode ser visualizada pelo Android Studio ou por ml0
de um prompt de comandos.
Demonstraremos primeiro como acessar o console do Android pelo prompt de
comandos. Para isso, digite os seguintes comandos em um prompt (o emulador
precisa estar aberto):
C:\android-studio\sdk\piatforn-too1s>adb devices
List of devices attached
1 enuiator-5554 device

Ao digitar o comando adb devices no prompt, exibida a lista dos emuladores


ativos ou dispositivos conectados, ou nada, se nenhum emulador estiver aberto
no momento. Nessa lista o cdigo (id) retornado pode ser utilizado se mais de

tm emulador
eseja acessar oestiver aberto
console. para
Nesse escolher
caso em qual
como temos a instncia do emulador voc

do _ . . .
A0 f3Zf1550 Um prompt ser aberto, permitindo navegar nos arquivos e pastas
. emulador. No exemplo a seguir, foi digitado o comando ls do Linux para
visualizar a estrutura de diretrios:
a.
basta digitar adb shell para acessar o ,console do pri:;)Sei1nnul:trd1d1rli(1(lisberto,

C:\android-studio\sdk\p1atform-tools>adb shell
#15
cache
init
etc
var
data
system
tmp
root
dev
Captulo 2 1 Congurao do ambiente de desenvolvimento 55
Observe que o sistema operacional do Android Linux. Dessa forma, ao entrar
no console do emulador, necessrio utilizar comandos do Linux, mesmo se
voc estiver desenvolvendo suas aplicaes no Windows. As aplicaes instaladas
cam na pasta /data/app. Vamos entrar nessa pasta para visualizar nossa primeira
aplicao desenvolvida.
# cd /data/app
# ls
br.livroandroid.helloandroidstudio-1.apk
#

O arquivo brlivroandroid.helloandroidstudio-1.apk corresponde aplicao nal do


projeto HelloAndrodStudio criado anteriormente. Outra forma de visualizar a estru
tura de diretrios do emulador abrir a ferramenta Android Device Monitor pelo menu
Tools > Android > Android Device Monitor do Android Studio. Nessa ferramenta a janela File
Explorer mostra a estrutura de diretrios do emulador, conforme mostra a gura 2.29.
*ff Android Device Monitor - U
f Sun gznw gelo
Qu.c<A.rc5s E3 _ ' f ' 'Q
i _ Name Size Date_ Hi'-'=
iltlfJEiz''fV`f T IT
Deves 5 -. ihreacisv. Heap Allocaii., lxietwor.- File Ex.. I; iimuiat.. * Systemm

1 E! ^.:usje'T1ulatcz'~5554} Online Nexus I-t.- ~ V df-acct 2014-D9-05 15

z; )
:cz,an::o~dfamrhe: 551 8600 ~;>ca:he 2014-09-05 15
'e*:_ar,.:c kemail 1817 8602 Y z -config 2014439-05 15
:\:rn_af1drc.:l_$yStemu 442 32303 ...Y cf 2014439-05
arczd Dfmdi 414 86044 87...
nf.1.'.r:afafcz_h-oancirmd , 4 data
855 8505 anr2014-09433
2014439-031-$(
'CO'Ll'l&(DCl.3l'C1VCl8fC1lEf' 922 8605 K app* _* H W _ K V rf M 2G'I$_~U9-U5
z<.zf..zf..<:f<z<1.f.zz.g5 ras aos cz tzfifvzan1r<f.izi.zzzamuzzs-tape jsa zowolo 'is
a.m1ez:.pro:ess.cor 585 86139 C1 timwmandrcnd.heiIoan'drozdsiudioiest-1.ao U O 7 O 25160 201409-d
ccmaP.drod.iputmemod Ie 525 3610 v app-asec 21314-D9-63
core,amdrozrJ,ca!e*:ar 979 SEU z app-lib 2314-09-G5
com ridridieychazn 1059 8512 .if app-private 2B*f=4=Q9-03
:r:'nam:l'o.d.rnms 951 8614 ~ cz: ar1run-tests 2814-Di-17
*c~man13ro.ddcsi<cicck 1000 8616 '~ za backup 2014439-3
l .~ .nz:1fe.zi.phonzz 5413 8618 ___, tiugrepmts 201i5^;~fJ3
i, ceasz 390 8521 . dalvk;ache ZUH-W-U5
fzIL=:.r'~f Z " ' _
ZU9? 8601 .;~ dara 314B9'iI3 V
'- ........|.-,. tl :RA hu Il.:
Y" pf Java regexes, Wex mrii prai: app;t text: lrrzit scope. vecbcrse V H A
PID TID Application Tag Text ^
.. _..
'A_.. ......,_,.
` 'WT C": fz--*xr V *s_~_ -.r Iz7x:i.r. : r i ;l":' 'x"2 r '
~
. _ ~. .'_ -..-'I
1" _ .__ ~z.z- ._, ..

ow of 492.M

Figura 2.29 - Android Device Monitor - File Explorer

No Android Device Monitor tambm podemos visualizar os logs Android, consumo de


memria, threads etc. Uma das janelas mais interessantes a Devices, que mostra
todos os dispositivos conectados na USB ou at mesmo o emulador. Nessa janela
podemos ver os processos que esto executando no Android, depur-lo, mat-lo,
66 | Log(at Google Android - 4 d

_, , . . ueas 1ntlisF|le
.z ~' Exporer
einclusiv tirar um screenshot da ttla.\/ale lunbritq `_ f cleimmduImjmcln
(lggg) Q as outras vo mostrar as in lormaoes do .lisP5'V* *
Devices, caso exista mais de Um
. _ . . ,. .. . ' cvicc Monitor'
.- n roit tu to com ~s 1
. , ane a.
Dica: no Windows tive problemas para uiccutrso /tlndroid Dpcnnigso d
somente funcionou ao executar o A, Adav c.Io:Gin
administrador. Essa ferramenta tambem pode Scr executa P
C:\androd-studo\sdk\too1s\monitor.bat.

Para remover uma aplicao pelo File Explorer basta selecionar o arquivo .aplc descjal
e clicar no icone com um trao - vermelho no canto direito superior da Jr
Observe que nessa janela tambm existem cones para enviar arquivos para O
emulador e para baixar os arquivos que esto no emulador para seu computad0r.
Note que toda a segurana do Android baseada na segurana do Linux. No
Android cada aplicao executada em um nico processo, com uma thread
dedicada. Para cada aplicao criado um usurio no sistema operacional para
ter acesso a sua estrutura de diretrios. Dessa forma nenhum outro usurio pode
ter acesso a essa aplicao. Por isso, se um dispositivo real estivesse conectado
na porta USB do seu computador, a janela File Explorer tambm funcionaria, mas
provavelmente no exibiria nenhuma aplicao por motivos de segurana. No
emulador como se o desenvolvedor tivesse acesso total (root) ao dispositivo.

2.13 ADB (Android Debug Bridge)

A ferramenta adb (Android Debug Bridge) permite gerenciar e controlar o emu

O8''r'
lador; Inclusive j estudamos o comando adb shell para entrar na estrutura de
diretorios do Android.

db consiste em uma aplicaao cliente-servidor que fica em execuo na m


adb.ee em sua m uina ' ` - . S0
quina. Ao iniciar o Android Studio e o emulador, observe que existe um pmges
Cl ,que e Justamente o servidor. Esse aplicativo executando

mais d , , . 8 ~u. e
em segundo plano controla as portas de cada emulador

Ao iniciar um emulador, sua porta-padro 5554 e a porta de deb 5555 5


ou e um emulador for aberto, os proximos emuladores usaro as prxim
apdbe
,as sequencialmente.
possivel acessarPor exemplo: 5556/.5557,
a estrut d , '5558/5559
- ' 1 O etc Con l ~ as
wmandfl
ura e dirctorios do emulador da m f
e depoisP ISUZI
o IslZ1l'
ara85 v` \lC
l' 3. 0 comando adb shell,
usando ajanela File Explorer. Para isso, abra um prompt C ci ft Sma Qrma que
pz|5[a5_
Captulo 2 I Congurao do ambiente de desenvolvimento 67
C:\android-studio\sdk\platforn-tools>adb shell
# ls
. . . // voc vai ver as pastas aqui
sdcard

Feito isso, possvel navegar na estrutura de diretrios do emulador normalmente.


Veja nos comandos a seguir onde acessamos a pasta /sdcard para visualizar os
arquivos do carto de memria.
# cd sdcard
# ls
linkin_park1.mp3 // Exemplos de arquivos
linkin_park2.mp3
linkin_park3.mp3
# exit
C:\>

Para sair do prompt, digite exit. At aqui tudo foi simples, mas se existir mais de
um emulador aberto? Nesse caso o adb vai exibir uma mensagem de erro, pois ele
no sabe qual emulador deve acessar:
C:\android-studio\sdk\platform-tools>adb shell
error: more than one device and enulator

Ento, preciso ajud-lo. Para isso, precisamos conhecer o identicador de cada


emulador; digite ento o comando adb devices.
C:\android-studio\sdk\platform-tools>adb devices
List of devices attached
enulator-5554 device
emulator-5556 device

Esse comando listar os emuladores abertos, exibindo seu identificador, que con
tem a palavra emulator mais o nmero da porta em que ele est aberto, por exemplo,
emulator-5554. Saiba que a notao pode mudar conforme o emulador; portanto,
o importante voc entender o conceito. Para acessar um emulador especco,
podemos utilizar o comando adb shell novamente e informar o parmetro -s com
o id da instncia do emulador que desejamos.
C:\android-studio\sdk\platform-tools>adb -s emulator-5554 shell

O argumento -s pode ser utilizado para todos os comandos da ferramenta adb sem
pre que existir mais de um emulador aberto e for necessrio informar a instncia
correta. Por exemplo, digamos que voc deseje enviar um arquivo para o emulador.
Para isso, poder utilizar o comando adb push, conforme mostrado a seguir: _
63 1/tw
aa vv .

C:\androd-studio\sdk\p1atform-too1sadb s emuiator
0 KB/s (0 bytes in 7.000s)
Google Android - 4 @da

' t /data/1068
_ -5554 push c=\t@D\fQU1V-Ui

A sintaxe do comando adb DU5h


' te o
. . 11 ara fazer Justame
adb push [camnho_arquivo_local] [P05.de5f"-emulador]

Da mesma forma, podemos utilizar o comando adbemu a


pu lpdor.
contrrio: o download de um arqU1V0 que esta HO

_ - ' anteriormente para O


adb pull [arquivo_origem_emulador] [ca/'inh0_0fGUV.lCl]

Por exemplo, para copiar O 111651110 GYQUWO que envlamos


computador, basta digitar o seguinte COmUd0
C:\androd-studto\sdk\p1atform-too1s>adb -s emulator-5554 DUU /data/10CH1/UP/ arquivo'
txt c:\temp\arquvo.tt
0 KB/s (0 bytes tn 7.000s)

H outras opes de uso da ferramenta adb, mas vamos deixar isso como leitura
para voc:
http://developerandroid.com/tools/help/adb. html
Gostaria apenas de dar uma dica caso voc perceba que o Android Studio e o
emulador no esto conversando muito bem. s vezes, se o emulador ca mui
to tempo aberto e executamos uma aplicao no Android Studio, parece que a
aplicao no instalada no emulador. como se o emulador estivesse parado.
Se isso acontecer, pode ser necessrio matar o processo do adb.ee em execuo
no sistema operacional. Isso pode ser feito com o comando adb kill-server. E para
iniciar o processo do adb pode-se utilizar o comando adb start-server. Outra forma
de reiniciar o processo adb.exe abrir a janela Devices da ferramenta Android Device
Monitor e clicar no item de menu Reset adb.

2.14 Informaes bsicas sobre a resoluo do emulador

Q . . ._ er o _ uma te a com resoluo HVGA


Os primeiros smartphones com Android tinham l
de tela, o cuidados
ue dem durante od 1'
(32-0480), T1185 hoje em dia existem smartphones e tablets com diversos tamanhos
q an a a guns desenvolvimento.

iar ~
emulador do Android nos permite simular exatamente o tamanho e a resoluo
da tela de um dispositivo real. Para realizar essa configurao basta escolh

manho da ela no momento de criar um novo emulador. Voc pode 5O1he


- -_Ce_ Ja eve terno
_ ,pois percebido
AVD Mana' er~aoicas_
sele `
etc 1\:-II; -2;1:va)dor.codm a resoluao HVGA (32Ox48O), QVGA (24-OX32O), VGA (48Ox8OO)
PO de d15P0SlUV0, o emulador criado com base nessas czacterist clonar um
Captulo 2 I Congurao do ambiente de desenvolvimento 69
Na gura 230 podemos visualizar como fica a resoluo da tela no emulador com
diferentes conguraes de AV`D.As guras mostram as resolues NVGA alta, I-NGA mdia
e QVGA baixa, respec1vamente.A resoluo QVGA (24Ox32O) so dispositivos muito
pequenos, como o famoso Sony Ericsson Mini. Muitas vezes pode ser complicado
desenvolver para celulares com telas to pequenas, pois necessrio customizar 0
projeto para ter barra de rolagem (scroll) em todas as telas e utilizar imagens menores.
Mas os smartphones com telas WVGA (480x800) so maiores e grande parte dos
aparelhos modernos est vindo com esse tipo de configurao ou at superior, o que
permite abusar melhor dos recursos grcos e criar uma tela bem diferenciada.

fz

Figura 2.30 - Emulador com as resolues WVGA, H VGA e QVGA respectivamente.

Apenas para dar uma ideia, como cada aparelho tem uma configurao de tela,
podemos utilizar imagens de tamanho diferentes para cada tipo de resoluo.
Para isso no projeto Android dentro do Android Studio criada uma pasta de
imagem para cada resoluo. Por exemplo, existe a pasta drawable-ldpi para celulares
QVGA (24Ox32O) de baixa resoluo, a pasta drawable-mdpi para celulares HVGA
(32Ox48O) de mdia resoluo, a pasta drawable~hdpi para alta resoluo com as
telas WVGA (480x800) e por a vai. A coisa comeou a car to complicada que
chegou o xhdp (extra high) e a histria continua.
Essas pastas no tm relao com o tamanho de tela, por exemplo, 240x320 ou
(480x800), e sim com a densidade e resoluo da tela. O tamanho da tela pode
ser o mesmo, mas a quantidade de pixels que a tela consegue exibir pode ser di
ferente, e isso tem a ver com a resoluo. Portanto, aparelhos WVGA (480x800),
embora tenham telas maiores, tambm apresentam resolues muito melhores,
para exibir imagens e grficos com uma tima denio.
Google Android - 4 2d5
. ' .. ' xl
em fra_bida
~, , ~ . ~ )0 . ~ -' -tor inserida
- , . ' na pasta
drawablc-mdP~ .
M lima mmgtm dt M px 480). Mas o que acontece se ela for
hida
- _ ,em
Pcrleitaniente
ul uma
em
- . d` . re in
tela
uma tela
iensionada
H\/GAmenor ou maior.
fifa? 8 imagem
para baixo em uma_tela ..
Ser redimensiw

_ .. .' .asrado. or 15
na
la P
orPadrao.
s araCaso
umaatela
imagem
maior5<~J
como _ WVGA
ara evl '
QVGA (2~lO32O) no temos problem8S.fT121 P _ f P . SO
(~l80800) a iinagem val distorcer e Perder quhdade, O que E espc -t tr que 0
podemos customizar as imagens em suas respCflV5 Past* P
Android faa isso em tempo de execuo.
Neste exemplo, para imagem de 100px, taramos a seguinte conta:
Para telas QVGA (24O32O), seria 100px 0,75 = 75PX- N55@ (3350 P0del05
inserir uma imagem de 75px na pasta drawable-ldpi. Ou voce pode deixar
que o Android redimensione a imagem para baixo em tempo de execuao.
Para telas WVGA (480800), seria 100px 1,5 = 150px. Nesse caso podemos
inserir uma imagem de 15Opx na pasta drawable-hdpi, pois esses modernos
aparelhos conseguem exibir imagens com muito mais denio.
O nome da imagem ser sempre o mesmo. Por exemplo, se o nome do arquivo
for imagempng, teremos vrias imagens com o mesmo nome, inseridas nas suas
respectivas pastas. Em tempo de execuo, conforme a resoluo da tela, o Android
vai escolher a pasta correta, voc s precisa garantir que a imagem vai estar l. Um
exemplo prtico que temos disso o cone da aplicao, criado automaticamente
pelo wizard com o nome icjaunchenpng e replicado nas pastas mipmap-mdpi,
mipmap-hdpi, mipmap-xhdpi e mipmap-xxhdpi para customizar o tamanho para cada
resoluo. Isso evita que o Android faa esse redimensionamento em tempo de
execuo, pois os recursos j foram informados em tempo de compilao.
Vamos aprender mais sobre esses detalhes durante o livro, ento que tranquilo

2.15 (omo fazer o download dos exemplos do livro


Se voc quiser fazer o cadastro no site ara
p mantermos contato, futuramente posgg
avis-lo de novos lanamem05_

Ou man . .
http://wwulivroandroid.com.br/

tenha contato nas redes sociais, que costumo postar sobre lan HITICII _
http://facebook.com/ricardolecheta Os
hUPS2//plus. google. com/+Ricardo Lehem
Captulo 2 I Congurao do ambiente de desenvolvimento 71
Caso queira apenas baixar o cdigo-fonte do livro, acesse o repositrio de cdigo
-fonte do GitHub nos seguintes endereos:
http://wwugithub.com/livroandroid/capitulos
http://wwmgithub.com/livroandroid/carros
http://wwwgithub.com/livroandroid/AndroidUtils
O repositrio /capitulos contm vrias pastas separadas por captulo. Aqui voc vai
encontrar os projetos prontos referentes aos exerccios de cada captulo. O reposi
trio /carros contm um passo a passo que vou utilizar durante o desenvolvimento
do projeto dos carros. Cada pasta corresponde a uma parte do projeto dos carros
concludo. O repositrio /AndroidUtils contm um projeto do tipo biblioteca com
classes utilitrias para nos auxiliar nos exemplos.
Pela explicao do livro, voc conseguir seguir os exemplos e entender o que
signica cada pasta. O objetivo dessas pastas servir de gabarito para voc.
Recomendo que voc tente fazer sozinho todos os exerccios de cada captulo e
o projeto dos carros passo a passo, mas se em algum momento tiver problemas
basta conferir o resultado com os exemplos do Gitl-Iub.
Para fazer o download do cdigo-fonte do GitHub voc pode simplesmente clicar
na opo Download Zip. Outra opo clonar o repositrio na sua mquina, assim se
eu fizer qualquer atualizao, como por exemplo uma possvel correo de bug
ou melhoria de algum exemplo, voc sempre pode atualizar o repositrio.
Com o Git instalado na mquina, abra um prompt e digite o seguinte comando
para clonar o repositrio:
git clone https://github. com/livroandroid/carros.git
Depois para atualizar o cdigo caso existam atualizaes utilize o comando gt pull.
Uma vez que voc fez os downloads dos exemplos, utilize o menu File > Open do
Android Studio para abrir o projeto desejado. Agora com voc, leia com calma
cada captulo e sempre conra as explicaes com os projetos de exemplo, que
podem servir de gabarito para voc.
Vamos l! Desejo desde j uma boa leitura.
\ cAPiruLo 3
Conceitos bsicos do Andr0d
-4
1
I

. , -- ~- ''m
criarrimir
telas na
Este captulo aborda alguns conceitos basicos do Android, como
aplicao, denir uma interface graca s1mpleS, fafaf eventos da tela e l P
mensagens (logs) da aplicao utilizando a ferramenta I.ogCat.
Um layout de tela no Android pode ser criado utilizando um arquivo XML que
dene os elementos da tela ou utilizando diretamente as classes da API Java. A0
nal deste captulo, voc ser capaz de criar telas simples na aplicao, demr
eventos para os botes e ainda visualizar os logs gerados pela aplicao.

3.1 Estrutura do projeto no Android Studio


No captulo anterior, instalamos o Android Studio e criamos o nosso primeiro
projeto para Android. Neste captulo vamos estudar mais detalhes sobre a estru
tura do projeto e os conceitos bsicos de desenvolvimento para Android.
O Android Studio pode abrir um projeto de cada vez, e cada projeto pode conter
um ou mais mdulos. A gura 3.1 mostra a estrutura do projeto que criamos
com a pasta do mdulo app fechada. A pasta app representa o mdulo padro
que e criado no projeto. Dentro da pasta app temos o cdigo-fonte e os arquivos
de compilao especcos desse mdulo. j na raiz do projeto existem os outros
arquivos, como por exemplo o q ar uz
uivo
gmb `ld. dl do projeto, que vale para
e geral

Nota: - . ii " "_ `


todos os mdulos.

Gradl estrutura de PHSHS 6 arquivos do projeto segue a estrutura de build do

72
Captulo 3 I Conceitos bsicos do Android 73
z_*^*"1*=i=

. fi . A ? ?i;*'"f W
1

*gif .gitignore
builcigradle
ll . 5
i;_igradle.prop:r1ies
'fg
E1-;3 l__,_z;
gradlew
gradiewbat
gii Iocai.properties
' settingmgradle
5 z ag; External Libraries
u

Figura 3.1 - Estrutura de arquivos do projeto.

A lista a seguir explica os arquivos que cam na raiz do projeto.


Pasta Descrio
app Mdulo app do projeto. O Android Studio abre um projeto de cada
vez e um projeto pode conter mais de um mdulo. O mdulo app
o padro.
build.gradle Arquivo de congurao do Gradle que vale para todo o projeto,
incluindo todos os mdulos. Voc provavelmente no vai alterar
nada nesse arquivo.
gradleproperties Arquivo de propriedades para customizar o build do Gradle.
gradlewbat Script que executa o build do Gradle para compilar o projeto.
local. properties Arquivo com as configuraes locais do projeto, como por exemplo o
caminho no qual o Android SDK esta instalado. Ao abrir um projeto
j existente, voc deve atualizar esse arquivo com o local do SDK;
por sorte, o Android Studio j faz isso automaticamente para voc.
settingsgradle Arquivo de congurao do Gradle que indica quais mdulos
devem ser compilados. Se voc abrir esse arquivo, ver que ele est
incluindo o mdulo app.

Ao expandir a pasta app voc ver os arquivos referentes a esse mdulo, e ba


sicamente aqui que cam os arquivos da aplicao. Por exemplo, na pasta
/app/src/main/ esto os arquivos .java, .xml e imagens do aplicativo, conforme a
gura 3.2.
Para visualizar a estrutura de diretrios real do projeto, selecione o item Project no
combo de opes que ca na parte esquerda superior da janela. Outra visualiza
o muito boa a Android, que mostra um resumo dos principais arquivos e deixa
a visualizao mais enxuta.
Google Android - 4' d

74
it ' ' 1
pszati '
HelloAn'li (\l('l('|lO `
. tr wi"
i

1.
v-.ii
.l
l

rrliz .L^
. *1]l lu'

i .il. _, . .i
s ul .l"
V ,W { _' lui, z
i\'..il~~
,, t, I,l\. .2
~\li.l\N'_{il' Kill
.lltiw lltzr
lxl l .
l

.till
,,),r17z.ll Nil);
ompzn ll) *WMV
www ii ll 'ii ^`
ii ' ii V,
3

gl

*,/ i}L1\" z*'{>.)l'\l V


,..i....s i--i
tz An l- 1 llf 3 `|'lv'\` "l`
llulivlf
rf _1>` _* ll
' iz.1 til.

Figura 3.2 - Estrutura de arquivos do projeto.

A seguinte lista explica os arquivos do mdulo app.


Pasta Descrio __ ____d ,_ _ _
build cam os arquivos compilados do mdulo. O arquivo apk
que o aplicativo compilado ca na pasta build/outputs/aph.
R. java A classe R. java gerada automaticamente ao compilar o projeto e permite
que a aplicao acesse qualquer recurso como arquivos e imagens utilizando
as constantes desta classe. Essa classe nu nca deve ser alterada manualmente.
O arquivo R. java gerado na pasta app/build/generated/soura'/r do mdulo.
libs
Pasta para inserir os arquivos .jars que devem ser compilados com o projeto
src/main/java Pasta com as classes java; por exemplo, a classe llainActivity que foi
criada pelo wizard.
src/main/res

d bl t '
Pasta que contm os recursos da 'ipz l'reao. como imagens, layouts
de telas e arquivos de internacionalizao. Existem cinco subpastasz
WW f.l1y0ul. menu. mipmap e values, com os seus respectivos classi
cadores, que vamos vericar mais adiante.
Captulo 3 i Conceitos bsicos do Android 75
Pasta Descrio
res/drawable Pasta com as imagens da aplicao. Atualmente como existem diver
sos dispositivos Android com diferentes resolues de telas, possvel
customizar as imagens para car com o tamanho exato em cada reso
luo. Para isso, h diversas pastas para as imagens: drawable-ldpi (low),
drawable-mdpi (medium), drau/able-hdpi (high), drau/able-xdpi (extra high)
e drawable-xxdpi (extra extra high). Na ltima verso do Android Studio
ele no cria todas as variaes, eu criei para mostrar a voc.
res/mipmap Pasta com o cone da aplicao, o qual por padro chama-se ic_launchex png.
Da mesma forma que a pasta /res/drawable, esta pasta tambm apresenta
variaes conforme a densidade da tela do dispositivo.
res/layout Pasta que contm os arquivos XML de layouts para construir as telas
da aplicao.
res/menu Pasta que contm os arquivos XML que criam os menus da aplicao,
que so os botes com aes na action bar.
res/values Pasta que contm os arquivos XMLutilizados para a internacionalizao,
congurao de temas e outras conguraes.
Cada arquivo seja imagem ou XML dentro da pasta /app/res contm uma referncia
na classe R, que automaticamente gerada ao compilar o projeto. Cada vez que
se altera, adiciona ou remove um arquivo dessas pastas, a classe R alterada para
reetir as informaes.
Por exemplo, se voc inserir uma imagem chamada smilepng em uma das pastas
/res/drawable, a constante R.drawable.smile ser automaticamente gerada na classe R.
Se voc inserir um arquivo XML chamado teste.xml na pasta /res/layout, a constante
R.layout . teste ser gerada. A classe R nossa grande aliada no desenvolvimento para
Android e nunca deve ser alterada manualmente. Observe que o cdigo-fonte da
classe MainActivity utiliza a constante R.layout.activity_main, a qual define o layout
utilizado para apresentar a tela ao usurio.

MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain); // O layout da tela denido aqui
}

@0verride
oooqio Android ~ 4 d|
76

publlt haolvan on(roato0ntlomMenu(Henu menu) J menu). // Menu com botes na action bar
9t*lMt~nulnalr().ln\tt*(R"''~"cUV{ty~m n' A '
return tlui
1

\ _` , .z . -. ~ (UIC\'l\`;ll'ClI\tflllll
. .~ '. . - I; -Li.: *MalnActlvlty.rt \ttH"" U V Q
NO!l.l\titnt||ti Itmlt t lx vt l g I. ` H h.. mm`mc|1I(* tl
kh) Jg,R_1yaut.actlvlty_maln. /\inIrnt . tua n
l ~ ~ ' ^ W.1|ml.Isszitt1iic;it1H1
nltquivn dc 'nx- H /,rx/l`?|)|((/|( H'_\'_ HUN". \", lu) UIIUI \ P*
ii Ctrl+cllque utilizntln cm niuittis mitrns |\|i24"`<`~

3.2 Arquivo AndrodManfest.xmI


O iirquiviiz\nlri.I\~1ini_/i*s!.xml ii lmsc do uma ;1|?|itf;1 Andriiid c ctmtnt todas
nstwnigurucsticccmhiusixuuivxccutnriimohcntt
Qiiindu crinnitis ti prujctti HelloAndroldStudlo nn cipttiln mtcrior. iki criada ai
Malnnctlvlty c vlzi iiii miiiigiirntli ititmirizitnicziniciitc um arquivo /lminidMmi_'sr.xrnI
tftiimm activity inicial du pi'ictt,tti sujz1,(*it|ticI;i idivityqtic contni miric na
tcln initfiul (l lume) c rcprcscnta ti uplicitivti que ti tisui_'i vai executar. Nu ctligo ai
wwmumwmwmmmpmwmnmmnmmmmmmkmnmwnAmmmMmmkuxmL
n1:nittlcin C(Hl1ilCl1(L

1% Andro|dMan|fest.mI

?ml verslon="1.u encodlng="utf-8"?


manlfest mlns:androld="http://schemas.androld.com/apk/res/androld"
DCka9e="br.com.llvroandrold.helloandroldstudlo" /I Pacote nlco do projeto
appllcatlon

d'td*u"BC'<UP="U`U" // Se for true entao permlte backup no cloud


"dfd11<="@MlDmap/lc_launther" // cone do apllcatlvo na tela Home
"d'td*labl="@5't"9/PPa" // Nome do aplicativo na tela Home

- d tl _ i
androld:theme="@5tyl@/Apprhemev , Tema d i It
octlvlty 00 Cl/0 (/tvs/values/styles.ml)
androld:name="_n1ntV
Y" // Classe da Actlvlty que deve ser a@u1;d,
"f*<'=11"'@Slfloo/ooo nan@'~ // Titulo

lntont.ltor
A // Declara um ltro para exect avals mostrar na action bar
// ao MAIN indica que tff BCVY D0de ser executada com
|ctlon ondrold:n|mo~"|ndrold.lntont.|ctlon.HAIN" / O a inicial
Captulo 3 I' Conceitos bsicos do Android 77
// A categoria LAUNCHER indica que o icone da activity deve car disponivel
// na tela inicial
<category android:nane="android.intent.category.LAUNCHER" /
</intent-lter>

<activity android:name=".LoginActivity" />


<activity android:name=".CadastroUsuarioActivity" />
<activity android:nane=".BemVindoActivity" />

O XML sempre deve iniciar com as linhas a seguir, declarando o namespace para vali
dar os atributos utilizados e o pacote da aplicao. O encode do XML deve ser UTP-8.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.livroandroid.helloandroidstudio">

Dentro da tag declarado o pacote principal do projeto, utilizando a


tag . Esse o pacote identicador do projeto e deve ser nico no Google
Play O mesmo pacote declarado no arquivo app/build.gmdle, o qual vamos ve
ricar mais tarde.

obrigatrio que cada activity do projeto esteja declarada no arquivo


AndroidManiest.xml e para isso utilizamos a tag , a qual recebe o nome
da classe.

Geralmente um aplicativo Android tem um cone que ca na tela inicial do


Android. Quando o usurio clica nesse cone, a activity inicial do projeto exe
cutada, que neste caso a MainActivity.
<activity android:name=".MainActivity" android:label="@string/app_nane">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>

A tag <intent-lter> necessria para customizar a forma como a activity ser


iniciada. A ao MAIN significa que a activity pode ser iniciada isoladamente, como
o ponto inicial da aplicao. A ao MAIN como a famosa funo nain() da lin
guagem C, a qual utilizada para iniciar a aplicao. A categoria LAUNCHER indica
que a activity estar disponvel para o usurio na tela inicial junto com as outras
aplicaes que o usurio possui instaladas.
78 .'. utras
ong
Iml rWc*
~ de que
_ . ~ ~ uma activity_SLTH
na L . .
. . na
maioria
f levem conter
das \tes
' ' C urada
esse
somente ti OPde Con
Goo9le Android - 4 @d

como O POW ds Partida' portanto s O C ~ 'vit com a classe BemVndoAct1vtty


aurao. Por exemplo, para declarar uma acti y
basta uma declarao simples asslmi
<activty androd:nane=".Ber'\Vd0ACVfY" />
i los forem sendo criados nos ximos pro
captulos,_111815
A medlda que novos exemp ~ - ' ' l "o fornecidos.
detalhes sobre a conguraao do arquivo AndrozdMamest.xm Sera

tiviz Obrigatrio
Notazsempre . . . - t inicia a
que criarcongur-la
uma nova ac yO
_ _afqulvo
_ _ , ld
AndroidManifesz.xml. A Ma1nAct1v1ty foi configurada como a EICUVI Y_
LAUNCHER. Mais detalhes
aplicao, pois est declarada com a ao MAIN e a categoria 1 Intent
sobre 3 tag <intent-1ter> sero explicados no capitulo 20, sobre a c asse .U

3.3 (Iasse MainActivity


Aclasse MainActvty foi congurada pelo vvizard do Android Studio como a activity
inicial, portanto pode ser executada pelo usurio ao clicar no cone do aplicativo
na tela Home do Android.

A classe ManActvty ou qualquer outra activity deve ser lha da classe androd.
app.Actvty.

public class ManActvty extends androd.app.Activity {

A classe androd.app.Activty representa uma tela da aplicao, sendo responsvel


por controlar o estado e os eventos da tela. Assim, para cada tela da aplicao

An d `
ar '
~._1 e
'
desc h d _na ac tvtty
n.A.nao
t .l ~sabe
voc criar uma classe-lha de androd.app.Actvty. O mtodo onCreate(bund1e) pre

_' app
old.
cisa ser implementado obrigatoriamente e chamado de forma automtica pelo
n roid quando a tela e criada No entanto a classe andr ' '
21 Ila e para isso precisa da ajuda da classe andro1d.v1ew.Vew, que
POY SUH VZ Se encarrega de desenhar os componentes visuais como cam d
fex, botoes e imagens. Para isso existem diversas subclasses especializafias de
mostra o na guravericar
33 No asdiaclas
~ ml
' Q
andro1d.1ew.View, conforme podemos verificar no diagrama de classes resu .d
_ grama podemos
Ed1tTet, Inagevew etc., responsveis por desenhar . I SCSITEXVIEW, Button,
os e ementos gracos da tela
Captulo 3 n Conceitos bsicos do Android 79
G andro|d.app.Activity
0 f|ndViewById(id: int): android.view.View _ G an ro Cl Id. ` .Vl
View ew
O setContentV|ew(layoutResID: int): void O dfBW(C6I'IV51 android-cr=DhicS.CvS)= VO
0 setContentView(view: android.view.\ew): void V

4 V l V A \
G android.widget.TetView G androidiwidgeumagewew Q and,.d_Vew_VewGmUp

Q android.widget.AbsoIuteLayout \\
G ndroid.widget.Button j G android.widget.EditTet | _ G android.widget.ReIativeLayout

Q andro|d.widget.FrameLayout

Q android.widget.L|nearLayout

G android.widget.TableLayout

Figura 3.3 - Hierarquia da classe android.1/ieu View

Na classe Activity, podemos vericar pelo diagrama que existem dois mtodos
setContentView(view); o primeiro recebe um argumento do tipo View, e o segundo recebe
um nmero inteiro indicando o recurso de algum arquivo XML de layout. Isso porque
no Android possvel construir a tela de duas formas: diretamente pelo cdigo-fonte
Java, ou utilizando um arquivo XML de layout que define a view que ser exibida.
Independentemente de qual verso do mtodo setContentView(view) for chamado,
seja informando um objeto ou um arquivo de layout, esse mtodo responsvel
por fazer a ligao entre a activity e a view que ser responsvel por desenhar a
interface grca da tela, e deve sempre ser chamado durante a execuo do mtodo
onCreate(bund1e) da classe Activity.

Nota: para cada tela da aplicao existir uma activity para controlar seu estado e
eventos, mas para denir a interface grca da tela utilizada uma view. Dentro
da activity necessrio chamar o mtodo setContentView(view) para informar a
view responsvel por desenhar a interface da tela.

Se voltarmos para vericar o cdigo-fonte da classe MainActivity, podemos verificar


que o mtodo setContentView(view) recebeu a constante R.1ayout.activity_main como
argumento:
setContentView(R.layout.activity_main); // Arquivo /res/layout/activity_main.xml

Nesse caso, a classe R utilizada para acessar o arquivo /res/layout/activity_main.


xml de forma mgica e convert-lo em um objeto do tipo View.
80 _ , .Menu
. ` - > nstra uma sim;~C_l `. z
menu),tiict.uii
l _ ' r-t i1. '\w C
, - . ~foiu criado
~ ~- bem

` f "
I ' tu;aI11Cl

_
automa .
Google Android - 4 dl

m\(,(|oonCreate0pttonsMenu( I ` g l ' `_ K ' Sm|cmm;1atle como criar


`|SSCMalnACHVlty'dpumdulln .' f l on0 ttonsItemSelected(MenuItem item) C
um item de menu na action bar. _l.1 0 nclm O D' li ido na action bar. Mas por
](Cl18

ns v' ~ '
cliarnado Ll*m*l *lltllm l><*f=' U cm dk lmllm JGIOQ estudar tudo isso dep0I5
enquanto no se l`\UP mm essa plrtc' I (

, . . V , ~ - 'f MLou utilizar. a.Al


)I
3.4 Arquivo de layout activity_main.xml
No Android possivel criar o layout da tela em atquix os X <
z _e criai
- . o. layout
~; em varara l1ica
sc; 8 dene
java. mas o recomendado - ~ . . ~ ' ' rillerdo
XML, pai
gocios da camada de apresentaao. Podemos dizer que a activity e O Cont 1
pad,-50 MVC (Model View Controller) e a view o arquivo XML com o layOUI~
Uma view pode ser um simples componente grco (boto, checkbox, imagem) OU
uma view complexa. que atua como um gerenciador de layout, que p0Cl COHYT
varias views-lhas e tem a uno de organiza-las na tela. Os gerenciadores de layout
serao explicados no captulo . No cdigo a seguir, podemos visualizar 0 cdigo
-lonte do arquivo /res/layouz/activiry_main.xml que dene a interface grca da tela.

/res/layout/activity__main.xml
<?ml version="2.G" encoding="utf-8"?>
<RelativeLayout xmlns:androtd="http://schemas.androtd.com/apk/res/androtd"
xmlns:tools="http://schemas.android.com/tools"
androtd:layout_width="match_parent" // Largura da tela
android:layout_hetght="match_parent" // Altura da tola
androtd:paddtngLeft="@di.men/activtty_hortzontal_margin" // Espaamento na esquerda
androtd:paddngRtght="Qdtmen/acttvtty_hortzontal_margtn" // Espaamento na direita
android:paddtngTop="@dimen/activty_vertical_margtn" // Espaamento no topo
androtd:paddingBottom="@dtmen/acttvity_vertical_margln" // Espaamento em batxo
tools:contet=".HalnActtvity">

<TetVtew // Componente (view) que mostra um texto (label)


android:textz@string/hello_world" // Mensagem
androtd:layout_width="wrap_content" // Largura da view
androtd:layout_height="wrap_content" /> // Altura da view
/RelativeLayout>

_ML
. _ un
.'~ ( k ` ai `
' ~' '- " Q af UWUS
_
No cdigo loi utilizada a sintaxe @d
tmen para acessar valores de espa im
;1m dehnidos no arquivo /res/values/dinicms.xml. Deixar essas constantes 1 qul fo
0 0 Cm
_ _e poder customizar
constanteso uand
valor d -l J. I
Zad I, 13 boa pratica porque podemos alterar o cdigo ein um lugar cekiltr' l`
essas
exemplo, para uulizarum valor dihei . _, , , _., q 0neCeS*m PW
tte para a versao tablet do mesmo aplicativo.
Captulo 3 1 Conceitos bsicos do Android 31
/res/values/dmens.xmI

<!- Espaanento recomendado pelas boas prticas do Android Design. -->


<dinen nane="activity_horizonta1_nargin">16dp
<dimen name:"activity_vertica1_nargin">16dp

Nota: o valor 16dp recomendado pelas boas prticas de interface (guidelines)


como o espaamento padro para as margens de um layout. A notao dp
(density independent pixels) uma unidade de medida do Android para tornar
transparente ao desenvolvedor a quantidade de pixels utilizada em cada aparelho.
No Android nunca se deve utilizar a unidade de pixels (px), pois isso pode trazer
resultados diferentes dependendo da resoluo da tela. Uma dica interessante
utilizar o Ctr1+C1ique no arquivo XML, pois ao passar o mouse em cima das
notaes @dimen/valor ou @string/texto podemos abrir rapidamente o arquivo em
que os recursos foram denidos.

No cdigo-fonte do arquivo /res/layout/actii/ity_main.xml podemos ver a tag


<TetView> que mostra um simples texto na tela. Essa tag dene o atributo
android:text:"@string/he11o_wor1d", que utiliza uma mensagem identicada pela
chave heiio_wor1d localizada no arquivo /res/values/stringsxml. Observe que no
arquivo existem as tags e <TetView>, que correspondem s classes
ReiativeLayout e Textview, respectivamente. Ambas as classes so filhas da classe
android.view.View do Android. A classe Re1ativeLayout um gerenciador de layout
que tem o papel de organizar a disposio dos componentes. Nesse exemplo a
tag <TetView> est contida dentro da tag justamente para compor
a interface da tela.

Nota: para cada tag denida no arquivo XML de layout existe uma classe
correspondente que filha de android.view.View. Para exemplificar, as tags
<TetView>, e correspondem s classes android.widget.TetView,
android.widget.InageView e android.widget.Button.

Se voc abrir o arquivo /res/layout/activity_main.xml no editor, ver que na parte


inferior existem duas abas para alternar entre o cdigo-fonte XML e o editor
visual (Figura 3.4).
A figura 3.5 demonstra a opo Preview AII Screen Sizes, que mostra no editor a pr

muito no desenvolvimento.
-visualizao para diferentes tamanhos de telas. Esse recurso incrvel e ajuda
82 ..' .. ... ,_ ._ Goo9Ie Android - 4' d9

,,z.i
"o Q .~.z--~ - ooo e
.. . tz.. ii W o ~~~
izz,..,.... iai- ea tu
g 1 lfnmel YU' 7 .. .J
i l u l ineml you! (Homontd),
i [mari you! (VCWU i
Tablcl you!
W mzunow
l `5uyn
llljkclmvelayout
E Cl W'
Plam Tcxwitvv
i gm; urge Ten
i Wii Medium Text
E *Abi Small Text
J Button

rz ] p 4
wi Smallufkon

ll \Rdo8u!!on
~.
Q _ .`_...z.. - --fz
l__QCheclr8ox

Figum 3.4 - Editor visual.

No Android voc deve criar layouts que se ajustem automaticamente ao tamanho


e a resoluo da tela; portanto, se voc desenvolver corretamente, no tera pro
blemas com a diversidade de aparelhos. Apenas no caso dos tablets que tm um
grande espaco disponvel na tela talvez seja necessario um layout espec eo para
oferecer tuna melhor experincia ao usuario. Isso e feito para deixar o aplicativo
mais agradavel a fim de que seja utilizado no tablet, mas por padro o layout
de um aplicativo que funciona no smartphone deve funcionar perfeitamente no

i! (tuto
r iLmsupevo
I ~., m ifpw
tablet ou ate mesmo no Google TV

jmlquuh
3"'*Y-"""-W' '* vt A i _l `
G' ' "Nmn4~ - Owomm "Muvmmy- 6- vwzy. ii i
on bw __ Q'
i l *reohilcyoutduge tu
l UQIQOQNH..
l V *"*~vw *r;~~e~,i1.!-\r~.t.~

L - ii 'hlxilv zilug-z,
z F3 \ Pim.. nrguwim tm
hl pl Andmud Vamu-u
i'\*l li: r-= iva fz.1\i
' V-'Y-' la.z ;l\r rx

p logon uma Moo.


3 nz:o.zz
_ ;;ChIrlon l
i ` SIIKTI
i

' ei .
i

'_ .Y vlw
lr ieo,'-Jp c

Fium3$---- ' -._ .~ ,.


g P' ' WWl~It1 para varios tlispositivos
Captulo 3'n Conceitos bsicos do Android 33
3.5 Arquivo strings.xmI
O arquivo /res/values/stringsxml contm as mensagens para organizar os textos
em um nico arquivo centralizado, o que uma boa prtica de programao.
As mensagens desse arquivo podem ser traduzidas para diversos idiomas para
internacionalizar o aplicativo. Por padro, esse arquivo contm o nome do apli
cativo que digitamos ao criar o projeto e a mensagem que aparece na tela inicial.

/res/values/strings.xmI
<?ml version="2.G" encoding="utf-8"?>

<string name="app_name">HelloAndroidStudio
<string name="hello_world">Hello world!
<string name="action_settings">Settings
O nome do aplicativo denido pela chave app_name, e o texto que aparece na tela
denido pela chave hello_world. No arquivo /res/layout/actii/ity_main.xml, ao denir
a tag <TetView>, foi utilizado o atributo android:tet="@string/hello_world" para refe
renciar a mensagem denida pela chave hello_world. O padro para acessar essas
mensagens, como voc j deve ter percebido, @string/nomeDaChave.
Para criar um aplicativo com suporte a diversos idiomas, basta criar uma pasta
/res/values/values(cdigo do idioma) e traduzir 0 arquivo /res/values/strings.xml . A seguir
temos um exemplo de uma pasta com as mensagens em ingls e outra em portugus.
/res/values-en/strings.xml
/res/values-pt/strings.xml

3.6 Classe R

A classe R gerada automaticamente ao compilar o projeto e contm as constantes


para acessar os diversos recursos do projeto. Sempre que um recurso adicionado
no projeto, como por exemplo uma nova imagem, a classe R gerada automatica
mente para conter uma constante para 0 novo recurso criado. No Android Studio
a classe R gerada na pasta app/build/generated/source/r_

R.java
public nal class R {
public static nal class attr { }
84

l} _i
public static nal class dcawable a 20000.
Dublic static nal int icon=97 0 f

public static nal class lay0U


public static nal int main=0x7fG3000G,

public static nal class string { .


Google Android - 4 d9

public static nal int DD_3e=@7f9400e1'


public static nal int hello=07f040000,
}

naente!
Nota: a classe R nunca deve ser alterada manual -F __ _

3.7 Informaes sobre como acessar recursos de texto e imagem


Vamos aproveitar que falamos da classe R e explicar um pouco mais sobra sm
taxe para acessar os recursos do projeto. Para explicar alguns conceitos, veia este
trecho de cdigo do arquivo AndroidManiest.xml.
<application android:icon:"@nipmap/ic_launcher" // cone do aplicativo na tela Home
android:label="@string/app_name" ... > // None da pllcao na tela He

A tag android:icon dene o cone do aplicativo que vai car na tela inicial do
Android, sendo que a sintaxe @drawable utilizada para acessar a imagem
/res/drawable/ic_launchexpng. Essa sintaxe padro para acessar imagens sempre
que voc estiver em algum arquivo XML. Por exemplo, ao adicionar uma view
do tipo Imageview no arquivo /res/layout/activity_main.xml, podemos mostrar uma

Porm cas
gura utilizando a notao @drawable/none_gura:

ImageView android:src="@drawable/none_da_gura"

_ _ - os e mr um
1 _ _ _
android:layout_width="wrap_content" android:layout_height="wrap content" />
magem mamicamente
o voc precise acessar esse Imageview e atualizar a i d'
pelo codigo Java, vai precisar utilizar a classe R Primeiro precisam d `
identicador para o Inageview com a tag android:id e a sintaxe @+id/identgado
<InageView android:id="@+id/img"

- ~ _ 9 = wrap_content" />
android:layout_width="wrap content" androidlay0ut hei ht "

Com este id possvel utilizar o mtodo ndViewById(id) no cdi o d ' '


imagem
acessar come adepois
esse objeto, Condisso
i para acessar
utilizada a classe o
R recurs d
a actmty para
stante R . drawable . none_inagem. O 8
Captulo 3 i Conceitos bsicos do Android 35
// Encontra o objeto pelo id
Imageview img = (ImageView) ndViewById(R.id.img);
// Atualiza a imagem dinamicamente. A classe R utilizada para acessar o recurso.
img.setImageResource(R.drawable.nome_da_imagem);

Agora vamos falar um pouco de mensagens e o arquivo /res/values/stringsxml. Se


voc reparar novamente no cdigo do arquivo AndroidManifest.xml, ver que o
nome da aplicao foi denido pela chave @string/app_name. A mesma sintaxe pode
ser utilizada ao criar o layout dos arquivos em XML. Por exemplo, no arquivo
/res/layout/activity_main.xml foi criado um Textview que referencia a mensagem
@string/hello_world, conforme demonstrado a seguir.
<TetView android:text:"@string/hello_world"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

Da mesma forma que zemos com o Imageview, tambm possvel atualizar o


texto de um Textview dinamicamente. Para acessar o objeto do Textview no cdigo,
novamente preciso denir um identificador para a view com a tag androidzid.
<TetView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

Para acessar esse objeto Textview no cdigo, basta utilizar o mtodo ndViewById(id).
Note que a constante R.string.hello_world utilizada com objetivo de acessar a
mensagem hello_world que est denida no arquivo /res/values/stringsxml.
// Encontra o objeto pelo id
Textview text = (Textview) ndViewById(R.id.text);
text.setText(R.string.hello_world); // Atualiza o texto dinamicamente.
A fim de encerrar o assunto, vamos fazer uma breve reviso. Lembre-se de que a
notao com o caractere @ utilizada sempre que for necessrio acessar um recurso
em um arquivo XML. Caso o recurso precise ser acessado no cdigo Java, preciso
utilizar a classe R. A tabela 3.1 compara as duas formas de acessar os recursos:

Tabela 3.1 - Formas de acessar um recurso

Objetivo Utilizando XML Uti|izandoacIasseR


na pgastag/r'es/drawable _
z\cessarz1iniageniouzpngltxxizada. @drawab1e/foto R drawable foto l

Acessara mensagem hello definida @Strng/heuo R'String'heu0 2


no arquivo /res/values/strmgs.xrrzl l
86 Google Android - 4 edio
3.8 Arquivo buiId.gradIe
O sistema de build do Android baseado no Cradle. No projeto existe o arquivo
build. gradlc padro de todos os mdulos e o arquivo app/buildgradle com as con gu
raes de compilao do mdulo app, que onde ca o cdigo-fonte do aplicativo.
A seguir, podemos visualizar o arquivo buildgradle que ca na raiz do projeto.
Na prtica, voc no vai alterar esse arquivo, exceto se o plugin do Gradle for
atualizado, pois no arquivo ca o cdigo da verso do plugin.

buiId.gradIe
buildscript [
repositories {
jcenter() // Repositrio padro do Android
}

dependencies {
// Verso do plugin do Gradle. Se o plugin for atualizado atualize este cdigo
// Caso tenha um erro de compilao nessa linha, tecle Alt+Enter para abrir o assistente
// O assistente vai ajuda-lo a congurar a verso correta do plugin
classpath 'com.android.tools.build:gradle:1.0.9'
}

allprojects {
repositories {
jcenter() // Repositrio para todos os projetos
l
}

O arquivo mais importante o app/buildgradle que ca dentro da pasta do m


dulo app do projeto, lugar em que ca o cdigo-fonte da aplicao. No arquivo
app/buildgradle voc congura a verso do aplicativo e tambm a verso mnima
do Android (API Level) que seu aplicativo suporta, alm de declarar as bibliotecas
que so necessrias para a compilao. A seguir podemos visualizar o cdigo-fonte
do arquivo app/build.gradle. '
app/buiId.gradIe
apply plugin: 'com.android.application' // Aplica o plugin 'android' no script de
// compilao do Gradle
android { // Este elemento congura os parmetros de compilao do mdulo
compileSdkVersion 22 // API Level usada para compilar o cdigo (deve ser sempre a ltima)
buildToolsVersion "22.6.1" // Verso do Android SDK Build-tools baixada pelo SDK Manager
Captulo 3 1 Conceitos bsicos do Android 37
defaultCong { // Este elemento congura os itens do AndroidManifest.ml
applicationld "br.com.livroandroid.helloandroidstudio" // Pacote do projeto
// (identicador nico da aplicao)
minSdkVersion 22 // API Level minima que o aplicativo suporta.
targetSdkVersion 22 // API Level mxima utilizada para otimizar o build na compilao
versionCode 1 // Cdigo para identicar o aplicativo no Google Play
versionName "1.0" // Verso do cdigo com formato amigvel para o usurio
}

buildTypes { // Este elemento congura os tipos de build (debug e release)


release { // Congura o build do tipo release
minifyEnabled false // Congura para utilizar o proguard e obfuscar o cdigo
}

l
dependencies { // Declara as dependncias (bibliotecas) para compilar do projeto
// Inclui todos os arquivos .jar da pasta libs na compilao
compile leTree(dir: 'libs', include: ['*.jar'])
}

No nal do arquivo app/buildgradle so declaradas as dependncias do projeto,


mas isso vamos exercitar bastante durante o livro, portanto que tranquilo. A
parte mais importante desse arquivo de congurao so todos os itens que cam
dentro do elemento defaultCong. Nesse elemento so congurados: o pacote do
aplicativo, a verso mnima do SDK e o cdigo de verso do aplicativo. Antiga
mente essas conguraes cavam no arquivo AndroidManifest.xml, mas agora
tudo gerenciado pelo Gradle.
Com o Gradle possvel criar builds customizados do aplicativo, para, por exem
plo, criar verses que apontem para web services de homologao ou produo.
Todo esse poder e flexibilidade para fazer o build uma das vantagens do Gradle,
alm de gerenciar as dependncias, claro.

Nota: todo aplicativo (apk) deve ser assinado com um certicado digital antes
de ser instalado no dispositivo. Por padro o sistema de build do Gradle
compila o projeto no modo debug e release. O modo debug utilizado durante
o desenvolvimento, e o aplicativo assinado com o certicado digital de
desenvolvimento, o qual criado automaticamente pelo Android Studio e ca
na pasta do usurio ~/.android/debug.lceystore. O modo release utilizado para
publicar o aplicativo no Google Play e deve ser assinado com outro certicado,
o qual voc precisa criar. Vamos estudar mais detalhes sobre isso no captulo
38, sobre Gradle.
88 Google Android - 4 edio
3.9 Log(at - Escrevendo mensagens de log
Em java, para imprimir uma mensagem no console utilizado 0 coman
do Systen.out.println(string), mas no Android recomendado utilizar a classe
android.util.Log, que contm mtodos utilitrios para imprimir informaes com os
niveis de detalhes desejados, como informao (i), debug (d), warning (W) C ff0 ()~

Dica: no Android Studio, utilize a tecla de atalho Alt+6 para abrir a janela 6:Android.
Nessa janela voc ver os logs do LogCat,`que tem por nalidade gerenciar todos
os logs do sistema operacional.

Com a classe android.util.Log possvel criar logs de informao, debug, alertas e


erro. Cada log pode ser escrito em uma determinada tag, para que posteriormente
apenas as mensagens desejadas sejam recuperadas. Para demonstrar como criar
logs em diferentes nveis de severidade, altere o cdigo-fonte da classe MainActivity
conforme demonstrado a seguir.

MainActivity.java

inport android.util.Log;
public class MainActivity extends Activity {
private static nal String TAG = "livro";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
OOI

Log.v(TAG log de verbose");


Log.d(TAG log de debug");
Log.i(TAG log de info");
Log.w(TAG log de alerta"); _
Log.e(TAG log de erro", new RuntimeEception("teste de erro"))
}

Observe que a constante TAG denida na classe utilizada como identicador das
mensagens. Isso til para ltrar as mensagens por tags/categorias, facilitando a
visualizao e debug. Os mtodos da classe Log so bem simples, e comeam com
a primeira letra do nvel de severidade. Por exemplo, para imprimir um log com
nivel de alerta utilizado o mtodo Log.w(tag,nensagen). O W da palavra warning
Captulo 3 n Conceitos bsicos do Android 39
(alerta). importante entender que necessrio informar a tag do log, para que
depois seja possvel ltrar os logs apenas da tag desejada.
Agora execute o cdigo e visualize os logs gerados na janela LogCat (Figura 3.6).
Observe que mensagens vermelhas no LogCat so erros. Neste exemplo lancei uma

. . logar ~-o'
exceo no cdigo para voc visualizar um erro de teste, portanto acostume-se a olhar
o LogCat, pois qualquer exceo lanada pelo seu aplicativo ser detalhada aqui.

os-oe 15:41 O4 son 1211-1217/2 I/Jdwpi rqnonnq se o dzbuqqez puizzq and droppinq 'A Y
h9~06 l5:4 04 500 1073-1079/7 I/Jdwpi Iqnoxring second debuqger ~- pcing and drcppinq j
59-96 5 4 O-1 500 1280-1286/? I/Jdwp= Iqnoring second debuqqer -~ a ceptinq and droppinq
O9 O6 15 Of; 680 402-416/? I/Choreoqrapheri Skipped 30 frame! Th pplication my be doing wo much work on its main t 1. 5
9 06 l5 4 OS 80 402-416/7 I/Chareographer Skipped 30 frames! Th pplmation may be doing too much work on its mam t `
9 O6 l i O5 490 1750-1750/? V/livro log de verbose
9 E 15 -i O5 3flO 402-1G/7 I/Choreographerf Skipped 36 xames! Th applic :on may be doing too much work cm 'cs main 1 `.' 1
9 3 15 05 4390 1750-1750'? Dlivre log de debug
O 15 ~ 05 4 O 1750-1150/7 I/livro' log de info
9 E 5 4 5 500 1 50-1750/? H/livro~ log de alerta
l ~l Sl 175 -1750/? E/livro log de erro
g R nti.:eE p 1 n neste de erro
i::.l1vroa ci d livroend:o1.d_cp03.Hair\r:t1..'i;y.onC:e'ce(Ma1rzAct:;v1tz.zava:31)
ac ar:1r:1_d.app A 1:ivir.y.performC:ear:e{ .`z ' ,_;ff;_*',, ~1)
andrf.:1d.a p I umencation.calLz'c:1vii:yCnCreate(` .~V ~ '''= ;.''";_V,.i.:f3; f)
c ndroid pp A t' ` _1'hrea .performLaunchActiv1t:y(%ir* i-:':.2, '1z~)
:X cl pp A \:` `1:y'1'h ea .hondleLaunchl1;ivity(.fz; ;.. iQ:j_;_-;;fff_'_f_)
d d pp A ` y'l'b .acces:r$E0O(`. m jzff ` f f f)
d d pp A y'Ih i.hand1e2$es:sege(..;:* . ; ' ~. _ ;; _)

Figura 3.6 - ]anela LogCat.

Contudo, podemos vericar que nos logs no aparecem apenas as mensagens


que criamos, pois tambm aparecem todas as outras mensagens do sistema ope
racional. Mas foi exatamente por isso que criamos os logs utilizando a tag livro,
pois agora podemos ltrar as mensagens apenas dessa tag.
Para criar um ltro, procure no canto direito superior da janela LogCat o combo
com os ltros e clique em Edit Filter Conguration. Feito isso, crie uma congurao
de ltro para a tag livro, conforme mostra a gura 31
Create New Logcat Filter z=- z~=.

+ - =~= Ml
Filter ogcat messages by different parameters.
Empty elds will match ali messages.

=LI=z=rff== W l
. by Log Message (regex):
by Pgckage Name: J
bz-flD= l_w__w_W_,_,__-_;
by Log Lad: VEIKE _

l-<=*l

Figura 3.7 - Criao de um ltro para 0 LogCat.


90 Google Android - 4 edio
Nesse caso o nome da tag livro, pois o valor da constante TAG que definimos na classe.
private static nal String TAG = "Iivro";

Depois de criar o ltro, possvel visualizar somente os logs gerados pelo nosso
aplicativo (Figura 3.8). Agora podemos ver somente as mensagens que nos interessam.
Q- _
ioglfvel Verbos: lf '``- A`
i 41 1
1 il1 "
ql

'J
F
;) il J
J

l"

Pi
1 l
' l
J
l

Figura 3.8 - janela do LogCat exibindo as mensagens do ltro desejado.

Importante: acostume-se a utilizar o LogCat, pois nessa janela que todas as


mensagens de logs e erros so exibidas. Lembre-se de que, em caso de erro, o
stack trace das excees aparece no LogCat, e voc pode inclusive vericar
o nmero da linha que originou o problema. Sempre que seu aplicativo travar
(force close), olhe as mensagens de erro no LogCat. Lembre-se de no utilizar
o System.out.print1n diretamente, pois recomendado utilizar o LogCat e separar
os logs em nvel de severidade e tags.

3.10 Tratamento de eventos

Neste prximo exemplo vamos alterar o layout da MainActivity para criar uma tela
de login com dois campos para o usurio e senha, a m de fazer a validao desse
login de forma simulada. Neste exemplo vamos aprender no s a tratar o evento
de clique em um boto, como tambm a ler os valores digitados pelo usurio.
Para comear a brincadeira, altere o cdigo-fonte do arquivo /res/layout/
activity_main.xml conforme demonstrado a seguir:
Captulo 3 I Conceitos bsicos do Android 91
/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:gravity="center_vertical" android:orientation="vertical"
android:padding="16dp">
<TetView
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:tet="@string/usuario" />

<EditTet android:id="@+id/tLogin"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:inputType="tet" android:singleLine="true" />
<TextView
android:layout_width="natch_parent" android:layout_height="wrap_content
android:layout_narginTop="10dp" android:tet="@string/senha" />
<EditTet android:id="@+id/tSenha"
android:layout_width="match_parent" android:layout_height="wrap_content
android:inputType="textPassword" android:singleLine="true" />
<Button android:id="@+id/btLogin"
android:layout_width="2G0dp" android:layout_height="wrap_content"
android:layout_narginTop="6dp" android:tet="@string/login"
android:textColor="#ffffff" android:layout_gravity="center"/>

A tag raiz desse layout um Lineartayout, que um gerenciador de layout que


organiza as views na horizontal ou vertical. Nesse caso a orientao foi congu
rada como vertical com o atributo android:orientation="vertical", portanto as views
sero posicionadas uma abaixo da outra. A tag <TetView> um simples label e a
tag <EditTet> um campo de entrada de dados para o usurio digitar um valor.
A tag , acredito que dispensa apresentaes.
O importante voc perceber que no layout foi adicionado um id para os dois
campos de texto e tambm para o boto. Isso feito com a tag android:id. No c
digo vamos utilizar o mtodo findViewById(id) para acessar esses objetos pelo id. A
seguir podemos vericar o cdigo-fonte atualizado da classe MainActivity que trata
o evento no boto e faz o login para o usurio ricardo e senha 123'

MainActivity.java

// Faa os imports das classes necessrias aqui.


// Caso no esteja familiarizado com Java, utilize o assistente do Android Studio.
92 Google Android - 4' ed0
/I Na linha do erro voc pode clicar neste assistente para fazer o import.
// O atalho ALt+Enter abre o assistente se o cursor estiver sobre a linha.
// O menu Code > Optimize Imports tambm pode ajudar.
public class HainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btLogin = (Button) ndViewById(R.id.btLogin);

@0verride l
btLogin.setOnClickListener(new View.0nClickListener() {

public void onClick(View v) {


Textview tLogin = (Textview) ndViewById(R.id.tLogin);
Textview tSenha = (Textview) ndViewById(R.id.tSenha);
String login = tLogin.getTet().toString();
String senha = tSenha.getTet().toString();
if("ricardo".equals(login) && "123".equals(senha)) {
alert("Bem-vindo, login realizado com sucesso.");
} else {
alert("Login e senha incorretos.");
}

});
}

private void alert(String s) {


// A classe Toast mostra um alerta temporrio muito comum no Android
Toast.makeText(this,s,Toast.LENGTH_SHORT).show();
l
}

No Android, para tratar os eventos de um boto utilizado o mtodo


set0nClickListener(iistener). Esse mtodo recebe como argumento uma instncia
da interface android.view.View.0nClickListener. A interface View.0nClickListener define o
mtodo onClick(view), o qual chamado quando o evento ocorrer, passando corno
argumento o objeto da view que gerou o evento, neste caso, o boto.

Nota: para definir um id para os componentes utilizada a notao android:id="@+id/


identicador_aqui". O mtodo ndViewById(id) utilizado no codigo para encontrar
uma view pelo seu id.

Depois dessa alterao, execute o projeto para conferir o resultado (Figura 39).
Ao digitar o usurio "ricardo" e senha " l2.3", o login de teste realizado.
Captulo 3 I Conceitos bsicos do Android 93

lJWQi ` 'fz ."_V' f%


Usuno
ricardo

Senha

..i

Bem vindo, login fealizado com sucesso.

Figura 3.9 - Tratamento de eventos.

Desenvolvedores com alguma experincia devem ter percebido que o tratamento de even
tos no Android similar a qualquer outra linguagem. Na verdade, existem muitas formas
de escrever esse mesmo cdigo, basta informar ao mtodo set0nClickListener(listener)
algum objeto que implementa a interface and roid .view . View . 0nClickListener.

O cdigo anterior utilizou o conceito de classes annimas do Java para fazer


essa implementao. Outra maneira de tratar o evento fazer a classe da activity
implementar a interface View.0nClickListener, e utilizar o this (self) para referenciar
a prpria instncia da classe para tratar o evento.

MainActivity.java

public class MainActivity extends Activity inplenents View.0nClickListener {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
Button btLogin = (Button) ndViewById(R.id.btLogin);
button.set0nClickListener(this); // O this representa a instncia da classe
}

@0verride
public void onClick(View view) { // A prpria classe implementa View.0nClickListener
// Cdigo que trata o evento aqui
}

}
94 Google Android - 4 edio
A desvantagem dessa implementao que, se voc tiver mais de um boto na
tela, o mesmo metodo onClick(view) ser chamado para todos OS botes- NCSSC
caso o parmetro View indica qual componente gerou o evento, e deve ser utilizado
para descobrir em qual boto foi feito o clique, conforme demonstrado a seguir.

ii MainActivity.java

public class MainActivity extends Activity implements 0nClickListener {


@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
Button btOk1 = (Button) ndViewById(R.id.botao0k);
Button btOk2 = (Button) ndViewById(R.id.botao0k2);
// Vamos imaginar que existem dois botes na tela
bt0k1.set0nClickListener(this);
bt0k2.set0nClickListener(this);
}

@0verride
public void onClick(View view) {
if(view.getId() == R.id.botao0k) {
// clicou no boto 1
} else if(view.getId() == R.id.botao0k2) {
// clicou no boto 2
}

Vejamos outra forma de escrever o mesmo cdigo, que criar um mtodo para
cada boto. Eu particularmente gosto desse jeito. A vantagem que fica simples
de separar cada clique em um mtodo diferente.

MainActivity.java

public class MainActivity extends Activity {


@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
Button btLogin = (Button) ndViewById(R.id.btLogin);
btLogin.set0nClickListener(onClickLogin());
}
Captulo 3 n Conceitos bsicos do Android 95
// O mtodo onClickLogin() retorna uma implementao de View.0nClickListener
private View.0nClickListener onClickLogin() {
return new Button.OnClickListener() {
public void onClick(View v) {
// Tratar o evento de clique aqui.
}

};
}

Dica: acostume-se a utilizar o assistente de cdigo com a tecla de atalho Alt+Enter


para complementar o texto do cdigo. Nesse exemplo eu digitei o mtodo
onClickLogin() e todo o restante foi criado com o assistente.

Ainda existe uma ltima maneira de tratar os eventos. Existem desenvolvedores


que preferem denir o nome do mtodo que deve ser chamado no arquivo XML
de layout.
<Button android:id="@+id/btLogin"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_margin="6dp" android:tet="Login"
android:onClick="onClickBtLogin" />

Nesse caso o atributo android:onClick="onClickBtLogin" informa que o mtodo


onClickBtLogin(view) ser chamado na classe da activity e deve existir, conforme
demonstrado a seguir:
public void onClickBtLogin(View view) {
// Trata o evento de clique aqui
}

Para gerar o cdigo do mtodo na activity novamente o assistente do Android


Studio pode ajud~lo. No arquivo de layout, clique na linha em que o atributo
android:onClick="onClickBtLogin" foi denido e tecle Alt+Enter. Voc ver uma opo
para criar o mtodo na activity
Eu no recomendo fazer desta ltima maneira, pois se o nome do mtodo for
digitado de modo incorreto voc descobrir o erro somente ao executar o projeto.
Em meus projetos, prero definir os mtodos no cdigo, pois qualquer problema
descoberto em tempo de compilao. Em muitos exemplos deste livro, acabei
usando essa tcnica de definir o mtodo no XML, pois ela reduz a quantidade
de cdigo a fim de facilitar a leitura.
* CAPTULO 4
A ) Activity
\-4
'I

A classe Activity representa uma tela da aplicao e responsvel por controlar


os eventos da tela e denir qual View ser responsvel por desenhar a interface
grca do usurio.
Neste captulo, vamos estudar como navegar entre telas da aplicao, como feita a
passagem de parmetros de uma tela para outra e o ciclo de vida da classe Activity.

4.1 Activity
Uma activity uma classe que deve herdar da classe android.app.Activty ou de
alguma subclasse desta, a qual representa uma tela da aplicao e responsvel
por tratar os eventos gerados nessa tela.
A classe da activity deve sobrescrever o mtodo onCreate(bundi.e). Esse mtodo
obrigatrio e responsvel por realizar a inicializao necessria para executar a
aplicao, como por exemplo chamar o mtodo setContentView(view) para denir
a interface de usurio.

Cada activity deve ser obrigatoriamente declarada no arquivo AndroidManiest.xml.


Isso feito por meio da tag , como demonstrado a seguir.
<activtty androd:name=".MinhaC1asseActivity" />

No h muito o que falar da classe androd.app.Actvity. Apenas que utilizada para


construir uma tela da aplicao. Assim, se voc est pensando em criar uma nova
tela, vai precisar de uma nova activity A coisa mais importante que voc precisa
lembrar que, sempre que uma nova classe de activity for criada, ela precisa ser
declarada no arquivo AndroidManifest.xml.

Nessa congurao, a activity geralmente declarada com a sintaxe do ponto, o


que indica que o pacote da classe relativo ao pacote do projeto. Por exemplo, se

96
Captulo 4 n Activity 97
o pacote do projeto br.com.1vroandrod.cap4 e a classe tambm est nesse pacote,
podemos utilizar a sintaxe do ponto.
<activty android:name=".MinhaClasseActvty" />

No entanto, se a classe da activity estiver em outro pacote, como por exemplo br.com.
1vroandrod.cap4.activity, podemos declarar essa activity com a seguinte sintaxe:
<activty androd:name=".activity.MinhaC1asseActvty" />

Se achar necessrio, denir o nome completo da classe tambm no errado:


<actvity androd:name="br.1vro.androd.cap4.activity.MinhaC1asseActvity" />

Nota: no erre o nome da classe da activity na congurao do AndroidManiest.xml


e lembre-se de que o nome precisa ser exatamente igual, pois case-sensitive.
Uma boa dica para ter certeza de que o nome est correto segurar a tecla Ctrl e
passar o mouse em cima do nome. Se 0 texto car selecionado, voc pode clicar
para abrir o cdigo-fonte da classe no editor. Se o editor abrir tudo est correto.

Na prtica podemos associar a palavra activity palavra tela Por exemplo,


vamos analisar as seguintes frases, na qual a primeira est escrita com a palavra
activity e a segunda com a palavra tela'
1. Iniciar uma activity: iniciar uma tela
2. O sistema operacional decidiu encerrar a actvityX para economizar memria:
O sistema operacional decidiu encerrar a tela X para economizar memria?
Seguindo esse raciocnio, se for necessrio criar uma nova tela no projeto, voc
saber que preciso criar uma nova activityA traduo da palavra activity para o
portugus atividade. Ento, tambm podemos dizer que uma activity representa
uma atividade, ao ou funcionalidade que 0 usurio pode realizar dentro de
sua aplicao. Se preferir, sempre que ler a palavra activity em uma frase pode
interpreta-la como atividade* Prero manter a palavra em ingls neste livro para
maior consistncia.

4.2 Classes FragmentActivity e AppCompatActivity


Existem algumas subclasses famosas da classe androd.app.Actvity que bom
voc conhecer desde j. Agora talvez o conceito no fique claro, pois estamos s
comeando, mas futuramente voc pode voltar neste tpico e l-lo novamente.
98 Google Android - 4 edio
listou explicando isso porque o Android Studio pode gerar um cdigo diferente, de
pendendo da verso do Android que voc selecionar no wizard de criao de projetos.
Ento vamos l! No Android 3.0 foram criadas a action bar e a API de fragments.
A action bar representada pela classe androd.app.ActonBar e um fragment pela
classe androd.app.Fragment. Para utilizar essas classes em verses mais antigas do
Android o Google criou a biblioteca de compatibilidade. A biblioteca de compati
bilidade v4 (API Level 4) compatvel com o Android 1.6 ou superior, e dentre as
APIs que ela tem est contida a API de fragments. Mas para usar os fragments de
compatibilidade todas as activities do projeto precisam herdar de androd.support.
v4.app.FragmentActivty, que uma subclasse especial da classe androd.app.Actvty
que permite utilizar os fragments nas verses antigas do Android. Ao utilizar a
biblioteca v4 voc deve utilizar a classe androd.support.v4.app. Fragment para acessar
os fragments e no a classe nativa androd.app.Fragment.
Da mesma forma, existe a biblioteca de compatibilidade v7 (API Level 7), que
compatvel com 0 Android 23 ou superior. A biblioteca v7 contm a action bar
de compatibilidade e permite habilitar a action bar nos dispositivos mais antigos.
Para isso todas as activities do projeto precisam herdar de androd.support.v7.app.
AppCompatActivty. Ao utilizar a biblioteca v7 voc deve utilizar a classe androd . support.
v7.app.ActonBar para acessar a action bar e no a classe nativa android . app.ActonBar.
Ento basicamente temos trs classes de activity que voc precisa conhecer.
androd.app.Actvity - Classe padro da activity
androd.support.v4.app.FragmentActvty - Classe da biblioteca de compatibili
dade v4, que permite utilizar os fragments em verses antigas do Android.
androd.support.v7.app.AppCompatActvty - Classe da biblioteca de compatibi
lidade v7, que permite utilizar a action bar em verses antigas do Android.
A classe AppCompatActi.vty lha de FragmentActvty.

Como exerccio, voc pode criar outro projeto no Android Studio e escolher a
API Level 9 (Android 23) como a mnima do projeto. Ao fazer isso, o Android
Studio vai criar 0 projeto utilizando a biblioteca de compatibilidade v7, e nesse
caso a ManActvity ser lha de AppCompatActvty. Bem, teoricamente era isso que
deveria acontecer, porm a verso do Android Studio que utilizo ao escrever este
livro gerava o cdigo antigo.
public class ManActvty extends androd.support.v7.app.ActlonBarActvty {
O correto seria usar a classe AppCompatActivty:

public class ManActvty extends androld.support.v7.app.AppCompatActlvty {


Captulo 4 I Activity 99
O wizard gera a activity de compatibilidade corretamente, porm ele usa a
ActonBarActvty em vez da AppCompatActvty. O problema que em 21 de abril de
2015 o Google postou no blog que a ActonBarActvty foi descontinuada (deprecated)
a favor da AppCompatActvty. possvel que quando voc estiver lendo este livro o
wizard do Android Studio j esteja gerando o cdigo atualizado, pois isso acon
teceu quando eu estava terminando de revisar o livro.

Nota: no se preocupe com essas questes de compatibilidade agora, estou


apenas introduzindo o assunto para voc ter uma ideia. Por enquanto utilize a
classe androd.app.Acti.vi.ty nativa nos exemplos. Durante seus estudos, ao criar
um novo projeto sempre utilize a maior verso do Android disponvel, pois vou
inform-lo quando precisar criar um projeto com compatibilidade.

As dependncias para essas bibliotecas so declaradas no arquivo buildgradle,


mas vamos deixar para mostrar esses exemplos mais tarde, pois ainda precisamos
estudar o bsico sobre activity
Nos captulos sobre Action Bar (5) e Fragments (8) vamos aprender a utilizar as
bibliotecas de compatibilidade, portanto que tranquilo. Por ora, le~mbre-se de
que no captulo 2 eu pedi para voc criar o projeto no Android Studio sempre
utilizando a ltima verso, assim estamos utilizando a classe padro da activity
que a androd.app.Activty.
Pelo menos agora voc j conhece essas trs classes do tipo activity e quando
encontrar algum cdigo que as utilize ter uma ideia do porqu. Esse tpico foi
apenas para alert-lo disso, mas, como eu j disse, que tranquilo porque vamos
estudar tudo isso depois.

4.3 Ciclo de vida de uma activity


Para ser um bom desenvolvedor Android, muito importante entender o ciclo de
vida de uma activitjg isto , os possveis estados que ela assume. O importante
entender que o sistema operacional cuida desse ciclo de vida, mas ao desenvolver
aplicaes importante levar cada estado possvel em considerao para desen
volver uma aplicao mais robusta.
Por exemplo, digamos que voc criou um maravilhoso jogo no Android e, enquanto
o usurio est jogando, algum faz uma ligao para ele. Nesse caso, o usurio
vai parar o jogo para atender a ligao, no ? E o que acontece com o jogo? O
sistema operacional vai parar o jogo temporariamente (pause) e coloc-lo em
U
100 Google Android - 4 edio
segundo plano enquanto o usurio atende a ligao. Depois que a ligao terminar,
o sistema operacional rciniciar a aplicao do jogo. Agora, a grande pergunta
: ser que seu jogo vai conseguir continuar de onde parou? Ser que o estado e
informaes do jogo sero salvos ou tudo ser perdido? O Android fornece toda
a estrutura necessria para controlar este estado, basta voc entender o ciclo de
vida de uma activity e implementar os mtodos corretamente.
Uma activity tem um ciclo de vida bem denido. Cada activity iniciada inserida
no topo de uma pilha, chamada de activity stack ou, se preferir traduzir, pilha
de atividades? O conceito de pilha j bem conhecido por todos. Assim, sempre
que uma nova activity inserida no topo da pilha, a activity anterior que estava
em execuo ca logo abaixo da nova. A activity que est no topo da pilha a
activity que est em execuo no momento, as demais podem estar executando
em segundo plano, estar no estado pausado ou totalmente paradas.
Sobre essa pilha importante voc entender que no Android at a tela inicial
(Home) uma activity Na verdade, sempre que voc abre um aplicativo, a activity
que representa aquela tela inserida no topo da pilha. Ento digamos que voc
esteja na tela inicial do Android e clicou no cone do Browser. Depois voc clica
no cone do Gmail. Nesse momento temos a seguinte pilha: Home > Browser> Gmail,
sendo que a aplicao da tela inicial sempre a base da pilha. Nesse caso alguma
activity da aplicao do Gmail est executando no topo da pilha, e a activity do
Browser est parada em segundo plano (pause).
Sempre que uma activity est pausada o sistema operacional pode decidir encerrar
o processo, para, por exemplo, liberar recursos e memria para outras aplicaes.
Por exemplo, digamos que o usurio estava jogando e de repente decidiu navegar
na internet e, para isso, ele parou o jogo. Isso faz com que o Android insira no
topo da pilha a aplicao do browser, e deixe em segundo plano 0 jogo que est
temporariamente parado. Agora, digamos que o usurio esquea-se de voltar ao
jogo para continu-lo, e, por algum motivo, o sistema operacional precisa liberar
recursos e memria. Sendo assim, como o jogo no est mais em uso, o sistema
operacional pode decidir encerrar seu processo. Nesse momento sua aplicao
pode decidir salvar algumas informaes ou no.

Nota: cada aplicao nativa do Android, como o browser, a tela inicial (Home),
a agenda e a prpria tela para discar para um nmero de telefone denida
por uma Activity. Portanto, esse conceito de pilha de atividades utilizado para
todas as aplicaes, sejam as desenvolvidas por voc ou uma aplicao nativa,
pois tudo funciona sobre a mesma arquitetura.
Captulo 4 I Activity 101
Agora partiremos para a prtica. Existem mtodos da classe Activity que podem ser utili
zados para controlar o estado da aplicao. Dentre eles j vimos o mtodo onCreate(bund1e)
que responsvel por inicializar a activity e dar incio ao seu ciclo de vida. Agora vamos
estudar outros mtodos importantes: onCreate(bund1e), onStart(), onRestart(), onResume(),
onPause(), onStop() e onDestroy().A gura 4.1 demonstra o ciclo de vida completo de uma
activity exibindo os possveis estados e a chamada de cada mtodo.
Fonte:

http://developer android.com/training/basics/activitylifecycle/starting.html

Resumzed H _
=.._\\ j (vision) |
onFiesume() onPause()
onResume0 \
____,,,; ':' O inli O Started O .z/O Paused
onStart()
<..\_ (vtibiei _`l (prauy
ontartii onStop() L
visible) 1

Created ~nHesta.rt()---if jp .gwppedunp A gli----


0nCfT3te(l xy- JM \. ,.~. ' /3 Ongetfyll
nmwyea

Figura"4.1 - Ciclo de vida de uma activity.

Depois de ver o diagrarrfa da figura 4.1, leia atentamente o signicado de cada

Mtodo Descrio
mtodo do ciclo de vida da activity

onCreate(bund1e) O mtodo onCreate(bund1e) obrigatrio e chamado uma nica vez.


O objetivo desse mtodo fazer a inicializao necessria para exe
cutar a aplicao. Nele deve-se criar uma view e chamar o mtodo
setContentVew(view) para configurar o layout da tela.
onStart() O mtodo onStart() chamado quando a activity est cando visvel
ao usurio e j tem uma view Pode ser chamado depois dos mtodos
onCreate() ou onRestart(), dependendo do estado da aplicao.
onRestart() O mtodo onRestart() chamado quando uma activity foi parada
temporariamente e est sendo iniciada outra vez.
onResume() O mtodo onResume() chamado quando a activity est no topo da
pilha activity stacl' e dessa forma j est executando como a activity
principal e interagindo com o usurio. Podemos dizer que o mtodo
onResume() representa o estado de que a activity est executando.
importante voc entender que quando o mtodo onResume() executar
102 Google Android - 4 edio
Mtodo Descrio (cont.) _, g
o usurio j est vendo a tela. Por isso esse mtodo frequentemente
utilizado para disparar threads que consultam os dados em web
services ou banco de dados para atualizar as informaoes da tela.
onPause() Sempre que a tela da activity fechar, o mtodo onPause() ser chama
do. Isso pode acontecer se o usurio pressionar o boto Home ou
o boto voltar para sair da aplicao. Ou tambm pode aC0ntCr
se voc receber uma ligao telefnica. Nesse momento o mtodo
onPause() chamado para salvar o estado da aplicao, para que
posteriormente, quando a activity voltar a executar, tudo possa ser
recuperado, se necessrio, no mtodo onResume().
onStoD() O mtodo onStop(~) chamado logo depois do mtodo onPause() e
indica que a activity est sendo encerrada, e no est mais visvel ao
usurio. Depois de parada, a activity pode ser reiniciada se necessrio.
Caso isso ocorra, o mtodo onRestart() chamado para reiniciar a
activity Caso a activity que muito tempo parada em segundo plano
e o sistema operacional do Android precise limpar os recursos para
liberar memria, o mtodo onDestroy() pode ser automaticamente
chamado para remover completamente a activity da pilha.
onDestroy() O mtodo onDestroy() literalmente encerra a execuo de uma
activity O mtodo onDestroy() pode ser chamado automaticamente
pelo sistema operacional para liberar recursos ou pode ser chamado
pela aplicao pelo mtodo nish() da classe Activity. Depois de o
mtodo onDestroy() ter executado, a activity removida completa
mente da pilha e seu processo no sistema operacional tambm
completamente encerrado.
Para demonstrar as chamadas dos mtodos do ciclo de vida da activity vamos criar
a classe DebugActivity, que sobrescreve cada mtodo e insere um log do LogCat.
Para este prximo exemplo vamos criar o projeto HelloActivity, mas se preferir
abra o projeto pronto dos exemplos do livro. O cdigo-fonte da classe DebugActivity
pode ser visualizado a seguir:

DebugActivity.java

// Activity que imprime logs nos mtodos de ciclo de vida


public class DebugActivity extends Activity {
protected static nal String TAG = "livro";
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
Log.i(TAG, getClassName() + ".onCreate() chamado: " + icicle);
}
Captulo 4 I Activity 103
protected void onStart() {
super.onStart();
Log.i(TAG, getC1assNane() + ".onStart() chamado.");
}

protected void onRestart() {


super.onRestart();
Log.i(TAG, getC1assNane() + ".onRestart() chanado.");
}

protected void onResune() {


super.onResune();
Log.i(TAG, getC1assName() + ".onResume() chanado.");
}

protected void onPause() {


super.onPause();
Log.i(TAG, getClassName() + ".onPause() chamado.");
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.i(TAG, getC1assNane() + ".onSaveInstanceState() chanado.");
}

protected void onStop() {


super.onStop();
Log.i(TAG, getC1assName() + ".onStop() chanado.");
}

protected void onDestroy() {


super.onDestroy();
Log.i(TAG, getCiassNane() + ".onDestroy() chanado.");
}

private String getC1assName() {


// Retorna o none da classe sen o pacote
String s = getC1ass().getNane();
return s.substring(s.lastIndex0f("."));
}

Nota: sempre que sobrescrever um mtodo da classe Activity, chame o mtodo


da classe me com o super, caso contrrio uma exceo ser lanada.

Essa classe imprime um log quando cada mtodo do ciclo de vida for chamado.
O log criado com a tag livro, portanto necessrio criar um ltro para essa tag
na janela do LogCat, conforme explicado no captulo 3. O prximo passo alterar
104 Google Android - 4 edio
a classe MainActvty para ser lha de DebugActivty, assim ela vai herdar todos os
mtodos que foram customizados na sua classe me.

i MainActivity.java

public class MainActivity extends DebugActivity {


// Mesmo cdigo aqui
}

Ao executar o projeto no emulador, no estamos interessados no layout da activity


ento desta vez vamos analisar somente os logs que mostram as chamadas para
cada mtodo do ciclo de vida. Na primeira vez que a aplicao executar, os se
guintes logs so gerados. Observe que os mtodos onCreate( ), onStart() e onResume()
so chamados na sequncia.
INFO/ID(8494): ManActvty.onCreate() chamado.
INFO/ID(8494): ManActivity.onStart() chamado.
INFO/ID(8494): ManActvity.onResume() chamado.

Agora clique no boto voltar (back) do emulador para sair da activity Isso pode
ser feito tambm pressionando a tecla Est. Observe que agora os mtodos onPause(),
onStop() e onDestroy() foram chamados para encerrar o ciclo de vida da activity
INFO/ID(8494): MainActivity.onPause() chamado.
INFO/ID(8494): MainActivity.onStop() chamado.
INFO/ID(8494): HanActivity.onDestroy() chamado.

Nesse caso, como o boto voltar foi pressionado, o sistema operacional sabe que
a activity precisa ser destruda. Por isso, o mtodo onDestroy() foi chamado, elimi
nando completamente a activity da memria.
Agora volte na tela inicial do emulador e entre novamente na aplicao. Isso vai
chamar os mtodos onCreate(), onStart() e onResume(). Ento, clique no boto Home
para voltar tela inicial. Observe que nos logs os mtodos onPause() e onStop()
foram chamados, mas no o mtodo onDestroy().
INFO/ID(8670): ManActvity.onPause() chamado.
INFO/ID(8670): ManActivty.onStop() chamado.

lsso acontece porque a activity no foi completamente destruda, apenas retirada


do topo da pilha, e agora est parada em segundo plano.
Por ltimo, volte tela inicial Home e clique n cone da aplicao novamente. lsso
vai reiniciar a activity fazendo com que ela volte ao topo da pilha para ser executada
em primeiro plano. Assim, s mtdos onRestart(), onStart() e onResume() so chamados.
Captulo 4 I Activity 105
INFO/ID(10550): ManActvty.onRestart() chamado.
INFO/ID(10550): MainActvity.onStart() chamado.
INFO/ID(10550): MainActvty.onResume() chamado.

Observe que o mtodo onCreate(bundle) chamado uma nica vez. Se a activity


estiver parada em segundo plano, o mtodo onCreate(bundle) no chamado nova
mente. j o mtodo onResume() chamado sempre que a tela car visvel ao usurio.
Perceba que os mtodos onPause() e onStop() so sempre chamados ao sair da tela.
O mtodo onResume() chamado sempre que iniciar ou voltar para a aplicao.
O mtodo onCreate() chamado apenas na primeira vez que a aplicao criada.
Caso a activity seja totalmente destruda, evento que acontece ao clicar no boto
voltar, o mtodo onDestroy() chamado para eliminar os recursos e encerrar o ciclo
de vida da activity Outra forma de destruir a activity chamar o mtodo nsh()
programaticamente no cdigo.
Entender o funcionamento do ciclo de vida de uma activity muito importante
para desenvolver aplicaes no Android. Temos de estar preparados para aes
externas que podem ocorrer, tal como uma ligao telefnica para o usurio. Essa
ao faz com que o sistema operacional do Android interrompa (pause/stop) a
activity atual, colocando-a em segundo plano. Isso feito porque a aplicao na
tiva da ligao quem vai ocupar o topo da pilha de atividades. Numa situao
como essa, quando a ligao terminar, temos de voltar e executar a aplicao de
onde ela parou sem perder nenhuma informao.

Nota: entender o ciclo de vida de uma activity fundamental para dominar o


desenvolvimento para Android. isso que diferencia um bom desenvolvedor
de um desenvolvedor comum.

4.4 Ciclo de vida avanado - oque acontece ao rotacionar o celular?


Ao girar a tela do celular da vertical para a horizontal importante voc saber que
o Android vai destruir a activity atual e vai recri-la logo em seguida. O Android
faz isso porque ele precisa recriar todas as views e aplicar espaamentos e margens
adequadas para a nova orientao.
Durante esse processo de troca de orientao, o Android vai chamar o mtodo
onSaveInstanceState(bundle) na classe da activity Esse mtodo recebe um objeto do
tipo androd.os.Bund1e como argumento que deve ser utilizado para armazenar os
106 Google Android - 4' edio
dados cnt uma estrutura de chave e valor. Para demonstraresse comportainent.
vatnos executar a aplicacao novamente. Ao fazer isso, a MainActlvi.ty e criada t:
inserida tio ltipti da pillta, conforme inostratn estes logs:
MainAct'tvity.onCreate() chamado: null
MalnActlvlty.onStart() chamado.
MatnActtvlty.onResume() chamado.

Agora pressione a tecla de atalho (trI+F11 do emulador para girar a tela para a
l1()l'l2t)Ill`ill.()S logs a segttir mostrain que a activity foi destruda e recriada, mas
durante esse processo o metodo onSaveInstanceState(bundle) foi chamado.
Ma'tnActlvtty.onPause() chamado.
MatnActtvtty.onSaveInstanceState() chamado. // Salve o estado aqui
HalnActtvlty.onSt0D() chamado.
MatnActivity.onDestroy() chamado. // Actlvty foi destruda
MalnActivlty.onCreate() chamado: Bundle[{...}]}] // Actlvlty foi recrlada. Recupere o
// estado aqui
MainActivlty.onStart() chamado.
MatnActivtty.onResume() chamado. // Acttvtty est visivel para o usurio
Se voc salvou valores no Bundle l no mtodo onSaveInstanceState(bundle) possvel
recuperar este estado no Bundle que voc recebe como parmetro no mtodo
onCreate(bundle). para isso que serve esse Bundle no mtodo onCreate(bundle). Se
for a primeira vez que a activity executada, esse Bundle sempre estar nulo.
Entretanto, no caso da rotao da tela em que o Android faz esse processo para
recriar a activity, o Bundle pode estar preenchido cont os dados salvos no mtodo
onSaveInstanceState(bundle)_

Nota: o comportamento padro do /\ndroid ao girar a tela destruir a activity


atual e recriar outra. sua responsabilidacle manter o estado da aplicacao salvando
os dados no Bundle durante a execuo do mtodo onSaveInstanceState(bundle).
para depois recuperar o estado no mtodo onCreate(bundle).

Essa teoria inicial para voc j ficar esperto, mas no captulo 8. sobre fragments.
vamos revisar esse conceito com exerccios praticos.

4.5 Navegao entre telas e inicializao de uma nova activity


(ieralniente utn aplicativo e composto de varias telas. entao precisainos aprender
como fazer a I1`lV(.}.!,Iflt) entre as telas.
Captulo 4 u Activity 107
Existem dois mtodos que podem ser utilizados para iniciar outra activity/ tela:
startActvity(ntent) e startActvtyForResu1t(intent,codgo). A diferena entre eles
que o mtodo startActvty(ntent) apenas inicia a prxima activity sem qualquer
vnculo. O mtodo startActvtyForResult(ntent,codigo) recebe um parmetro que
identica essa chamada, para que posteriormente essa segunda activity possa re
tornar alguma informao para a activity que a chamou. Esse mtodo utilizado
caso a activity inicial que fez a chamada esteja interessada em obter o retorno
quando a segunda activity terminar.
Esse retorno de uma activity para outra pode ser utilizado, por exemplo, em uma
aplicao que exibe uma tela para escolher um contato do celular e, logo depois
de escolher o contato, a informao retornada para a activity inicial, para, por
exemplo, enviar uma mensagem ou email para o contato selecionado.
Os mtodos startActvty(ntent) e startActvtyForResult(codigo,resu1tado,ntent) re
cebem um objeto do tipo androd.content.Intent como parmetro. A classe androd.
content.Intent o corao do Android, tudo gira em torno dela, e uma explicao
detalhada ser fornecida no captulo 20.
Ento mos obra! Crie uma nova classe camada BemVi.ndoActvty, que tambm
deve herdar de DebugActvty, assim podemos monitorar as chamadas dos mtodos
do ciclo de vida. Para criar uma activity preciso fazer trs coisas:
1. Criar a classe Java que estende de alguma subclasse de android.app.Actvty.
2. Criar o arquivo XML de layout.
3. Registrar a activity no arquivo AndroidManifest.xml.
Para facilitar esse trabalho, recomendo voc clicar com o boto direito no pacote
das classes do projeto e selecionar o wizard New > Activity> Blank Activity (Figura 42).
Esse wizard vai auxili-lo a executar estes trs passos.

.~ Cuz
ff; _ . XCtri+
l Rle .C Java Class

gem, mc fil Package


Cpv warn crz1s1+ c *W ""9 ^
' Copy Reference Ctrl* \l+ Shifh C ' Alm- V um
z Bam ,,,.,\, zig. Anmrvdwcy
i Find Qsages lt+F? iai Fade' Shu* Activity _
Find in Bath... Ctrh Shi?t+F ii' F'a9'* " Blank fvty with Fragment

Figura 4.2 - Wizard para criar uma activity.

Depois de criar a activity com o Wizard, digite o seguinte cdigo-fonte:


108 Google Android - 4 edio
n BemVindoActivity.java

import android app.Activity;


import android.os.Bundle;
public class BemVindoActivity extends DebugActivity {
@0verride
protected void onCreate(Bundle savedlnstancetate) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bem_vindo); // Layout desta activity
// Recebe o nome enviado por parmetro
Bundle args = getIntent().getExtras();
String nome = args.getString("nome");
// Vamos atualizar o texto do Textview com uma mensagem de bem-vindo
Textview text = (Textview) ndViewById(R.id.text);
text.setText(nome + ", seja bem-vindo.");
}

No layout da activity vamos adicionar um simples Textview com o id


android:id="@+id/text", para atualizar o texto dinamicamente dentro do cdigo.

/res/layout/actvity_bem_vindo.xmI
<?xml version="1.G" encoding="utf-8"?>
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:padding="10dp" >
TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

classe.xml. _
Nota: o arquivo de layout XML segue a nomenclatura /res/layout/activity_nome_

Lembre-se de que todas as activities precisam ser declaradas no arquivo


AndroidManiest.xml. A vantagem de utilizar o wizard do Android Studio que
ele j fez essa congurao.
Captulo 4 I Activity 109
AndroidManifest.xmI

<application ...>
<activity android:nane=".MainActivity" .. >
. . . // intent-lter da MAIN aqui.

activity android:nane=".BemVindoActivity" android:label="Ben-vindo" /

Nota: todas as activities precisam ser declaradas no arquivo AndroidManifest.xml.


Geralmente somente uma activity ter as conguraes de <intent-lter> MAIN e
LAUNCHER, pois a activity que ca na tela inicial e chamada pelo usurio.

O prximo passo alterar o cdigo da classe MainActivity para navegar para a activity
BenVindoActivity. Na classe MainActivity zemos o exemplo do layout de login e vamos
continuar de onde paramos. Caso o login seja feito com sucesso, vamos navegar para
a prxima tela, a qual vai mostrar uma mensagem de bem-vindo ao usurio. Nesse
exemplo vou passar um parmetro nome=Ricardo xo para a prxima tela.

MainActvty.java

public class MainActivity extends DebugActivity {


@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
Button btLogin = (Button) ndViewById(R.id.btLogin);
btLogin.set0nClickListener(onClickLogin());
}

private View.OnClickListener onClickLogin() {


return new View.OnClickListener() {
@0verride
public void onClick(View v) {
Textview tLogin = (Textview) ndViewById(R.id.tLogin);
Textview tSenha = (Textview) ndViewById(R.id.tSenha);
String login = tLogin.getText().toString();
String senha = tSenha.getTet().toString();
if("ricardo".equals(login) && "123".equals(senha)) {
// Navega para a prxima tela
Intent intent = new Intent( getContext(), BemVindoActivity.class);
no Google Android - 4' edio
Bundle parans = new Bund1e();
parans.putString("none", "Ricardo Lecheta");
intent.putExtras(parans);
startActivity(intent);
} else {
alert("Login e senha incorretos.");
}

};
}

private Context getContext() {


return this;
}
private void a1ert(String s) {
Toast.makeTet(this,s,Toast.LENGTH_SHORT).show();
}

Para navegar para a prxima tela, criado um objeto do tipo android.content.Intent


informando a classe da activity que deve ser chamada. Ao criar a intent, preciso
passar a referncia do contexto, que a classe android.content.Context, que por sua
vez me de android.content.Activity.

Por isso, geralmente comum ver no cdigo-fonte o contexto sendo referenciado com
o this da classe, pois o this no Java representa a instncia do objeto atual. No caso de
o cdigo estar dentro de uma classe Activity do Android, o this representa essa activity
Intent it = new Intent(this, BemVindoActivity.c1ass);
startActivity(it);
Eu muitas vezes gosto de declarar a varivel context conforme demonstrado a
seguir. Neste exemplo simples no tem muita utilidade, mas em exemplos mais
complexos vale a pena.
nal Context context = this;
Intent it = new Intent(contet, BemVindoActivity.c1ass);
startActivity(it); '
Veja que no cdigo do mtodo onC1ick(view) da MainActivity no foi utilizado o
this para passar o contexto. Isso porque o mtodo onC1ick(view) criou uma classe
interna (inner class do java), e nesse caso o this faz referncia classe interna c
no classe MainActivity. Por isso criei o mtodo96tC0f@'C() para retornar o this
e facilitar o cdigo.
111
Captulo 4 n Activity

Ao executar a aplicao e fazer o login, feita a navegao para a tela de bem


-vindo, conforme a figura 43. Como voc j deve saber, para voltar tela anterior
basta utilizar o boto de voltar.

1g Login
` V CapD4-Activity
i . v ' I..i ::/ '

li
i L'._..:r~'..= Ricardo Le|;li;=u, aaja bcrri vinda
i ricardo
ii
1 :`~._1.':i

7 ..-_ ___ _ ____-___, ____] I

Figura 4.3 - Navegao de telas com a activity.

Agora vamos voltar a falar do ciclo de vida da activity Ao abrir a segunda activity
ela inserida no topo da pilha de atividades e a tela do login est parada em
segundo plano. Para conrmar a teoria, vamos verificar os logs na janela LogCat.
Ao abrir a primeira activity podemos vericar nos logs que os mtodos onCreate(),
onStart() e onResume() foram chamados normalmente como esperado.
INFO/ID(11033): MainActivity.onCreate() chamado.
INFO/ID(11033): MainActivity.onStart() chamado.
INFO/ID(11033): MainActivity.onResume() chamado.

At agora no h novidades. Mas ao fazer o login os seguintes logs aparecem no LogCat:


INFO/ID(11033): MainActivity.onPause() chamado. // Primeira activity interrompida (pause)
INFO/ID(11033) BemVindoActivity.onCreate() chamado // Segunda activity iniciada
INFO/ID(11033). BemVindoActivity.onStart() chamado.
INFO/ID(11033): BemVindoActivity.onResume() chamado. // Segunda activity colocada no
// topo da pilha
INFO/ID(11933): MainActivity.onStop() chamado. // Primeira activity parada (stop)

Podemos vericar que a activity MainActivity teve seus mtodos onPause() e onStop()
chamados para deixar a primeira tela em segundo plano. j a activity BemVindoActivity
teve os seus mtodos de inicializao chamados e agora ocupa o topo da pilha.
112 Google Android - 4 edio
Entretanto, o que acontece se 0 boto voltar for pressionado? O sistema operacional
identificar que a activity BemVindoActivity deve ser destruda, porque teoricamente
ela no mais necessria. Isso far com que a activity MainActivity seja relniciada,
voltando ao topo da pilha. Esse fluxo pode ser vericado pelos seguintes logs:
INFO/ID(11033]: BemVindoActivity.onPause() chamado. // A segunda activity Dfd
INFO/ID(11G33] HainActivity.onRestart() chamado. // Reinicia a primeira activity
INFO/ID(11G33 HainActivity.onStart() chamado.
INFO/ID(11033 HainActivity.onResume() chamado. // Volta ao topo da pilha
INFO/ID(11033 BemVindoActivity onStop() chamado.
INFO/ID(11033] BemVindoActivity.onDestroy() chamado.// Destri a segunda activity

Observe que os logs demonstram que a activity BemVindoActivity foi destruda, uma
vez que a chamada ao mtodo onDestroy() foi realizada. j a activity MainActivity foi
reiniciada, chamando os mtodos onRestart(), onStart() e onResume(). importante
que voc exercite o conceito de ciclo de vida de uma activity brincando bastante
com os logs do emulador.
Lembre-se de que os eventos extemos, como o usurio atender uma ligao telefnica,
tambm podem parar a activity e deix-la executando em segundo plano. Os mtodos
do cielo de vida devem estar preparados caso isso aconteaj que o exemplo da ligao
foi lembrado, vamos aproveitar e finalizar o assunto com chave de ouro e simular uma
chamada telefnica, para que a activity que est executando seja inserida em segundo
plano enquanto o usurio atende a ligao. Caber a voc executar a activity analisar
os logs e estudar o que ocorre quando o usurio atende uma chamada.
Para simular a chamada telefnica, abra a ferramenta Android Device Monitor no menu
Tools >Android >Android Device Monitor e procure pela janela Emulator Control. Nessa janela digite

3 .es Qi Z ._
um nmero de telefone no campo incoming Number e clique no boto (all (Figura 4.4).

L "`. Y.

Figura 4.4 - Simulando uma ligao,


Captulo 4 I Activity 113
A figura 4.5 mostra o emulador simulando uma ligao telefnica. A foto do Mickey
apareceu nessa gura porque no captulo 32, sobre a agenda de contatos (Content
Provider), eu cadastrei os contatos Mickey Donald e Pateta no meu emulador.

,ap Mickey 4.52 PM


-Q incoming call

>< DISMlSS K, xmswa

Figura 4.5 - Emulador recebendo uma ligao simulada.

Lembre-se: se alguma activity estiver em execuo enquanto a ligao for feita, ela
ser removida do topo da pilha e car parada em segundo plano. Agora quem
ocupa o topo da pilha a prpria aplicao nativa da ligao. Como assim, a pr
pria aplicao da ligao? Isso mesmo, a tela que exibe a ligao, assim como a tela
inicial, o browser, os mapas, a agenda de contatos etc., todas elas so uma activity
s que nativas e j disponveis no Android. Aos poucos voc vai entendendo que
no Android tudo funciona da mesma forma, seja uma aplicao desenvolvida por
voc, seja uma nativa do Android.
Voltando ligao, quando ela terminar e o usurio fechar a tela, a activity que
estava parada em segundo plano ser reiniciada e voltar ao topo da pilha, estando
pronta para interagir com o usurio novamente. Esse comportamento incrvel, j
que o sistema operacional encarrega-se de tudo. Ele coloca a activity em segundo
plano e depois a reinicia quando necessrio, e, claro, todos os mtodos de ciclo
de vida so chamados para o seu controle.
Depois de toda essa teoria no se esquea de brincar um pouco com o emulador:
e muito importante entender o funcionamento do ciclo de vida da classe Activity
114 Google Android - 4' edio
Nota: se existirem muitos processos parados em segundo plano e se as condies
de memria estiverem baixas, o sistema operacional pode decidir encerrar o
processo de uma activity, chamando o mtodo onDestroy(). O fato que 0 sistema
operacional encarrega-se do ciclo de vida da activity. Cabe ao desenv0lvCClOT
apenas implementar os mtodos desse ciclo corretamente.

4.6 Mais detalhes sobre a classe Bundlee como passar parmetros


No exemplo anterior, aprendemos a utilizar 0 mtodo startActvity(ntent) para
navegar entre as telas e vimos que a classe androd.os.Bundle utilizada para passar
parmetros usando a estrutura de chave e valor.
Para revisar o cdigo que fizemos, veja como foi criado um Bundle e nele foi pas
sado o parmetro none=Rcardo. Depois chamado o mtodo ntent.putExtras(bundle)
para passar esses parmetros para a intent:

public class MalnActivity extends DebugActvty {

f("ricardo".equals(login) && "123".equals(senha)) {


Intent intent = new Intent(getContet(),BenVndoActvty.class);
Bundle parans = new Bundle();
parans.putStrng("none", "Ricardo Lecheta");
ntent.putEtras(params);
startActvty(ntent);
}

Nesse exemplo uma string foi adicionada como parmetro, mas a classe Bundle tem
mtodos para diferentes tipos primitivos, como por exemplo: putBoolean, putChar,
putByteArray, putShort, putInt, putlong, putFl_oat, putDouble e vrios outros.

Se quiser voc pode escrever esse cdigo de maneira mais simplificada, pois a classe
Intent tem alguns atalhos para passar parmetros. Nesse caso podemos substituir
o trecho de cdigo anterior por apenas trs linhas. Internamente a classe Intent
vai continuar utilizando 0 Bundle, mas isso ca encapsulado.
public class HanActvity extends DebugActvty {

if("rtcardo".equals(login) && "123".equals(senha)) {


Intent intent = new Intent(getContet(),BenVindoActlvty.class);
Captulo 4 I Activity 115
intent.putEtra("nome", "Ricardo Lecheta");
startActivity(intent);
}

Para ler o parmetro na segunda activity; tambm podemos utilizar um atalho, e


ler diretamente da classe Intent, conforme demonstrado a seguir:
Intent intent = getIntent();
String nome = intent.getStringEtra("nome"); // L o parmetro do Bundle

Ou voc pode recuperar o objeto Bundle completo para ler os parmetros.


Intent intent = getIntent();
Bundle args = intent.getExtras();
String nome = args.getString("nome");

Existem vrios mtodos para cada tipo primitivo, como getIntEtra(chave),


getLongExtra(chave), getBooleanExtra(chave) etc. Agora que voc j conhece a classe
Bundle e os mtodos da classe Intent que encapsulam o acesso, ca a seu critrio
utilizar a forma de enviar e recuperar os parmetros que mais lhe agradar.

4.7 O bsico sobre action bar e como voltar para tela anterior
No ltimo exemplo, mostramos como navegar entre duas activities da aplicao.
Sempre que uma activity chamada, surge a necessidade de voltar tela anterior,
e para isso voc pode utilizar o boto de voltar do Android, ou colocar o indicador
de voltar na action bar.
Para adicionar o indicador de voltar na action bar, basta utilizar o mtodo
getActionBar().setDisplayHomeAsUpEnabled(true), e quando o usurio clicar no boto ele
vai disparar a ao de menu com o identicador android.R.id . home. Para testar a brin
cadeira, altere o cdigo da classe BemVindoActivity conforme demonstrado a seguir:

BemVindoActivity.java

public class BemVindoActivity extends DebugActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bem_vindo);
Bundle args = getIntent().getExtras();
String nome = args.getString("nome);
116 Google Android - 4 edio
Textview text = (Textview) ndViewById(R.id.tet);
tet.setTet(none + ", seja ben-vindo.");
// Adiciona o boto "up navigation"
getActtonBar().setbisp1ayHoneAsUpEnab1ed(true);
}

@0verride
public boolean on0ptionsItenSe1ected(MenuItem item) {
int id = iten.getItemId();
if(d == android.R.id.hone) {
// 0 mtodo nsh() vai encerrar essa activity
nsh();
return true;
}

return super.onOptionsItemSe1ected(tem);
}

Nota: o mtodo getActionBar() retorna o objeto android.app.ActonBar que est


disponvel para o Android 3.0 (API Level 11) ou superior. Se estivssemos
utilizando a biblioteca de compatibilidade, seria utilizado o mtodo
getSupportActionBar(). Posteriormente vamos aprender a manter a compatibilidade
com verses antigas do Android, mas por enquanto no se preocupe com isso.

Desta vez, ao executar o projeto voc ver que na action bar da segunda activity
apareceu a seta que aponta para esquerda (Figura 4.6), indicando que o usurio
pode voltar para a tela anterior. O indicador de voltar na action bar chamado
de up navigation e vai adicionar uma pequena seta para esquerda indicando
que o usurio pode clicar nela para subir na hierarquia de telas.
O evento gerado pelo up navigation como se o usurio tivesse clicado em qual
quer outro boto da action bar. Nesse caso, porm, o identificador do evento a
constante androd.R.d.hone da classe androd.R nativa do Android. Observe que no
cdigo, como temos de voltar manualmente para a primeira activity chamado o
mtodo nsh(), que faz com que a activity atual seja encerrada. Assim, o Android
vai remover a activity da pilha e chamar todos os mtodos do ciclo de vida como
onPause(), onStop(), at o onDestroy().

Antes de continuarmos o assunto, deixe-me explicar algo importante sobre o up


navigation. Segundo as boas prticas de design do Android, existe uma diferena
entre o boto voltar do Android e o up navigation. O boto voltar sempre volta
para a tela anterior e pronto. Mas o up navigation conceitualmente faz a aplicao
subir na hierarquia de telas. Por exemplo, se o celular est parado na tela Home do
Captulo 4 I Activity 117
Android e sua aplicao for chamada por meio de uma noticao (como se che
gasse um email), voc pode decidir como o up navigation deve funcionar. O boto
voltar simplesmente vai voltar para a tela anterior (que nesse exemplo a home),
mas o up navigation pode voltar para uma tela diferente (tela com a lista de emails).
Muitas vezes voc tambm pode ter vrias telas empilhadas: A > B > C > D > E. Na
tela E, se voc pressionar o boto voltar, as telas sero desempilhadas uma a uma
at a tela A. Mas utilizando o up navigation podemos mudar esse comportamento
e fazer a aplicao voltar direto para a tela A. Isso ca a critrio da sua aplicao;
o importante voc entender que o up navigation nem sempre funcionar igual
ao simples boto voltar do Android.

*~-ii r;?; . ,zz . . ,` /~.,/ zk_.VV_,,/

Usuario Ricardo Lechet, seja bem vindo.


ricardo

Senha
` lllliiit

,__..___ _ ___-,_mJ

Figura 4.6 - Boto de up navigation na action ban

Nota: acostume-se com o termo up navigation, o qual se refere seta de voltar que
colocada na parte superior esquerda da action bar. Outro detalhe importante:
para encerrar uma activity por programao utilize o mtodo finsh(). Ao fazer isso,
o mtodo onDestroy() da activity ser chamado para encerrar o seu ciclo de vida.

4.8 Links teis


Para complementar sua leitura, segue o link da documentao oficial:
Android API Guides - Activities

http://developer android.com/guide/components/activities. html


xi CAPITULO S
Action Bar e temas
O

liste capitulo e sobre o padro de design mais importante do Android, que e a


action bar, ou seja, a barra de navegao que fica na parte superior da aplicao,
a qual contem os botes corn aes, tabs e menus para interagir com o usuario.
Aprender a utilizar a action bar e seguir as recomendaes de design d Android
e um diferencial importante para ter um aplicativo de sucesso.

5.1 Introduo Action Bar


A action bar mostra de forma consistente para o usuario as possveis aes que
ele pode fazer no aplicativo. A grande vantagem de utilizar a action bar que os

'i "A
usurios de Android j esto acostumados com ela, pois todos os aplicativos
nativos so feitos assim.
Para entender a action bar, vamos estudar a figura 5.1.

ai. i ii narB I ,,:


tar Acto
Figura 5.1 - Action ban

1. App Icon

Por padro o cone da action bar mostra o cone do projeto e pode ser
customizado con forme sua necessidade. Nesse espao tambm mostrado
o up navigation que a seta para esquerda que indica que o usuario
pode navegar para cima na hierarquia de telas.

118
Captulo 5 I Action Bar e temas 119
Esse cone atualmente me deixou um pouco intrigado, e me fez pensar
um pouco antes de escrever as prximas frases. Desde que a action bar
foi criada, a documentao ocial refora o conceito de que esse cone
representa algo importante, como o logo da aplicao. Porm, com o sur
gimento do Material Design, a action bar nos dispositivos com Android 5.0
ou superior no mostra o cone, diferentemente de como era no Android
3.x e 4.x. No Material Design dado um foco maior na cor principal da
aplicao (primary color) versus a cor de acentuao (accent color) para
dar destaque s views e a alguns componentes. No Material Design uma
boa prtica customizar as cores da action bar para se identificar com a
marca do cliente ou aplicativo. Tambm recomendado utilizar a cor de
acentuao para destacar as principais aes quando necessrio.
Por isso, dependendo da verso do Android, voc ver aplicativos que
mostram o cone na action bar ou no.

2. View control

Nesse espao podemos mostrar o ttulo do aplicativo ou da tela em que o


usurio est, ou utilizar algum controle de navegao como o drop-down
e as tabs.

3. Action buttons

Espao dedicado para os botes que representam as aes mais comuns


do seu aplicativo. Caso a quantidade de cones no se encaixe no espao
reservado, automaticamente eles so inseridos no menu do action overow

4. Action overow

Menu flutuante que mostra as aes que no so to frequentes no aplicativo.

5.2 Temas Holo e Material

O segredo para utilizar a action bar est no tema que o aplicativo utiliza, o qual
configurado no arquivo AndroidManiest.xml.
A interface do Android funciona com base nos temas. Desde sua criao os dois
temas clssicos eram o Theme.B1ack e Theme.Lght. O tema Theme.B1ack utiliza o fundo
preto e fonte branca e o tema Theme.Lght o contrrio, utiliza o fundo branco e
a fonte preta.
120 Google Android - 4' edio
lisscs eram os temas ate o Android 2.x, mas a partir do Android 3.0 (Honeycmb)
foi criado o tema Holographic, popularmente conhecido apenas como Holo. O tema
Holo tambm tem suas variaes com fundo preto ou branco, que so os temas
Theme.Holo e Theme.Holo. Light.

j no Android 5 (Lollipop) foi criado o tema Material, que aplica as regras do


l\~1latcrial Design. Segundo o Google esta foi a maior mudana da histria referente
a design na plataforma do Android. O tema Material tambm tem suas variaoes
com fundo preto ou branco, que so os temas Theme.Material e Theme.Material.Light.
Entender o que um tema muito importante para voc ser um bom desenvol
vedor Android, por isso, tentarei explicar desde o bsico, inclusive voltando um
pouco na histria desde as primeiras verses do Android.
A maneira mais fcil de voc entender o que um tema abrir um arquivo de
layout no editor visual. No editor possvel congurar o tipo do dispositivo para
fazer a pr-visualizao, congurar a orientao (vertical ou horizontal), o idioma
caso voc tenha preparado o aplicativo para internacionalizao etc. Uma das
conguraes que podemos fazer no editor escolher o tema para visualizar a
tela. Por padro, o editor utiliza o tema AppTheme, conforme indicado na gura 5.2.
H z.~z.:.__ '
Fa me Q- - I z.;>Y'P~~s~z
WW" . -.ari
' .Y .2.

._ 'E'
L~ z~;;\ 's

., -:z1.L.'
*ef ~i..'

~.;" fz-". fzz

.' \|.z,.'
_ ge 'ef'

JU
z ha..
f .>=.:~.f.i
.: ,.,:_>
, a, rar; ~
*`.1_zrc':
s;c e
''7 ..rr;.zE' `
Dl* 'fz'

Figura 5.2 - Pr-visualizao do layout com o tema padro.

Por enquanto vamos deixar para l este AppTheme, depois voltamos a falar dele. Agora
vamos brincar um pouco com o editor. Para trocar o tema que o editor utiliza, clique
no combo do AppTheme e escolha um dos temas Classic > Black ou Classic Light Light.
Esses so os temas utilizados at o Android 2.x (Figura 53). Observe que nesse tema
no existe a action bar, pois antigamente na barra superior era mostrado apenas
um pequeno ttulo, que inclusive muitas aplicaes deixavam oculto.
.
Captulo 5 I Action Bar e temas 121

I
f* MW-mfm' * <ff~*f.f~==~I .aa, .r
2E C:zll@~u=~

J,i \..
-\*fa*ul-.`

l Cap-Activity
. senna
l

Li, ___ __
I
QQ2El_,-
BI=

2 1;
' '
* 'im' s'_
5 W

*afif,
. .-~-
i ll-~.,

:al .~~
l -.I
ll H:p_.~_
wuu-

Stnha
fzi i at ,Q5
Gium - 9- ''21'

Figura 5.3 - Pr-visualizao do layout com os temas do Android 2.x.

Com a chegada do Android 3.0 (API Level 11) e os temas Theme.Ho1o e Theme.Ho1o. Light
a histria mudou, e nasceu a action bar, que representa o padro de design mais

.
importante do Android, pois nela que cam os botes com as principais aes
do aplicativo. Para fazer a pr-visualizao do layout nos temas Holo e HoIo.Lght
basta selecionar a verso do tema desejado no editor (Figura 5.4).

I M . _.
T activity__main.m1 X . _. . A activity maimxml > li V A
I32- =*
tvi, ,=
#25 ,Wu
lg ig; uzuz4~ ' Qoie "H @~ vzi- lg Q; g;Nzus4v - lwqm W- h rivai
El

Figura 5.4 - Pr-visualizao do layout com o tema Holo.

Na pr-visualizao voc vai perceber que o tema Holo padro tem fundo preto
e a fonte tem uma cor azul parecida com a do filme Tron: O Legado. j o tema
Ho1o.Lght tem o fundo branco com fonte preta, e a action bar fica com um fundo
cinza. importante entender que no tema Holo (escuro) os botes da action bar
devem ser brancos. E no terna Ho1o.Lght (claro) os botes da action bar devem ser
escuros, assim a visualizao dos botes da action bar cam coerentes. Cada tema
tem diversas variaes; no entanto, isso voc vai perceber com o tempo. Uma das
,ii1; 1lI.
122 Google Android - 4 edio
\';1ria(es de tema mais utilizadas o Hoio.Light.DarkActionBar, qu d1X21 0 fUnd0
da tela claro igual o Ho1o.Light, mas o fundo da action bar ca escuro, Pfm1fmd0

1 . ._
utilizar botes brancos na action bar.

irI=~
uI | ut ___
Por ltimo voc pode selecionar no editor para visualizar o tema Material Dark e
Material Light, conforme a figura 5.5. Veja que a diferena entre o tema Material
para o Holo e que 0 I-Iolo mostrou 0 cone na action bar.
l , (N`]_fT1I.IYl K 1 0 dmty_man.xml - _
'yflll
'- ,. .il* ';- zz r;
if ' :,& fi . . , ~'' 5'
1 ' Ntxux 4' rs' Q'Mterra| W' ii: A 1- ' Nc\us4- ' ' ' fhf f112*'
* l3&&ii:'ic:.

` Gnplclviy i
E

l
l
I

L...._a.
l

i l

i
r

Figura 5.5 - Pr-visualizao do layout com o tema Material.

Certo, muito bem. Agora vamos voltar a falar daquele AppTheme que vimos no editor
visual. De onde esse nome surgiu? O tema AppTheme denido no arquivo /res/vaIucs/
styles.xml, e 0 editor consegue ler essa configurao e mostra-la para voc. Esse
o tema da aplicao, portanto, nele que vamos fazer as customizaes de cores.
A partir deste momento preste muita ateno, pois estou explicando com base
nos arquivos que foram gerados no wizard na poca em que este livro estava
sendo escrito. Mas 0 wizard frequentemente muda a forma de gerar os arquivos,
portanto o que importa voc entender 0 conceito. A seguir temos 0 arquivo
styles.xml, que foi criado pelo wizard, nele que 0 tema AppTheme foi definido. Esse
arquivo foi congurado para utilizar ou tema Holo. Veja que 0 tema AppTheme herda
de Theme.Ho1o.Light.

/res/values/styIes.xmI

<sty1e name="AppTheme" parent="android:Theme.Ho1o.Light ">


Captulo 5 I Action Bar e temas 123
E no arquivo AndroidManiest.xml o tema AppTheme foi congurado como o tema
do projeto com a notao @styIe/tema. Por isso o editor visual l esse AppTheme, pois
ele o tema do projeto.

AndroidManifest.xmI
<?xmI version="1.0" encoding="utf-8"?>

<appIication android:theme="@styIe/AppTheme" >

Nota: o tema que o aplicativo vai utilizar configurado no AndroidManiest.xml. Por


padro, o tema AppTheme criado automaticamente no arquivo /res/values/styles.xml
e herda de algum tema nativo do Android, seja alguma variao dos temas HoIo
oi1MateriaI.

Veja que o Android Studio tambm criou o arquivo /res/values-v21/styles.xml, que


est configurado para utilizar o tema Material. A pasta /res/values-v21 utilizada
para configurar os recursos para o Android 5.0 (API Level 21) ou superior.

/res/values-v21/styIes.xmI

<styIe name="AppTheme" parent="android:Theme.Materia1.Light ">

No Android, sempre que voc ver uma pasta com um trao e um nmero, como
por exemplo /res/values-1/21, porque esse nmero referente ao identificador da
API Level com o qual essa pasta compatvel. Nesse caso, o Android vai utilizar
por padro o arquivo /res/values/styles.xml, e se o dispositivo tiver o Android 5.0 ou
superior (API Level 21), a congurao feita no arquivo /res/values-v21/styles.xml
utilizada.

Nota: os identicadores numricos nas pastas so chamados de qualicadores


por API Level e denem a verso do Android que vai utilizar esses recursos.
124 Google Android - 4= edio
Para encerrar esta breve explicao sobre temas, lembre-se de que no ar
quivo AndrilMmi_csr.xml o tema da aplicacao definido pelo trlbuw
androd:theme:"@style/nome_do_tema". lsso e feito assim para que o tema possa ser
customizado dependendo da versao do Android.
limbora voc possa customizar o tema dependendo da verso do Android com
os qualicadores por API Level e criar variaes da pasta /res/values, atualmente
isso nao e mais necessario, pois podemos utilizar a biblioteca de compatibilidade
e usar o terna Apptlompat para todas as verses do Android. Vamos primeiro estudar
como utilizar a action bar nativa e depois aprenderemos mais detalhes sobre essas
questes de compatibilidade.

5.3 Projeto de exemplo sobre action bar


Para estudarmos o funcionamento da action bar, abra o projeto HeIIoAt'tionBar no
Android Studio, o qual faz parte do material de download do livro. Para fazer isso.
utilize o menu File Open e selecione a pasta do projeto.
Ao executar esse projeto no emulador, o resultado ser como a figura 5.6, que
mostra os botes de busca e atualizar na action bar, bem como a ao de Settings
no menu action overflow. Saiba que os botes que cam como action buttons
podem ter imagens, mas os botes que ficam no menu action overflow contm
apenas textos.

Settings

z~n'l ,,;'i` iti|.I_`_ '.i

Figura 5.6 - Exemplo de action bar:


Captulo 5 -Action Bar e temas 125
Os arquivos de menu so denidos no arquivo XML /res/menu/menu_main.xml.
Como voc pode ver no cdigo-fonte a seguir, os botes na action bar aparecem
na mesma ordem em que esto denidos no arquivo.

/res/menu/menu_man.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/androd"
m1ns:too1s="http://schemas.android.com/tools" >
<item androd:d="@+d/action_search"
android:icon="@drawable/c_action_search" androd:title:"@string/action_search"
androd:showAsAction="a1ways" />
<tem androd:id="@+d/action_refresh"
androd:icon="@drawable/ic_acton_refresh" android:title:"@string/acton_refresh"
androd:showAsAction="a1ways" />
<item android:id="@+d/acton_settngs"
android:tit1e="@string/action_settngs"
androd:showAsActon="never" />

Ao executar o projeto, os cones de Search e Refresh vo aparecer na action bar,


pois esto configurados com a opo androd:showAsActon="a1ways". j a ao
de Settings ca no menu action overovsg pois est congurada com a opo
androd:showAsActon="never". Ao clicar nos botes, o evento est sendo tratado. A
gura 5.6 mostra o resultado ao clicar no boto de Refresh. Note que cada item de
menu referencia um texto que est cadastrado no arquivo /res/values/stringsxml.

/res/values/strings.xmI
<?m1 version="1.0" encoding="utf-8"?>

<strng name app_name">LvroAndroidCap5-ActonBar</strng>


<strng name he11o_wor1d">He11o wor1d!</strng>
<strng name action_settings">Settings
<strng name action_search">Search</string
<strng name action_refresh">Refresh

Para utilizar esse arquivo XML de menu, a activity deve implementar o metodo
onCreateOptonsMenu(menu) e inar o menu XML. Isso simples conforme mostra
este cdigo:
@Override
public boolean onCreate0ptonsMenu(Menu menu) {
// Ina o menu com os botes da action bar
126 Google Android - 4' edio
getHenuInater().inate(R.menu.menu_main, menu);
return true;

Uma vez que o menu com os botoes da action bar esto configurados, basta imple
mentar o mtodo on0ptionsItemSelected(MenuItem) para tratar os eventos gerados pelos
botoes. Por isso no arquivo XML dc menu foi denido um id para cada ttcm. O
codigo-fonte completo da classe HainActivity do projeto pode ser visualizado a scgutr.

l: ManActivity.java

public class MainActivity extends Activity {


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

@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getHenuInater().inate(R.menu.menu_main, menu);
return true;
}

@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_search) {
toast("Clicou no Search!");
return true;
} else if (id == R.id.action_refresh) {
toast("Clicou no Refresh!");
return true;
} else if (id == R.id.action_settings) {
toast("Clicou no Settings!");
return true;
}

return super.on0ptionsItemSelected(item);
}

private void toast(String msg) {


Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

}
Capitulo S I Action Bar e temas 127
Esse cdigo est mostrando um alerta ao clicar em algum boto da action bar,
portanto execute o projeto no emulador e congura o resultado. Conforme po
demos verificar, adicionar os botes na action bar realmente simples.

Nota: ao congurar um action button no arquivo XML de menu, recomendvel


utilizar um cone para deixar o visual mais interessante. Muitas vezes, somente
de olhar o cone, o usurio j sabe o que aquele boto faz, como so os casos
da lupa de busca e o refresh. No entanto, mesmo que uma gura seja utilizada,
e importante congurar o ttulo, porque se o usurio pressionar o boto por
dois segundos o Android vai mostrar um toast com o ttulo desse boto. Isso
tambm importante por questes de acessibilidade, pois deficientes visuais
utilizam softwares que conseguem ler tudo o que est escrito na tela. O mesmo
conceito aplica-se ao componente Imageview, cujo atributo android:contentdescrpton
fornece o texto explicativo da gura, o qual pode ser lido por estes softwares.

5 4 Opes de visualizao dos action buttons (always, never, ifRoom)


Ao adicionar um action button na action bar, voc deve escolher como ser feita
a visualizao deste, isto , se ele sempre ficar visvel, ou se car disponvel no
menu do action overflow etc. Para isso, voc pode utilizar qualquer uma destas
constantes no atributo app:showAsActon="xxx":

Indica que o boto sempre deve car visvel como action button. re
comendado utilizar essa opo para denir as aes mais comuns do
aplicativo.

Mostra o boto na action bar se existir espao, ou move ele automatica


mente para o menu action overow caso no tenha. Muitas vezes essa a
congurao adequada para manter a compatibilidade com diversos tipos
de dispositivos e tambm com telas na vertical e horizontal.

w1thText

Mostra o ttulo do boto ao lado do cone, caso tenha espao disponvel


na action bar. Por exemplo, na horizontal existe mais espao na action bar,
portanto possvel exibir o ttulo opcionalmente.
128 Google Android - 4 edio
never

Nunca mostra 0 boto na action bar, deixando a ao nO 11161111 8CUOI1


overflow

co11apseActonVew

Esse atributo indica que uma view que geralmente grande deve ser
contrada para exibir apenas o boto. Esse 0 caso do boto de busca do
Android, que ca contrado, mas ao ser clicado se expande para o usurio
digitar o texto.
Com o tempo voc entender melhor cada um desses itens, mas lembre-se de
que geralmente recomendado utilizar as opes fRoom para garantir que se no
houver espao o boto aparea no menu action overow, e a opo never se voc
tiver certeza que a opo deve car no menu action overflovv Tambm possvel
combinar essas opes com o separador, como por exemplo: app:showAsActon="if
Room|wthTet". Agora com voc, brinque um pouco com essas opes e veja os
resultados no emulador.

1
Nota: segundo as boas prticas do Android, os action buttons (botes com
aes) devem ser utilizados para as aes mais comuns da tela, ou seja, as aes
que sero mais utilizadas pelo usurio. j as aes que so menos utilizadas,
como por exemplo Ajuda e Configuraes, devem car no menu action overflow
(menu flutuante).

5.5 Template de cones para os botes da action bar


Ao criar os cones para os botes da action bar, importante seguir o padro de
cores. Por exemplo, o projeto de exemplo est congurado para utilizar o tema
Material conforme demonstrado a seguir:

/res/values-v21/styIes.xmI
<?m1 verson="1.0" encodng="utf-8"?>

<sty1e name="AppThene" parent="androd:Theme.Materal.Lght.DarkActonBar">


</sty1e
Captulo 5 I Action Bar e temas 129
Nesse caso, como foi congurada a action bar com o padro escuro Dark os
botes precisam ser brancos, por isso verique que os cones que coloquei na
pasta /res/drawable so claros (Figura 5.7).
Eres __i,
p tg drawable-mdpi
D*-F' dfawmbte-nidp Q W
ic,,action_refr:sh.png
ic__action_s:zarch.png i
D layout
menu
i va|ues I
* ==' v'alues~w820dp _
W AndroidManif:st.xmI 1

Figura 5.7 -cones para os botes da action bar

Nota: os nomes dos cones da action bar seguem a nomenclatura c_acton_none.png.

Voc deve estar se perguntando de onde eu tirei essas guras. Felizmente existe
uma coleo com o template de vrios cones com as aes mais comuns, como:
atualizar, buscar, adicionar, deletar, copiar, colar, compartilhar etc. Para baixar o
pacote de cones da action bar, acesse a pgina da documentao da action bar e
procure pelo link Download the Action Bar Icon Pack.

http://developer android.com/design/patterns/actionbar html


Ao descompactar o arquivo do pacote, voc ver as imagens para o tema Holo
Dark e Holo Lgh. No projeto que criei neste captulo, estou utilizando o tema
Theme.Matera1.Lght.DarkActonBar. Isso signica que a action bar escura e os botes
devem ser brancos. Nesse caso o correto selecionar algum cone criado para o
tema Holo Dark. Eu copiei as guras de Refresh e Search para a pasta /drawable/xxhdpi.
No pacote de cones voc ver que existem as guras para vrias densidades,
mas eu geralmente coloco no projeto apenas as guras para a maior densidade,
e deixo o Android redimensionar as imagens em tempo de execuo, no caso de
o aplicativo executar em telas com menor resoluo.

5.6 Classe android.app.ActionBar


Por padro, o ttulo que aparece na action bar o nome da activity congurado
no atributo androdzlabel da tag <actvty> no arquivo AndroidManiest.xml.
130 Google Android - 4 edio
Esse ttulo pode ser customizado dinamicamente no cdigo, 6 550 P0d ser ne'
cessrio dependendo da lgica da aplicao. A activity p ode chamar o mtodo
getActionBar() para obter o objeto android.app.ActionBar e depois chamar O metodo
actionBar.setTit1e("") para customizar o ttulo.

MainActivity.java
import android.app.Activity;
import android.app.ActionBar;
public class MainActivity extends Activity {`
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ActionBar actionBar = getActionBar();
actionBar.setTit1e("Captu1o S");
}

Ao executar esse exemplo no emulador, voc ver que o ttulo da action bar ser
Captulo 5' Existem vrios mtodos na classe android.app.ActionBar e a lista a
seguir contm alguns dos mais importantes.

setCustomView(int ou View)

Permite adicionar uma view customizada na action bar. Um exemplo de


view customizada o boto de busca do Android, que encapsulado na
classe Searchview.

setTit1e(string)
Altera o ttulo da action bar.

setIcon(DrawabIe)

Altera o cone home que por padro mostra o cone do projeto.

setDisp1ayShowTit1eEnab1ed(boolean)

Congura se para exibir o titulo na action bar.

setDispIayShowHomeEnabied(boolean)

Congura se para exibir o logo/cone na action bar, chamado de home


Captulo 5 n Action Bar e temas 131
setDisp1ayHomeAsUpEnabled(boolean)

Exibe a setinha para esquerda up navigation para indicar que o usurio


pode voltar tela anterior ou navegar para cima na hierarquia de telas.
Embora existam muitos outros mtodos na classe android . app.ActionBar, o recomen
dado voc ler a documentao ocial (javadoc) da classe. Alguns dos mtodos
so para criar a navegao por tabs e drop down, ento vamos estud-los depois.

5.7 SearchView

A action bar permite adicionar views customizadas, sendo que um exemplo cls
sico o Searchview. Para este prximo exerccio, vamos utilizar o Searchview; assim,
quando o usurio tocar no boto de busca, o campo de texto ser expandido
para o usurio digitar o texto da busca. Felizmente o Searchview j faz todo esse
trabalho e para utiliz-lo basta congurar a tag android:actionViewCiass conforme
demonstrado a seguir.

/res/menu/menu_main.xmI
<menu xmins:android="http://schemas.android.com/apk/res/android"
xm1ns:toois="http://schemas.android.com/tools" >
<item android:id="@+id/action_search"
android:icon:"@drawabie/ic_action_search" android:title:"@string/action_search"
android:showAsAction="a1ways"
android:actionViewC1ass="android.widget.SearchView" />
// . . . Outros botes de refresh e settings aqui

Nota: o Searchview uma view customizada que pode ser inserida na action bar,
a qual chamada de Action View. Um Action View tem por objetivo substituir o
boto da action bar por alguma view customizada.

No cdigo da activity; basta obter -o -objeto do S=ea'rchView e configurar para a apli


cao tratar o evento Searchview.0nQueryTextListener. Note que o cdigo a seguir
est resumido, pois estou mostrando apenas o que precisa ser alterado referente
ao Searchview.
132 Google Android - 4 edio
Qu ManActivity.java

import android.widget.Searchview;
public class MainActivity extends Activity {

@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getMenuInater().inate(R.menu.menu_main, menu);
Henulten item = menu.ndItem(R.id.action_search);
Searchview searchview = (Searchview) item.getActionView();
searchview.set0nQueryTextListener(onSearch());
return true;
}

private Searchview.0nQueryTetListener onSearch() {


return new Searchview.0nQueryTetListener(){
@Override
public boolean onQueryTextSubmit(String query) {
// Usurio fez a busca
toast("Buscar o texto: " + query);
return false;
}

@Override
public boolean onQueryTetChange(String newText) {
// Mudou o texto digitado
return false;
}

};
}

Ao executar o projeto e clicar o cone de busca, o Searchview vai expandir para o


usurio digitar a busca. A gura 5.8 mostra o resultado em que eu digitei o texto
Livro Android e pressionei no emulador para fazer a busca.
Captulo 5 n Action Bar e temas 133

Helio 'NOYC' Helio world!

Buscar o texto: Livro Android

Figura 5.8 - Exemplo de SearchView

5.8 Action provider


De forma similar ao action vievsg um action provider tambm substitui algum
boto por um layout customizado. C* action provider mais famoso de todos o
de compartilhar. Por exemplo, na galeria de fotos, ao compartilhar uma foto, o
Android mostra automaticamente todos os aplicativos que podem enviar a foto,
como por exemplo: Gmail ou Hangouts.
Para configurar um action provider em algum boto da action bar, basta adicionar a tag
androd : actonProvderC1ass e informar alguma subclasse de androd .vew.ActonProvder.
Neste exemplo vamos utilizar a classe androd.widget.ShareActonProvder que facilita
o trabalho de compartilhar algum contedo.

/res/menu/menu_main.xmI
<menu xmlns:androd="http://schemas.androd.com/apk/res/androd"
xmlns:tools="http://schemas.androd.com/tools" >
<tem androd:d="@+id/acton_search"
androd:icon:"@drawable/c_acton_search" androd:title:"@string/acton_search"
android:showAsAction="always" androd:actonVewC1ass="androd.wdget.SearchVew" />
ten androd:d="@+d/action_share"
androd:con="@drawable/c_acton_share" androd:tt1e="@strng/acton_share"
android:showAsActon="a1ways"
androd:actionProvderC1ass="android.widget.ShareActonProvder" /
<tem
134 Google Android - 4 edio
android:id="@+id/action_refresh" android:icon="@drawable/ic_action_refresh"
android:title="@string/action_refresh"
android:showAsAction="ifRoom" />
<item
android:id="@+id/action_settings" android:title:"@string/action_settings"
android:showAsAction="never" />

Nota: como desta vez existem trs botes na action bar, congurei os botes de
busca e compartilhar como showAsAction="always" para carem visveis, mas o boto
de refresh deixei como showAsAction="ifRoom"; assim, o Android vai decidir se o boto
vai car como action button ou se vai ser mostrado no menu action overflow.

Depois de congurar o item de menu da action bar com o ShareActionProvider, basta


customizar a Intent que ser utilizada para disparar a mensagem ao sistema opera
cional, a m de compartilhar o contedo Vimos no captulo 4, sobre a classe Activity,
que uma Intent pode ser utilizada para navegar entre as telas da aplicao, mas na
verdade uma intent representa uma mensagem que enviada ao sistema operacional
para os mais variados ns. No caso do compartilhamento, quando a intent enviada,
o sistema operacional vai perguntar para todas as aplicaes instaladas quem que
consegue tratar essa mensagem para compartilhar o contedo O cdigo-fonte a seguir
est resumido e mostra apenas o necessrio para implementar o ShareActionProvider.

MainActivity.java

import android.uidget.ShareActionProvider;
public class MainActivity extends Activity {

@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar

// Searchview `
getHenuInater().inate(R.menu.menu_main, menu);

Menultem searchltem = menu.ndItem(R.id.action_search);


Searchview searchview = (Searchview) searchltem.getActionView();
searchview.set0nQueryTetListener(onSearch());

// ShareActionProvider
Henelten shareltem = menu.ndItem(R.id.action_share);
SharectionProvider share = (ShareActionProvider) shareltem.getActionProvider();
share.setShareIntent(getDefaultIntent());
return true;
Captulo 5 I Action Bar e temas 135
}

// Intent que dene o contedo que ser compartilhado


private Intent getDefau1tIntent() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/*");
intent.putExtra(Intent.EXTRA_TEXT, "Texto para compartilhar");
return intent;
}

Ao executar a aplicao no emulador, o resultado deve ser como a gura 5.9. O

l
1w l
ShareActionProvder mostra todos os aplicativos que conseguem tratar a mensagem
enviada pela intent de compartilhamento, porm no simulador somente o aplicativo
de mensagem est instalado. Em um dispositivo real, comum encontrar vrios

z4 1
l
1I EKi. i1..'
aplicativos ao clicar no cone de compartilhar. Nesse exemplo vimos a configurao

y
.
!
I
E
' 1
= l l
de uma intent bsica para compartilhar textos. Futuramente, durante o desenvol
vimento do aplicativo para carros, veremos como compartilhar a foto de um carro.

Heilo world! ,TO I p ,


5i

1i
5

.
zTexto
I
1 ir , . _ . , _ .
para compartilhar

Figura 5.9 - Exemplo de ShareActionPr01/ider

5.9 Split action bar


Dependendo da aplicao, voc pode precisar de mais espao para exibir os
botes na action bar, e uma opo utilizar o split action bar, que vai deixar os
botes na parte inferior da tela. Para habilitar a split action bar, edite o arquivo

a tag na configurao da tag . _


AndroidMcmifest.xml e insira o atributo android:ui0ptions="splitActionBarwhenNarrow" e
136 Google Android - 4 edio
AndroidManifest.xmI
<3m1 version="1.0" encoding="utf-8"?>
manifest ... >
application ... >
attivity android:name="br.livroandroid.cap5.actionbar.MainActivity"
android:1abe1="@string/app_name"
android:ui0ptions="sp1tActionBarHhenNarrow">
<neta-data android:nane="android.support.UI_0PTIONS"
android:va1ue="sp1itActonBarHhenNarrow" />
<ntent-1ter>...</intent-1ter>
</actvity>
</app1caton>
</manfest>

Feito isso. ao executar o projeto o resultado deve ser como a figura 5..lO. Na ver
dade, a split action bar economiza espao se necessrio; caso o dispositivo esteja
na horizontal, como existe espaco suciente, os botes da action bar cam na
parte superior normalmente.

* 'i L `*f. Oi kl

Figura 5.10 - Split action ban

Nota: a configurao da split action bar deve ser feita activitv por activity e no
e possivel alterar pelo codigo, somente no arquivo Ai1ridMziiij~st.xml. Essa
conguraao parece que toi descontinuada (deprecated) no Material Design
pois no Android 5.0 isso no funciona mais. O print que voc est vendo e do
Android 4.4 com o tema Holo.
Captulo 5 n ction Bar e temas 137
5.10 Up navigation
No captulo 4, sobre activity criamos um aplicativo com uma tela de login com
a mensagem de bem-vindo, a qual foi mostrada em outra tela. Para voltar tela
anterior, podemos utilizar o boto voltar nativo do Android, ou o up navigation,
que a seta que aponta para a esquerda e ca na action bar.
Como j explicado anteriormente, o up navigation utilizado para subir na
hierarquia de telas; seu funcionamento muitas vezes parecido com o boto
voltar, mas, dependendo do caso, pode ser diferente. Vimos que para mostrar
a seta do up navigation na action bar basta chamar o mtodo getActionBar().
setDisp1ayHoneAsUpEnab1ed(true) e depois o mtodo on0ptionsItenSe1ected(Menulten)
chamado; o identicador android . R.id . home deve ser utilizado para identicar a ao.
@0verride
public boolean onMenuItenSe1ected(int featureld, Menulten item) {
// Clicou no "up navigation"
if(iten.getItenId() == android.R.id.hone) {
// O mtodo nish() vai encerrar essa activity
nish();
return true;
}

return super.onMenuItenSe1ected(featureld, iten);


}

Se no quiser voc no precisa implementar o mtodo onOptionsItenSe1ected(Menulten),


pois possvel congurar o AndroidManiest.xml para que o comportamento do up
navigation da activity siga o padro, que voltar a tela anterior. Para isso existe
o atributo android:parentActivityNane, que permite congurar a activity me na hie
rarquia de telas, assim o Android sabe para qual tela precisa voltar. Como esse
atributo foi criado no Android 4.0 (API Level 14), preciso congurar tambm a
tag neta-data para suportar as verses com Android 2.1 (API Level 7) ou superior.
Ento faa o teste! Altere o cdigo do arquivo AndroidManifest.xml do projeto que
fizemos com a tela de login, e comente o mtodo onOptionsItenSe1ected(Menulten) na
classe BenVindoActivity, pois ele no ser mais necessrio. Ao executar o projeto no
emulador, o up navigation vai funcionar normalmente.

AndroidManfest.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<nanifest ... >
<app1ication ... >
<activity android:nane=".MainActivity" android:1abe1="@string/tit1e_nain_activity" >
133 Google Android - 4 edio
</activity
<activity android:nane=".BenVindoActivity" android:parentActivityName="-HBHCVY" >
<!-~ Compatibilidade com a API levei 7+ -->
neta-data android:nane="android.support.PARENT_ACTIVITY"
android:va1ue=".HainActivity" /

/appiication

5.11 Navegao por tabs na action bar


A navegao por tabs na action bar permite ao aplicativo mostrar as principais
sees que o usurio pode navegar e considerada o nvel (top-level) de navegao
do seu aplicativo. As tabs devem ser utilizadas no caso de existirem poucas sees
para navegar no aplicativo, sendo que o principal objetivo mostrar ao usurio
as sees que o aplicativo contm de forma simples e rpida.
A navegao por tabs utilizada em aplicativos como o Google Play Para aplicati
vos que temham muitas sees de nvel (top-level), um padro de design que est
sendo muito utilizado o Navigation Drawer (menu lateral utilizado no aplicativo
do Gmail), pois nele o menu lateral pode exibir diversas opes e mostrar uma
rolagem na vertical.
Adicionar as tabs na action bar bem simples, basta utilizar o mtodo
setNavigationMode(ActionBar.NAVIGATION_MODE_TABS) da classe ActionBar e depois criar as tabs
com o mtodo addTab() . Ao clicar numa tab, os mtodos da interface ActionBar . TabListener
so chamados para permitir que o aplicativo realize a ao necessria para trocar o
contedo da tela. Para brincar com as tabs, vamos alterar o projeto He11oActionBar para
criar trs tabs. Ao clicar em cada tab, vamos apenas mostrar um alerta com o toast.

MainActivity.java

public class MainActivity extends Activity {

@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.iayout.activity_main);
ActionBar actionBar = getActionBar();
actionBar.setTit1e("Capitu1o 5");
Captulo 5 I Action Bar e temas

/I Congura a action bar para utilizar as tabs


actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Cria as tabs (Passa como parmetro o indice de cada tab: 1,2,3)
actionBar.addTab(actionBar.newTab().setText("Tab 1")
.setTabListener(new MyTabListener(this,1)));
actionBar.addTab(actionBar.newTab().setText("Tab 2")
.setTabListener(new MyTabListener(this,2)));
actionBar.addTab(actionBar.newTab().setText("Tab 3")
.setTabListener(new MyTabListener(this,3)));
}

Para o cdigo compilar crie a classe MyTabListener, a qual implementa a interface


ActionBar.TabListener e responsvel por tratar os eventos das tabs.

MyTabLstener.java

import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.content.Context;
import android.widget.Toast;
public class MyTabListener implements ActionBar.TabListener {
private Context context;
private int tabIdx;
public MyTabListener(Context context, int tabIdx) {
this.context = context;
this.tabIdx = tabIdx;
}

@0verride
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado ao selecionar uma tab
Toast.makeText(context, "Selecionou a tab: " + tabId, Toast.LENGTH_SHORT).show()
l
@0verride
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado quando a tab perde o foco (se outra tab selecionada)
}

@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// Chamado quando uma tab selecionada novamente.
l
}
HO

\11l`111.1
\

\111
._\
.1.\1`1 11c11.11'1111`111~
`\
1 1 1. _ ~1111 11111 1(\11\1
1 11 11111u.1 11 1c1111.11111 111*c c\c111|1111 111.11 .1 111 1.11 1 1

1I\\
1 _ 1.\1\( 111111111.111.1
\111`1l.1\1\1111111`1`11.11.11\ 1~1cc11111.111.1. N1|11'1111`1 1|111111111 111111 _1\x
1111119111 Androld 41 edi

_ 1 11. ~ . .1\. 111

. 111 . 11 .111
\\\ .11`111.1u\11;1c1.11111c111c.1u1.111:.111111111111c111111111111.1111.1 111.1 l~>1'1 * "* * *
1 1 1 \ `. . ` . 1.
'\
\l11(` \.111111 cs1u11.11 11111 1\1l1111\~111uu1' 1 1111 131 1 1
.~\111*11
1 11111.11 11111.1 1.11\1\ 11111111111111111.1111111111111111 1' 1 11.1111.11111.11 1111.11 11111. ll 111
1

1 1. . 111 111

1l111111\1\111`\`11`\\'1`1\1`1`I.11\11l1`|\1\111`1`1`11\1`1l111\1`l1\111l1l111l11.\111\11 1 1111111111 Il 1
1

c1c1111.11` .11|uc11* c11111c111111.

:z111~1~111111111 11 1311 L'

1~`1`u1.1 1.11 .-1111111111111 111m 11111.1

.1-11.
Ateno: \'11c1* 11c\'c 1c1 |1c11'1~1111111 1`111` c\1s1c111 .11}11l1\ .11c1`1.1s \\\'.\l`l\ll\}S1 1111
1
111 11i1;11 11.15 l.111s. Isso 1\_\1Hl\`\`l` I`\11_\I11\` 1111 .~\11111'11111 1.11 11 111111111 11c 11.1\'1'1;.1c.111

|1111'1.111s 11.1 .111111111 111111111 111~sc1111111111.11111 111c111'c1.111~11\.

l)[`}`\1lS 1111 1111111;1c I 11 .T11l_ 11 11111111111 111|111111111l1:1111 11111.1 1111111111c1`.1 1111


\`\\1I\\\(`1\l\lS 111111.-'.11111s 1111 M.111*1'1.11 I3c11z11 c11.1111.111.1 .-11111111111 111~11gr1 Su1.1r
111 11.111-. 111`l1ll`1` 11s 1111\'11 111111111111c1111-s. 1111 1*1`1.11111 11 111111 _1\-11111_ 1111~ 111111111 11

p1^11111`111 11111111~ 1.1 111: c 111111.'.11111 1.11'.1 1111.11 1.I1\s. \`.111111s c11111.1 111 111.115 1.1111c
1111 111'1c11\'111\^1'l` U 1\l11\`-111\' 11115 1'.11'1'11. 11111111 1111111'.u' 11111111 1'1i.11` 1.111s \.\\111
.1 .11`1I1\11 1\.11`. 1110511111 1|111` 111`|W1`1'1`.111'1_1. 111115 CNIC 11\1~|\\}1|11 1~ 11111 11_\.111 11 \111'1`

|\I`1`1`1.11`1\I1111`1`1\ 111.1\1111111\'1111.1s1.111 1111111.11'11111111.11 1~s1.11'11c11cc.11c11cp111'111c


1111111111111 111111 1'c1"11111c1111.11111111111;'.11'.1 11111111.11'11 .1I\11111\\ l.*\.
Captulo 5 I Action Bar e temas 141
5.12 ActionBar(ompat - a biblioteca de compatibilidade da action bar
No evento Google I/ O 2013, o Google anunciou o suporte a action bar para verses
anteriores do Android, compatvel com o Android 2.1 (API Level 7) ou superior. Essa
biblioteca conhecida como biblioteca de compatibilidade v7 ou ActionBarCompat.
Para utilizar a action bar de compatibilidade, a primeira tarefa a fazer declarar a
dependncia para a biblioteca v7 no arquivo app/buildgradle do mdulo. Lembre
-se de que existe o arquivo build. gradle que ca na raiz do projeto (no esse) e o
arquivo app/buildgradle que fica dentro do mdulo. Voc geralmente vai mexer no
arquivo de configurao do mdulo. Como exerccio, vamos migrar o projeto de
exemplo da action bar deste captulo para utilizar a action bar de compatibilidade,
para deixar o projeto compatvel com o Android 2.1 (API Level 7).
Ento vamos l! Para comear a brincadeira, faa duas alteraes no arquivo
app/build.gradle.

1. Altere o atributo minSdkVersion para 7


2. Adicione a dependncia para a biblioteca appcompat-v7.

app/buiId.gradIe
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultCong {
applicationld "br.com.livroandroid.actionbar"
ninSdkVersion 7 // API Level 7 (Android 2.1)
targetSdkVersion 22
versionCode 1
versionName "1.0"
}

dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
// Dependncia da biblioteca de compatibilidade v7
conpile 'com.android.support:appconpat-v7:22.1.1'
1

No item defaulttong foi denido que a aplicao suporta o Android 2.1 (API Level 7)
como a verso mnima e o target o Android 5.1 (API Level 22). Lembre-se de que
o target deve ser sempre a ltima verso e quando voc estiver lendo este livro
1 42 Google Android - 4 edio
, . - z - ~ uivo dentro do
bem provavel que existam versoes novas. Na ultima linha dO 8fCl
item dependences configurada a seguinte dependencia:
Compile 'com.android.support:appcompat-v7:22.1.1'

Isso indica ao Gradle que a biblioteca de compatibilidade deve ser adicionada


como dependncia do projeto, a qual contm a action bar de compatibilidade.
No meu caso a verso 22.1.1, pois a verso do item Android SuDDf'f Lbffy que
instalei pelo SDK Manager.

Nota: ao adicionar uma nova dependncia no buildgradle clique no boto Sync Ilow
que aparece no editor. Isso faz com que o Gradle baixe a dependncia e a deixe
ativa no projeto, para que as funcionalidades como o assistente de cdigo e o
compilador encontrem as classes desta biblioteca. Essa opo tambm pode ser
acessada pelo menu Tools > Android > Sync Project with Gradle FIGS

O simples fato de referenciar no arquivo buildgradle a biblioteca de compatibili


dade da action bar faz com que o Gradle faa o build corretamente e resolva essa
dependncia na compilao do projeto. Mas lembre-se de clicar no boto Sync Now
que aparece no editor!
Depois de adicionar a dependncia, a primeira tarefa a fazer congurar 0 tema
do projeto no AndroidManifest.xml. O tema de compatibilidade o Theme.AppCompat
e suas variaes so o Theme.AppCompat.Light e Theme.AppCompat.Lght.DarkActonBar.
Ento altere o arquivo /res/values/stylesxml para utilizar o tema Theme.AppCompat.
Lght.DarkActonBar.

/res/values/styIes.xmI
<resources
<sty1e name="AppTheme" parent="Theme.AppConpat.Lght.DarkActonBar">

Como vamos utilizar o tema de compatibilidade AppCompat, no precisamos ter


vrios arquivos styles.xml, portanto apague os arquivos que esto em pastaS
/res/values-1/11 ,/res/values-1/14,/res/values-v21 etc. O Objetivo de utilizar essas pastas
com os qualicadores por API Level alterar o tema dependendo da verso do
Android. Vamos utilizar o tema AppCompat,que nico para todas as verses, por
isso podemos deixar apenas um arquivo /res/values/styles.xmI no projeto
_ j Q ' car o que precisa ser alterado no cdigo
Depois de configurar o tema, vamos veri
da activity Primeiramente, altere o cdi
80 da HACtvty para que ela seja lha
Captulo S I Action Bar e temas 143
de android.support.v7.app.AppCompatActivity. Feito isso, em vez de utilizar o mtodo
getActionBar() que retorna a classe android.app.ActionBar, obrigatrio utilizar o
mtodo getSupportActionBar() que retorna a action bar de compatibilidade, cuja
classe android.support.v7.app.ActionBar.

O cdigo a seguir mostra as alteraes bsicas da MainActivity para utilizar a action


bar de compatibilidade:

MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
public class MainActivity extends AppCompatActivity {
@Override

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setTitle("Capitulo 5");

A MainActivity utiliza tambm o Searchview e o ShareActionProvider, e essas classes


tambm fazem parte do pacote de compatibilidade, por isso precisamos alterar o
cdigo para utilizar as classes android.support.v7.widget.Searchview e android.support.
v7.widget.ShareActionProvider. Alm disso, importante que ao congurar os itens
de menu no mtodo onCreate0ptionsMenu(menu) voc utilize a classe android.support.
v4.view.MenuItemCompat para obter o Searchview e o ShareActionProvider. A seguir podemos
visualizar o cdigo completo da classe MainActivity com todas as alteraes necess
rias para utilizar a action bar de compatibilidade. Preste ateno nos imports das
classes, pois estamos importando os pacote android.support.v4 e android.support.v7.

MainActivity.java

import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.ShareActionProvider;
public class MainActivity extends AppCompatActivity {
@Override
144 Google Android - 4 edio
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.setTit1e("ActionBar Compat");
// Navegao por tabs
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
// Cria as tabs (Passa como parmetro o indice de cada tab: 1,2,3)
actionBar.addTab(actionBar.newTab().setTet("Tab 1")
.setTabListener(new MyTabListener(this,1)));
actionBar.addTab(actionBar.newTab().setTet("Tab 2")
.setTabListener(new MyTabListener(this,2)));
actionBar.addTab(actionBar.newTab().setTet("Tab 3")
.setTabListener(new MyTabListener(this,3)));
}

@0verride
public booiean onCreate0ptionsMenu(Menu menu) {
// Ina o menu com os botes da action bar
getMenuInater().inate(R.menu.menu_main, menu);
// Searchview
Menultem searchltem = menu.ndItem(R.id.action_search);
Searchview searchview = (Searchview) HenuItemCompat.getActionView(searchltem);
searchview.set0nQueryTetListener(onSearch());
// ShareActionProvider
Menultem shareltem = menu.ndItem(R.id.action_share);
ShareActionProvider share = (ShareActionProvider)
Henultemtompat.getActionProvider(shareltem);
share.setShareIntent(getDefau1tIntent());
return true;
}

Como o exemplo que fizemos utiliza a navegao por tabs, precisamos ajustar
os imports da classe MyTabListener para utilizar a action bar de compatibilidade e
tambm a verso de compatibilidade da classe FragmentTransaction. Vamos estudar
mais detalhes sobre a classe FragmentTransaction no captulo 8, sobre fragments.

MyTabListener.java

import android.support.v7.app.ActionBar;
import android.support.v4.app.FragnentTransaction;
Captulo 5 I Action Bar e temas 145
public class MyTabListener implements ActionBar.TabListener {
// Mesmo cdigo aqui
}

Muito bem, estamos quase acabando. Para nalizar a migrao para a biblioteca
de compatibilidade, precisamos ajustar o arquivo XML de menu. No exem
plo que zemos utilizamos as tags android:showAsActon, android:actionVewClass e
androd:actionProvderC1ass para congurar os itens de menu e tambm o Searchview
e o ShareActionProvider.

O problema que as verses antigas do Android no conhecem essas tags, ento


precisamos adicionar o namespace "app" no XML e em vez de utilizar o atributo
como androd:showAsActon="a1ways" Vamos utilizar como app:showAsActon="always".

A seguir podemos visualizar as alteraes necessrias no arquivo de menu. Preste


muita ateno em como o namespace "app" foi utilizado e veja que tambm estamos
utilizando as classes android.support.v7.widget.SearchVew e android.support.v7.widget.
ShareActionProvder da biblioteca VZ

/res/menu/main.xmI
<menu xmlns:android="http://schemas.androd.com/apk/res/android"
xmlnsztools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" tools:contet=".ManActvity">
<item androd:id="@+id/action_search"
android:icon="@drawable/c_acton_search" androd:title:"@string/action_search"
app:showAsActon="always"
app:actionVewC1ass="androd.support.v7.widget.Searchview" />
<item androd:id="@+id/action_share"
android:icon:"@drawable/ic_action_share" android:title="@string/action_share"
app:showAsActon="always"
app:actonProvderC1ass="android.support.v7.widget.ShareActonProvder" />
<item androd:id="@+d/action_refresh"
android:icon:"@drawable/c_action_refresh" android:title="@string/acton_refresh"
app:showAsActon="ifRoom" />
<item android:id="@+d/action_settings"
android:title="@string/action_settings"
app:showAsActon="never" />

Pronto! Isso tudo. Relembrando, zemos os seguintes passos para utilizar a


biblioteca de compatibilidade da action bar:
146 Google Android - 4 edio
1. Declarar a dependncia no arquivo app/buildgradle.
2. A activity precisa ser lha de androd.support.v7.app.AppCompatACtV>'
3. No cdigo da activity utilize 0 mtodo getSupportActonBar() para obter o
objeto androd.support.v7.app.ActonBar.

4. Ao congurar os itens de menu, utilize a classe androd . support.v4.view.MenuIternC0mDB

5. Utilize as classes de compatibilidade android.support.v7.wdget.SearchVew e


androd.support.v7.wdget.ShareActionProvider.

6. No arquivo XML de menu, utilize o namespace "app" pois as verses antigas do


Android no conhecem as tags showAsAction,actionVewC1ass e actionProvderClass.

Atualmente o Google recomenda utilizar as classes do pacote de compatibilidade,


pois a vantagem que essa biblioteca compilada junto com o cdigo do projeto,
e a biblioteca baixada pelo SDK Manager. Para qualquer melhoria ou correo
de bugs nessas classes basta atualizar a biblioteca pelo SDK Manager. j no caso
de uma biblioteca nativa, ela est naturalmente embutida dentro do sistema
operacional. Torna-se ento uma boa prtica utilizar sempre as bibliotecas de
compatibilidade; isso vem funcionado to bem que vrias classes so distribudas
apenas nessas bibliotecas. o caso do famoso componente ViewPager que ainda
vamos estudar, que ca na lib v4 e para o qual nem existe uma verso nativa.

Dica: ao criar um projeto no Android Studio com suporte ao Android 2.3


(API Level 9), a action bar de compatibilidade ser automaticamente congurada
no projeto.

5.13 Links teis

Para complementar sua leitura, seguem alguns links importantes da documen


tao ocial:
Android User Interface - Action Bar

http://developenandroid.com/guide/topics/ui/actionbart html
Android Training - Adding the Action Bar

https://developerandroid.com/training/basics/actionbar/index. html
Android Training - Styling the Action Bar

https://developerandroid.com/training/basics/actionbar/styling.html
, cAPruLo
it

Interface grca
tw gerenciadores de layout
\

No Android, existem diversos tipos de gerenciadores de layout. Alguns podem


organizar os componentes na horizontal e vertical, outros podem organizar os
componentes em uma tabela com linhas e colunas.
Neste captulo, aprenderemos a organizar os componentes da tela com o layout
desejado e construiremos diversos exemplos.

6.1 View

A classe androd.vew.Vew a classe-me de todos os componentes visuais do


Android, e suas diversas subclasses so utilizadas para criar a interface grca
do aplicativo. Cada subclasse de View precisa implementar o mtodo onDraw(Canvas),
responsvel por desenhar o componente na tela.
Voc deve ter percebido que muitas vezes, neste livro, chamamos uma view sim
plesmente de componente, para facilitar a leitura. Existem dois tipos de views/
componentes, os chamados Widgets e os gerenciadores de layout. Um widget
um componente simples que herda diretamente da classe View, como as classes
Button, Inagevew e Textvew. j os gerenciadores de layout consistem em subclasses
de androd.view.VewGroup e so popularmente chamados apenas de layouts.

6.2 (Iasse ViewGroup


Um gerenciador de layout utilizado para organizar a disposio dos componentes
na tela. A classe-me de todos os gerenciadores de layout a androd .vew.VewGroup.
Na gura 6.1, podemos visualizar a hierarquia das classes View e Viewroup, com
algumas de suas subclasses:

147
148 Google Android - 4 edio
J 'Garod.app.u:t|vlty _
ii ~ ' c G android.view.VIeW
0 rndvnewayldodz ht):ari-dro|d.v1ew.`v'ew ._.___ _ __________._ ,.._. _ _
o sctConter\tVlew(leyoutRoslD: inn. vozd o dzowtev- WW 0'*'"3""*)* "'d
O setCmtent\=rew(\-rew: androd.v1ew.v|ew): void N 7
,\;. _'|' t Q af-0d_vl0w.V|QWGI'OtD
G an: oe! ex view Q <5dl0|ll.\MdQEl.ll'l'l5QBV|BW
VA\
- _ G andzro|d.wrd9et.AbsoluteLayout - \
'B android.wldqet.Button G ondroid.w\doet.EdrtTet ~ G nd|ozd.wdqet.Rt-latrvetayout
G andro|d.w|d1et.FrarneLayout

G .zndozd.widoe=t.LmeafLwUf

G androd.wdqez.rableia~/out

Figura 6.1 - Hierarquia da classe Weu/Group.

Conforme visualizado na gura 6.1, as principais classes de layout so:


Layout Descriao g _ _ ,V
Abso1uteLayout Permite posicionar os componentes, fornecendo as coordenadas x
e y Est deprecated.
FrameLayout Tipo mais comum e simples de layout. Funciona como uma pilha,
sendo que uma view ca por cima da outra.
Li.nearLayout Utilizado para organizar os componentes na vertical ou horizontal.
TableLayout lho de LnearLayout e pode ser utilizado para organizar os com
ponentes em uma tabela, com linhas e colunas.
Re1ati.veLayout Permite posicionar um componente relativo a outro, por exemplo,
abaixo, acima ou ao lado de um componente j existente.

6.3 Congurando a altura e largura de uma view


Ao construir a interface grfica da tela, necessrio congurar a largura e altura
do layout principal, bem como a largura e altura de cada view inserida no layout.
Isso feito por meio dos parmetros androd:1ayout_heght e androd:1ayout width.
Parmetro Descrio
androd:1ayout_height Especfica a altura de un;;Q-~ *Hum W .`m_"
androd:1ayout_wdth Especica a largura de uma view
Captulo 6 n Interface grca - gerenciadores de layout 149
O trecho de cdigo a seguir mostra a congurao de largura e altura de um
LinearLayout.

<LnearLayout mIns:androd="http://schemas.android.com/apk/res/android"
androd:1ayout_wdth="natch_parent" androd:Iayout_heght="natch_parent"
android:orentaton="verticaI" >

Esses dois parmetros podem receber os seguintes valores:


Valor Descrio
fiII_pa rent Signica que o componente precisa ocupar todo o tamanho denido
por seu pai (layout). Isso deve ser utilizado sempre que algum layout
ou view precisa ocupar a tela inteira ou todo o espao de layout
denido pelo layout-pai.
natch_pa rent Idem ao fiII_parent. Na verdade, o fiII_parent virou deprecated no
Android 2.2. Se voc vai criar aplicaes para Android 2.2 ou superior,
poder utilizar o match_parent. Ambas as notaes tm o mesmo fun
cionamento, e o Google apenas descontinuou a notao U._parent.
wrap_content Utilizado para o componente ocupar apenas o tamanho necessrio
na tela, sem esticar.
nmero (dp) Nmero inteiro especicando o tamanho, por exemplo, 50dp para
ocupar 50 pixels da tela. A notao dp (density independent pixel) faz
a converso correta de pixels conforme a densidade/ resoluo da tela
do dispositivo. Nunca utilize a notao 50px, sempre utilize 50dp para
garantir que o aplicativo funcione bem em diversos tamanhos de telas.

Os parmetros androd:Iayout_heght e androd:Iayout_wdth so denidos pela classe


interna ViewGroup.LayoutParans. Se voc construir a tela utilizando diretamente a API
Java, ser necessrio conhecer essa classe. Caso contrrio, isso no obrigatrio.
Nos prximos tpicos veremos na prtica como congurar a largura e altura do
layout e de suas vievig e explicaremos detalhadamente todos os gerenciadores de
layout do Android.

6.4 Entendendo as constantes wrap_content e match_parent


Neste tpico, vamos criar alguns exemplos para voc entender o signicado das cons
tantes wrap_content e match_parent, utilizadas para definir o tamanho do layout e das views.

Nota: para testar os exemplos crie o arquivo XML de layout no projeto, e faa a
pr-visualizao do layout no editor visual. No ser necessrio executar o cdigo.
Ou, se preferir, abra o projeto deste captulo no Android Studio e abra cada arquivo
de layout no editor, assim car mais fcil para acompanhar as explicaes.
150 Google Android - 4 edio
Nesses arquivos de layout, vamos escolher FrameLayout como tag raiz, mas isso
na verdade nao importa. O primeiro exemplo demonstra >FFf'1@L>'0U Uflllfmd
(vsatrihtiuwsandroid:1ayout_widthtrandrod:1ayout_heightxnimdrwraD_Ctt-l5U
significa que a altura e largura d layout sera exatamente o espaco I1CCCSS8l`l()
para conter todos os componentes/views definidos na tela.

/res/layout/exempIo_wrap_content.mI
<?m1 version="1.0" encoding="utf-8"?>
<FrameLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
android:background="#8B8B83" >
<ImageVew
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content"
androd:src:"@drawab1e/android_green" />

Nota: sc a altura e largura de um layout forem especicadas como wrap_content,


o tamanho desse layout ser denido pelo tamanho necessrio para desenhar
todos os seus componentes.

Nesse exemplo, o Image-View utiliza o valor wrap_content para denir sua altura e
largura, consequentemente a imagem ocupa somente o tamanho necessrio. A
gura 6.2 mostra a pr-visualizao deste layout no editor.

Figura 6.2 - l~xcmpl de wrap_cnten_


Captulo 6 i Interface grca - gerenciadores de layout 151
Observe que o fundo cinza atrs da imagem mostra o tamanho ocupado pelo
layout principal, que nesse caso o mesmo do tamanho da imagem. O restante
da tela ca com fundo branco, pois o tema padro do projeto.

Nota: sempre que car em dvida sobre qual o tamanho que o layout est
ocupando, utilize o atributo androd:background para denir uma cor para o fundo.
Neste caso estou utilizando a cor cinza, assim possvel visualizar a rea
retangular ocupada pelo layout.

No prximo exemplo, vamos alterar a altura e largura do layout principal para


match_parent para fazer com que ele preencha a tela inteira.

/res/layout/exempIo_match__parent.xmI
<?xm1 verson="1.0" encodng="utf-8"?>
<FrameLayout m1ns:androd="http://schemas.androd.com/apk/res/android"
androd:1ayout_width="match_parent" android:1ayout_height="match_parent"
androd:background="#8B8B83" >
<ImageVew
android:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:src="@drawable/androd_green" />

A gura 63 mostra que desta vez o layout com fundo cinza ocupou a tela inteira e a
imagem ocupou apenas um pequeno espao necessrio para que fosse desenhada.

Figura 6.3 - Exemplo match_parent.


152 Google Android - 4* edio
,. rt ~ r, . " - ' w wra content faz
L ,omo podemos ver o 11_parent ou match_parent esttcam a new. L D
com que a view ocupe somente o espao necessario.

... .. -. ~.
~ -. z; ~~
' ' *' zhr* uma
7 `ilm*nte.
Dica: um atalho interessante do Android Studio c o Shtft+Sh1ft, qu* l
Janekapanivocechguarcuuquerrnnnechranquni)paral0LJh2 k)3

Agora vamos alterar tambm a largura e altura da imagem para I'1'CCh_Df@'f- 1550
signica que ela deve esticar e ocupar o tamanho ocupado por seu pal.

a- /res/layout/exempIo_match_parent_imagem.xml

<?xm1 verson="1.0" encodng="utf-8"?>


<FrameLayout m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
android:background="#8B8B83" >
<ImageVew
android:1ayout_wdth="natch_parent" androd:1ayout_height="natch_parent"
androd:src="@drawable/androd_green" />

O resultado dessa alterao pode ser visto na gura 6.4. Sempre que usar o valor
match_parent, os componentes vo esticar/expandir para preencher 0 tamanho do
layout-pai. Mas tome cuidado com imagens, pois geralmente elas devem ser dc
nidas como wrap_content, justamente para no distorcer a figura.

Figura 6.4 - Exemplo com a imagem c5rian10_


Captulo 6 I 'Interface grca - gerenciadores de layout 153
Em linhas gerais, recomendado que a largura e altura do layout principal sejam
denidas como match_parent para ocupar a tela inteira. Se necessrio, congure a
cor de fundo do layout para ter certeza do tamanho que ele est ocupando. Ao
congurar a altura e largura para cada view da tela, possvel combinar os valores
match_parent e wrap_content para expandir os componentes somente na vertical ou
horizontal, conforme necessrio.
Para voc entender melhor como funciona o match_parent e wrap_content, vamos criar
outro exemplo. Desta vez vamos utilizar o componente EdtText, que representa
um campo para o usurio digitar algo.

/res/layout/exempIo_textview_wrap_content.xmI
<?ml verson="1.0" encodng="utf-8"?>
<LnearLayout xm1ns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_width="match_parent" androd:1ayout_height="match_parent"
androd:paddng="10dp" androd:orientaton="vertca1" >
<TetVew

androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="@strng/nome" />
<EditText
androd:1ayout_width="wrap_content" androd:1ayout_height="wrap_content"
android:nputType="tet" />
<BU'C'C0n

androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_gravity="right" androd:text="@strng/ok" />
</LnearLayout>

A gura 6.5 mostra a pr-visualizao desse layout. Observe que o campo de


texto ocupou somente o espao necessrio, pois sua largura foi denida com
android:layout_width="wrap_content" e no existe nenhum texto digitado. medida
que o usurio digitar algo, esse campo vai expandir automaticamente, o que pode
no ser o desejado.

Figura 6.5 - Campo de texto com wrap_content.


154 Google Android - 4 edio
.\ruaias pn^tairusuiatk\ veia qtufrilaywiut e utu LinearLay0Ut f<ll***f"l<*
Y3i
\'ertieal. o qual liaz com que eada view seia adieiouada lo;o al aixo da outra ver
tiealnieute.(3tura etutvaekidt'e.urihtutiandrod:layout_grVy="F9h"Itlotati
que lax. eom ele seia posieionado ua direita.
lara eorrigir o layuitJesse siniples rniulario.\ Nifi ^\'l"ff " L
ruira th eatuivo ele texttx o seja. a \1e\v EdtTet.lss e leito ewiui atribtiui
androd:1ayout_wdth="match_parent" tiara tpie stia laiyuara tvreeaielia tuJt esrxigti
tlisw\i1i\w:l tll;i}wL1t-twai.

/res/layout/exempIo_textview_wrap_content.xmI
<?xml version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" android:1ayout_heght="match_parent"
androd:paddng="10dp" androd:orentation="vertcal" >
<TextView
android:layout_width="wrap_content" androd:layout_height="wrap_content"
androd:tet="@strng/nome" />
<EdtTet
androd:1ayout_wdth="natch_parent" androd11ayout_heght="wrap_content"
androd:nputType="tet" />
<Button
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content"
androd:1ayout_gravty="rght" androd:tet="@strng/ok" /

A figura bb mostra a r~-muziiztzti do layout. e dessa vez o eampo de texto


esueotiualarguraxu11oe1uxu'uxJottaniauluiehspeniuxd.Lbserveiqueidezupenas
estuxiutiaIargtu1r,pustitiniexeuiosiiatxiufuguraeatweh:altura,;1epialtx)utuiua
com wrap_content. (omo exereieio, voe pode estiear o eampo de texto ua altura
txutiverriqturaeonteee

Fgunibb-(Mnqnnh'uwhunninuuh_unnwHruthuuuni
Captulo 6 I Interface grca - gerenciadores de layout 155
Note que no adicionamos valores xos para o tamanho, pois recomendado
criar telas que se ajustam automaticamente conforme a resoluo. Por isso no
importa se o aplicativo vai executar no smartphone, tablet ou numa gigante TV
que o layout ser o mesmo.

6.5 Framelayout
A classe androd.wdget.FrameLayout o mais simples de todos os gerenciadores de
layout e utilizada para empilhar uma view sobre outra.
possvel adicionar vrios componentes dentro do F rameLayout, e sempre os ltimos
componentes caro sobre os anteriores, seguindo o conceito de uma pilha, em
que o ltimo elemento ca no topo. O caso mais comum disso para inserir um
ProgressBar por cima de alguma outra view:A propsito, a classe ProgressBar uma view
que desenha aquela bolinha que ca girando para simular algum processamento.
Para exemplicar, vamos inserir um ProgressBar por cima do boto OK do formulrio
do exemplo anterior. Dessa forma, ao clicar no boto podemos disparar a animao
do Prog ressBar para simular o processamento de alguma tarefa. Vamos aprender
como fazer isso no cdigo depois, por enquanto vamos estudar apenas os layouts.

/res/layout/exempIo_frame_Iayout_1.xml
<?m1 version="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_wdth="match_parent" androd:layout_heght="match_parent"
androd:orentaton="vertca1" androd:paddng="19dp" >
<TetVew androd:1ayout_wdth="wrap_content" androd:layout_height="wrap_content"
androd:tet="@strng/nome" />
<EdtText androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:inputType="tet" />
<FrameLayout android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content" >
<Button
androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="rght" androd:text="@string/ok" />
<ProgressBar androd:Iayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center" />

</LnearLayout>

A gura 6.7 mostra a pr-visualizao desse layout. Talvez na gura no que


visvel, mas eo ProgressBar to crculo animado que est sobre o boto. justamente
isso que o *FrameLayout faz, ele adiciona uma view sobre a outra, como uma pilha.
156 Google Android - 4 edio

* -s.,
z

Figura 6.7 - FrameLayout com um ProgressBar

Nota: vamos estudar mais sobre os componentes visuais no prximo captulo;


no momento preocupe-se apenas com os gerenciadores de layout. Apenas
por curiosidade, o ProgressBar ao ser inserido no layout vai exibir a animao
automaticamente. Para parar a animao, voc deve esconder o ProgressBar com
o mtodo setVsib1e(boo1ean) definido na classe androd.vew.Vew.

Outro caso comum de utilizao do FrameLayout com o ProgressBar quando temos


uma lista na tela, como por exemplo a lista de contatos da agenda. Como boa
prtica de programao, recomendado que, durante o carregamento dos dados
da lista, o ProgressBar seja utilizado para exibir uma animao para o usurio. O
seguinte layout mostra um Lstvew, que o componente que exibe uma lista e
no centro um ProgressBar para mostrar a animao.

/res/layout/exempIo_frame_Iayout_2.mI
<?xm1 verson="1.0" encodng="utf-8"?>
<FrameLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:orientaton="vertca1" androd:paddng="10dp" >
LStVew
androd:d="@+d/Istvew"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent" />
ProgressBar
android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravity="center" />

A figura 6.8 mostra a pr-visualizao desse layout. O editor mostra urna lista para
simular que o Lstvew est preenchido. j o ProgressBar utilizado para mostrar a
animao para o usurio, enquanto os dados da lista esto sendo carregados. O
ProgressBar est por cima do Lstvew, pois isso que o FrameLayout faz_
Captulo 6 I Interface grca - gerenciadores de layout

ltm 1
o..
mzrl THY K

R2_
fzti ima Z

Item 3
`S|.'l' i
.lif Q*

lte-m 4
sua trr'n -1

Item 5
tltl Hr-m S

'Item 5
au: mais l

Item 7

Figura 6.8 - FrameLayout com um LzstV1ew e ProgressBar

6.6 LinearLayout
A classe androd .wdget. LinearLayout um dos gerenc1adores de layout ma1s utilizados
sendo possvel organizar os componentes na horizontal (padrao) ou na vertlcal
A orientao, nesse caso, configurada pelo atr1buto androtd ortentatton
A seguir temos um exemplo de como utilizar a classe L1neacLayout

/res/layout/exemplo_linear_layout_1 xml
<?xnl verslon="1.0" encodng="utf-8"?>
<LnearLayout xnlns:androd="http://schenas androtd com/apk/res/androtd
androd:layout_wdth="natch_parent" androtd layout hetght match parent
androd:ortentation="horlzontal ou verttcal
androd:paddng="10dp" >
<ImageVew androd:src="@drawable/androtd blue
androd:layout_wdth="wrap_content androtd layout hetght wrap content
androld:contentDescrpton="@strng/content descrtptton /
<InageVew androd:src="@drawable/androtd green
androd:layout_wldth="wrap_content androtd layout hetght wrap content
androd:contentDescrpton="@string/content descrtptton /
Google Android - 4 edio

^A
158

~ ' II ~ u ` ` I ' ' . :H lp H.


A figura 6.9 exibe a pr-visualizao desse layout, priml
ro cm a configurao
andro1d:or1entat1on= horizontal eaoladocomaconfiguraaoandrotd.or1enta'C10 VG 61

''IPI|
Pa|e[[e- 'UPene
V' N Of! ' Vl ev2palee
Pxus NexusV One
_ ' ll
exem0l0_l|near_laY0ut_1 xml 2 ' 0mDl0-l''UY0Ul- 1 *ml i

Form Widgets Cl lffi' Fi , ~ Form WWF lr-l 'J


*W l*~t*.'!3v At- Teirtvuew

^~ Larue lex? l'L~ Large Text

' ^.~l (hum left fm) Medium Text

I'I
Ab Small Text
Small Text
l* Button
Button ;~ vw

Q .:'l.

'* Small Button Small Button

.r Text Fields
. Toczqle8utton

3 V
fl cneciB0
Lv~\= _.
C recl<8o
ToggleButton gi z?

Figura 6.9 - LinearLayout na horizontal e vertical.


O funcionamento do LnearLayout bem simples e no tem muito o que explicar.
Apenas lembre-se de que, caso o atributo androdzorentaton seja omitido, 0 padro
horizontal.

6.7 Linearlayout - controle do alinhamento "Iayout_gravity"


Agora demonstraremos como alinhar os componentes de forma centralizada, na
esquerda ou direita da tela. Para isso, usaremos 0 atributo androd:layout_gravty.
Os valores vlidos para esse atributo so top (cima), bottom (baixo), left (esquerda)
right (direita), center_vertcal, fill_vertical, center_horzontal, fill_horzontal, center c fill.
Anteriormente, no exemplo do formulrio com o Franelayout e o ProgressBar, utili
zamos o seguinte cdigo:
<ProgressBar
androd:layout_wdth="wrap_content" androd:layout_heght="wrap_content"
android:layout_gravty="center" />

Repare que o ProgressBar est declarado com o atributo androd :layout gravty="center",
o qual faz com que ele seja posicionado no centro do seu layout-pai. Para demons
trar outras maneiras de utilizar o atributo androdzlayout gravty, criaremos uma
Q ue
tela com trs imagens inseridas na vertical, a in a as
cam l` hna
d esquerda, no
centro e na direita da tela.
Captulo 6 I Interface grca - gerenciadores de layout 159
/res/layout/exempIo_Iinear_Iayout_2_gravity.xmI
<?xm1 verson="1.0" encodng="utf-8"?>
<LnearLayout xmlns:android="http://schemas.android.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:orentaton="vertica1" >
<ImageVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_gravity="1eft" androd:src="@drawable/androd_green" />
<InageVew
android:1ayout_wdth="wrap_content" android:1ayout_heght="wrap_content
androd:1ayout_gravty="center" androd:src="@drawab1e/androd_b1ue" />
<ImageVew
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:1ayout_gravty="right" androd:src="@drawab1e/androd_green" />

A gura 6.10 exibe as imagens alinhadas esquerda, no centro e na direita con


forme esperado.

Figura 6.10 - Controlando 0 alinhamento com 0 atributo layout _gra1/ity.

6.8 LinearLayout - controle do peso


Outra forma de organizar os elementos na tela atribuir um peso (porcentagem)
para cada um. O componente com o maior peso ocupar o maior espao na tela.
Para isso podemos utilizar o atributo android:1ayout_weight.
Para demonstrar isso, criaremos um exemplo com um formulrio, que contm
os campos nome, email e observaes. Nesse exemplo o campo observaes
deve ocupar a rea restante da tela para que o usurio possa digitar um texto um
pouco maior. Por isso foi definido o peso = 1 para esse componente utilizando o
atributo android:1ayout_weght="1".
160 Google Android - 4 edio
f1 /res/layout/exempIo_Iinear_Iayout_3__weight.mI

<?m1 verson="1.0" encodng="utf-8"?>


<LnearLayout m1ns:androd="http://schemas.androd.com/apk/FGS/3dfd"
androd:orentaton="vertca1"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_D3f@"" >
<TetVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wraD_C"t@"t"
androd:tet="Nome" />
<EdtTet
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrD_C0t@t
androd:tetCo1or="#ff0000" />
<TextVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content
androd:tet="Ema1" />
<EdtTet
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content" />
<TetVew
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:text="0bservaes" />
<EdtTet
androd:layout_wdth="match_parent" androd:1ayout_height="0dp"
androd:1ayout_weght="1" />
<Butt0n
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:text="Envar" />
</LnearLayout>

A gura 6.11 mostra a pr-visualizao desse layout. Observe que o campo ob


servaes ocupou o restante da tela. Isso ocorreu porque o campo observaes
foi denido com o atributo androd:layout_weght="1" e androd:1ayout_heght="9dp".
Como ele o nico componente que tem peso, esticou na altura.

Nota: sempre que utilizar o peso com o atributo androd:1ayout_weght, deve-se


denir a largura ou altura da view com Gdp. Essa notao indica que a largura
ou altura da view deve respeitar o peso atribudo.

Sempre que apenas um componente na tela precisar ocupar o tamanho restante,


basta atribuir o valor 1 para o atributo android:1ayout_weght, como acabamos de
vericar. Mas o que acontece se for necessrio expandir mais de um componente
Para facilitar a explicao criaremos um novo exemplo, com trs campos de texto.
l
Captulo 6 n Interface grca - gerenciadores de layout 151

E1
Enviar
2

Figura 6.11 - Exemplo de layout_weight com peso = 1.

/res/layout/exempIo_linear_Iayout_4_weight.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:orentaton="vertica1"
androd:layout_wdth="match_parent" android:layout_heght="match_parent" >
<EdtTet.androd:1ayout_weght="1"
andrd:layout_wdth="match_parent" android:1ayout_height="0dip"
android:text="Texto (weght=1)" />
<EditTet android:1ayout_weght="2"
androd:1ayout_wdth="match_parent"
android:1ayout_height="0dip"
android:text="Texto (weight=2)" />
<EdtText androd:1ayout_weight="3"
android:layout_wdth="match_parent" androd:1ayout_height="0dp"
android:tet="Texto (weght=3)" />

Observe que cada EdtText define um valor diferente para o atributo


androd:1ayout_weight. O primeiro define o valor 1, o segundo o valor 2, e o terceiro
o valor 3. A gura 6.12 mostra a pr-visualizao desse layout. Como podemos
vericar, o campo de texto com o maior peso ocupou a maior rea da tela.
Lembre-se de que tambm a altura de cada componente foi denida com 0dp ou
Gdip, que a mesma coisa. Isso permite deixar a altura configurvel pelo peso que
esse componente tem. O mesmo conceito tambm se aplica largura.
162 Google Android - 4 edio

|'QI1O (vnil=2l

Iulo (miqti-3)

Figura 6.12 - Exemplo do atributo layout_u/eight para controlar o peso.

Para entender melhor o que aconteceu, vamos fazer uma superconta para somar
estes valores: 1 + 2 + 3 = 6. Como a soma dos valores do atributo 1ayout_weght ,
cada campo de texto vai ocupar o espao proporcional na tela, relativo ao total.
como se 6 fosse 1.000/0. Assim, podemos visualizar que o ltimo campo denido
como 1ayout_weght=3 ocupou metade da tela, sendo que 3 50% de 6.
Para entender melhor como a atribuio de pesos funciona, vamos brincar um
pouco com esse exemplo, alterando o cdigo-fonte do arquivo XML de layout.
Por exemplo, vamos alterar o primeiro campo de texto para peso = 3. Nesse caso.
agora a soma ficaria 3 + 2 + 3 = 8 (Figura .13). Como o primeiro e o terceiro
campo tm os mesmos pesos, eles ocuparo o mesmo espao na tela. j o campo
do meio ser o menor.

Para nalizar este tpico, vou dar uma dica muito importante e que muitos desen
volvedores Android demoram a descobrir ou entender. O componente Lstvew que
utilizado para criar listas deve ser utilizado na maioria das vezes com peso = 1
na vertical para esticar a sua altura.
<LstVew
androd:d="@+d/Iistvew"
androd:1ayout_wdth="match_parent"
androd:1ayout_heght="@dp" androd:1ayout_weght="1" />
Captulo 6 n Interface grca - gerenciadores de layout 153
A diferena que, se congurarmos a altura como androd : layout_heght="match_parent",
a lista vai esticar sem pensar em quem est abaixo dela, mandando todas as outras
views para fora da tela. Mas com a configurao do peso, o Ltstvew vai esticar
at onde puder, pois geralmente somente ele tem o peso = 1, ou seja, somente o
Lstvew deve esticar. medida que formos avanando na leitura deste livro, vamos
aprender mais detalhes e voltamos a revisar este conceito.

Y mw (weight =3}

Texto (wegm<=2} 3

Texto (wegzm=3}

Figura 6.13 - Exemplo do atributo layout_u/eight para controlar 0 peso.

6.9 Tablelayout - uso de uma tabela com linhas e colunas


A classe androd.wdget.Tab1eLayout um dos layouts mais teis para construir al
gumas telas, como por exemplo formulrios. Cada linha da tabela formada por
um and rod.wdget.Tabl.eRow, que uma subclasse de LnearLayout e consequentemente
pode conter outros componentes, em que cada um representa uma linha na tabela.
O TableLayout tem os atributos androd:stretchColumns e androdzshrnktolumns, os quais
recebem os ndices das colunas, separadas por vrgula, que devem ser alteradas.
Por exemplo, pode-se informar para o atributo androd:stretchColumns os valores
2,3 para alterar apenas as colunas de ndice 2 e 3. Observe que o ndice inicia
em zero, de modo que nesse caso as colunas 3 e 4 sero alteradas.
Veja a seguir a denio de cada atributo:
164 Google Android - 4 edio
android:stretchCo1umns - Faz com que as colunas ocupem O spag d15P0mVl
na tela, expandindo-as. Use esse atributo quando for necessario que uma
coluna ocupe a linha inteira. O funcionamento como um coispan de uma
pgina HTML.
androidzshrinktolumns - Faz com que as colunas especicadas sejam sempre
exibidas na tela. Caso o valor do texto seja muito grande e que para fora da
tela, a linha quebrada e o texto exibido em vrias linhas na mesma coluna

6.10 TabIeLayout e shrinkCo|umns - contrao de colunas


O atributo android : shrinkCo1umns recebe o nmero das colunas que preciso contrair,
ou seja, fora o contedo da coluna para car dentro da tabela. Esse recurso deve
ser utilizado quando o componente de uma coluna longo demais.

/res/layout/exempIo_tab|e_Iayout_shrink.xm|
<Tab1eLayout xmins:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:shrinkCo1umns="2">

<TetView android:tet="Co1una 1" />


<TetView android:tet="Co1una 2" /

<TetView android:tet="Co1una 1" />


<TetView android:text="Co1una 2" />
<TetView android:text="Texto Grande que vai sair da tela mas o shrinkCo1umns=2 foi
utilizado para quebrar. " />

<TetView android:text="Co1una 1" />


<TextView android:text="Co1una 2" />
<TetView android:text="Coiuna 3" />

Neste exemplo, a coluna 2 da segunda linha (lembre-se de que o ndice da colu


na C0m8 m 0) tem Um texto muito grande. O atributo android:shrinkCo1unns foi
usado para forar seu contedo a ser exibido na tela (Figura 6.14). Dessa fQfm3_ 0
texto foi quebrado e consequentemente foi necessrio utilizar mais de uma linha.
Captulo 6 n Interface grca - gerenciadores de layout 165

Coluna 1CoIuna 2 _v__~_.__m___~~__W___


Coluna 1 Coluna exto Grande que vai sair da
aew um shri=nkCoIumns=2

eu -_c_,_13_33_9.12s9_U_?L$l Li
Coluna ICo|una 2CoIuna. 3

Figura 6.14 - TableLayout com shrin/cC0lumns.

6.11 Tablelayout e strechCoIumns - expanso de colunas


O atributo androd:stretchColumns recebe o nmero das colunas que precisam ser
expandidas (forar o preenchimento da tela inteira).

/res/layout/exempIo_tabIe_layout_stretch.xmI

<Tab1eLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent"
androd:layout_heght="match_parent"
androd:stretchCo1umns="1" >

<TetVew android:text:"@strng/lsta_de_produtos" />


<Vew

android:1ayout_heght="2dp"
androd:background="#FF90909@" />

<TetVew androd:tet="@strng/produto_a" />


<TetVew

android:layout_gravty="rght"
androd:text="@string/reas_100" />

<TetVew androd:tet="@strng/produto_b" />


<TextView

androd:layout_gravty="rght"
androd:tet="@strng/reas_200" />

<TextVew androd:tet="@strng/produto_c" />


<TetVew
166 Google Android - 4 edio

androd:1ayout_gravty:"rght"
androd:tet="@string/reais_300" />

<Vew

androd:1ayout_heght="2dp"
androd:background=#FF909090" />
<LnearLayout
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="match_parent"
androd:gravty="bottom|center_horzonta1" >
<Button
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="35dp"
android:text="@strng/cancelar" />
<Button
androd:1ayout_wdth="wrap_content"
androd:1ayout_heght="35dip"
androd:tet="@strng/comprar" />
</LnearLayout>

A pr-visualizao desse layout pode ser vista na gura 615.

-.

Cancvm Comum

Figura 6.15 -'I`ab1I-ayut com strctchColumns.

Observe que o tamanho da segunda coluna 6 mai r >


que o daprimeirajustamente
pelo atributo androd:stretchColumns estar con
*urado.V
ej 1 t nn1em
-b que o atributo
Captulo 6 I Interface grca - gerenciadores de layout 157
androd:1ayout_gravty=" right" usado para posicionar os valores da segunda coluna
direita da tabela. Os botes na parte inferior da tela esto centralizados, pois
o LinearLayout est com a congurao e androd:gravity="botton|center_horizonta1".
Tambm possvel adicionar outros componentes (views) na tabela, no somente
o TableRow. Caso outro componente seja adicionado na tabela ele ocupar a linha
inteira. Veja que esse exemplo demonstrou a integrao entre dois layouts dife
rentes, LinearLayout e TableLayout.

Nota: um gerenciador de layout tambm um tipo de View e por isso possvel


inserir um layout dentro de outro para criar interfaces mais complexas. Esse
conceito chamado de layouts aninhados.

6.12 Tablelayout - criando um formulrio


Um formulrio com o Tab1eLayout similar a um formulrio HTML. Cada Tab1eRow repre
senta uma linha, e cada componente, uma coluna. Neste exemplo a segunda coluna foi
congurada para expandir seu contedo, utilizando o atributo android : stretchCo1umns="1"
(lembre-se de que o ndice comea em O). Dessa forma, os campos do login e senha
da segunda coluna ocuparo todo o espao restante da linha.

/res/layout/exempIo_table_layout_form.xml
<?ml version="1.0" encoding="utf-8"?>
<Tab1eLayout xmins:android="http://schenas.android.con/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:stretchCo1umns="1">

<TextView android:text="Usurio: " android:padding="3dip" />


<EditTet android:id="@+id/canpoLogin" android:padding="3dip" />

<TextView android:text="Senha: " />


<EditText android:id="@+id/campoSenha" android:inputType="textPassword" />

<TableRow android:gravity="right">
<Button android:id="@+id/login" android:text="Logn" />
168 Google Android - 4 edio
Lihserve qturziiihinia hria usa o atrvuto androd:graV1tY= flght Para al*
iihar o bcHtkrlogin direua.( can1pth:scr1a COHICH1 0 arrvuro
androd:nputType="tetPassword" para mostrar os no lugar do texto (Flgf -J.

Senha

Login

Figura 6.16 - TableLayout para a construo de um formulrio.

6.13 GridLayout
A classe androd.wdget.GrdLayout organiza as views em linhas e colunas; para isso
a view deve utilizar os atributos 1ayout_row e 1ayout_co1umn.

No exemplo a seguir vamos criar quatro botes. Veja que o layout foi denido
com 2 linhas e 2 colunas e cada view inserida em determinada posio.

/res/layout/eemp|o_grid_Iayout.mI
<?m1 verson="1.0" encodng="utf-8"?>
<GrdLayout xm1ns:androd="http://schemas.android.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:padding="16dp"
androd:co1umnCount="2" android:rowCount="2">
<Button

android:1ayout_wdth="wrap_content" androd:1ayout_height="wrap_content"
android:1ayout_co1umn="0" androd:1ayout_row="0"
android:tet="Boto 1" />
<Button

androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:1ayout_co1umn="1" androd:1ayout_row="6"
androd:tet="Boto 2" />
Captulo 6 n Interface grca - gerenciadores de layout 159
<Button
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:Iayout_co1umn="0" androd:1ayout_row="1"
androd:tet="Boto 3" />
<Button
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_co1umn="1" androd:1ayout_row="1"
androd:tet="Boto 4" />
</GrdLayout>

O GrdLayout tambm contm alguns atributos que no usei nesse exemplo, como
os atributos androd:1ayout_co1umnSpan e androd:1ayout_rowSpan, os quais podem ser
utilizados para que a view ocupe mais de uma coluna ou linha, de forma similar ao
span em pginas I-ITML. Voc tambm pode usar o atributo androd:1ayout_gravty
para informar se a view deve esticar na vertical, horizontal ou ambas (fi11_vertca1,
11_horzonta1 e 11). O resultado do exemplo pode ser visualizado na gura 6.11

.............
,mni i-aowz_
*sim

Figura 6.17 - GridLayout organizando as views em linhas e colunas.

importante ressaltar que o GridLayout s est disponvel a partir do Android 4.0


(API Level 14), mas felizmente podemos utilizar a biblioteca de compatibilidade
compatvel com o Android 2.1 (API Level 7). Para isso basta adicionar a seguinte
dependncia no arquivo app/build.gradle:
comp1e 'com.androd.supportzgrdlayout-v7:21+'

Feito isso, podemos utilizar a classe androd . support.v7.wdget.GrdLayout. Observe que


por questes de compatibilidade, obrigatrio utilizar o prexo app nos atributos
do layout, pois eles no so reconhecidos nas verses anteriores do Android.

/res/layout/exempIo_grid_|ayout.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<android.support.v7.widget.GrdLayout m1ns:androd="http://schemas.androd.com/apk/res/androd'
xmlns:app="http://schemas.androd.com/apk/res-auto"
androd:1ayout_width="match_parent" androd:1ayout_height="match_parent"
170 Google Android - 4 edio
androd:padding="16dp"
app:co1umnCount="2" app:rowCount="2">
<BUtt0n
androd:1ayout_width="wrap_content" androd:1ayout_height="WF3P_C"tt"
androtd:tet="Boto 1"
app:1ayout_co1umn="0" app:1ayout_row="0" /

</androd.support.v7.widget.GridLayout>

6.14 Relativelayout
A classe androd.widget.Re1atveLayout pode posicionar os componentes ao lado,
abaixo ou acima de outro componente j existente. Para isso necessrio denir
um id para cada componente da tela, pois o posicionamento de um componente
depende de outro. Os seguintes atributos podem ser utilizados para informar a
posio do novo componente inserido relativo ao componente j existente:
Atributo Descrio
anroztzyoutzbiori Posiciona abaixo do componente indicado.
androd/Iayoutzabove Posiciona acima do componente indicado.
android:1ayout:toRght0f Posiciona direita do componente indicado.
androd/1ayout:toLeftOf Posiciona esquerda do componente indicado.
androd:1ayout_a1gnParentTop Alinha no topo do layout-pai.
android:1ayout_a1ignParentBottom Ainha abaixo do layout-pai.
android:1ayout_a1gnParentRght Alinha direita do layout-pai.
android:1ayout_a1ignParentLeft Ainha esquerda do layout-pai.
android:1ayout_a1ignTop Alinha no topo do componente indicado.
androd:1ayout_a1gnBottom Ainha abaixo do componente indicado.
androdzlayout a1ignRight Alinha direita do componente indicado.
androidzlayout a1i.gnLeft Ainha esquerda do componente indicado.
androd:1ayout_margnTop Utilizado para denir um espao na margem superior
do componente.
androidzlayout margnBottom Utilizado para denir um espao na margem inferior
do componente.
android:1ayout_marginRight Utilizado para denir um espao direita do compo
nente.

androdzlayout margnLeft Utilizado para denir um espao esquerda do com


ponente.
Captulo 6 I Interface grca - gerenciadores de layout

A seguir podemos visualizar um exemplo para construir um formulrio

/res/layout/exempIo_reIative_|ayout_form.xml
<?nl version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:background="#8B8B83" android:padding="16dp">
<TetView android:id="@+id/labelUsuario"
android:layout_width="55dip" android:layout_height="wrap_content"
android:tet="@string/usuario"/>

<EditText android:id="@+id/canpoUsuario"
android:layout_width="natch_parent" android:layout_height="wrap_content
android:background="@android:drawable/editbox_background"
android:layout_toRight0f="@id/labelUsuario"
android:inputType="text"/>

<TextView android:id="@+id/labelSenha"
android:layout_width="55dip" android:layout_height="wrap_content"
android:layout_below="@id/campoUsuario"
android:gravity="left" android:tet="@string/senha"/>

<EditText android:id="@+id/canpoSenha"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_toRight0f="@id/labelSenha"
android:layout_alignTop="@id/labelSenha"
android:inputType="textPassword" />

<Button android:id="@+id/btLogin" android:layout_narginTop="10dp"


android:layout_width="wrap_content" android:layout_height="35dip"
android:layout_below="@id/canpoSenha"
android:layout_alignParentRight="true"
android:tet="@string/login" />

A pr-visualizao desse arquivo pode ser vista na gura 6.18.


172 Google Android - 4 edio

usuario

Senha

Lql

Figura 6.18 - Relativclyout para criar um irmulrio.


Consideraes sobre o exemplo:
1. O label Usurio e o primeiro componente,justamente por isso, posicionado
acima, alinhado fi esquerda.
2 O campo de texto Usurio foi alinhado direita do label Usurio, com o atributo
android : iayout_toRight0f="@id/iabeiusuario". Observe que o campo de texto utiliza
o atributo iayout_width="match_parent" para preencher o restante da tela.
3 O label Senha foi alinhado abaixo do campo Usurio, com o atributo
android : 1ayout_be1ow="@id/campoUsuario". Observe que o campo Usurio foi utilizado
como referncia, no o label Usurio. Isso foi feito porque a altura do campo e
maior do que a do label, sendo assim o campo Usurio era um ponto de referncia
melhor. Como fica a direita, foi necessrio alinhar o label Senha esquerda para
voltar margem da pgina. Isso foi feito com o atributo android:gravity="1eft".
4. O campo de texto Senha foi alinhado direita do label Senha.
5. O boto de Login foi inserido abaixo do cainpo de Senha, com o atri
buto android:iayout_beiow. Para alinhar a direita foi utilizado o atributo
android:iayout_aiignParentRight="true", relativ ao campo Senha. Para deixar
um pequeno espao (margem) entre a linha do campo Senha e o inicio
do boto, foi utilizado o atributo android:1ayout_narginTop="10dip" com o
valor de 1Odp. Tambm existem os atributos android:iayout_marginLeft e
android:iayout_narginRight com funcionamento semelhante. l
O ReiativeLayout bem flexvel ao construir as telas, permitindo que um compo
nente seja inserido, sempre relativo a outro j existente. Mas isso pode tambm
aumentar a complexidade da construo da tela, u ma vez que necessrio dominar
todos os atributos utilizados para controlar as posies em que os componentes
so inseridos. Uma desvantagem desse gerenciador de layout que, se a localiza
o de um componente for alterada, pode quebrar o layout de toda a tela pois U
posicionamento de um componente depende de outro 3 1
Captulo 6 I Interface grca - gerenciadores de layout 173
Esse layout adorado por alguns desenvolvedores e criticado por outros, ento ca
ber a voc decidir us-lo ou no. Saiba que o Google recomenda esse layout, pois
possvel criar com menos cdigo as mesmas telas que se fariam com muitas linhas
aninhando vrios LnearLayout com vertical e horizontal. Atualmente o editor visual
bem eciente ao fazer o drag and drop dos componentes e gerar automaticamente
todos esses atributos do Re1atveLayout; portanto, faa o teste no editor visual.

6.15 Absolutelayout (deprecated)


A classe androd.wdget.Abso1uteLayout permite controlar exatamente a posio
dos componentes, fornecendo suas coordenadas x e yr utilizando os atributos
androd:layout_x e androd: layout_y.

O AbsoluteLayout foi descontinuado (deprecated), pois o layout da tela pode car


diferente ou totalmente errado em dispositivos com diferentes resolues de tela.
Isso acontece porque 100 pixels em uma tela pode ser uma coisa, mas em outro
dispositivo pode ser outra, tudo depende da densidade e resoluo da tela de
cada dispositivo. Este breve tpico serve apenas para avis-lo para nunca utilizar
o Abso1uteLayout, portanto, nenhum exemplo ser demonstrado.

6.16 Utilizando layouts aninhados para criar telas complexas


Neste prximo exemplo vamos demonstrar como utilizar layouts aninhados.
Digamos que precisamos construir um formulrio, em que cada campo aparea
logo abaixo do outro na vertical. Contudo, na ltima linha necessrio exibir
dois botes: cancelar e login, mas eles precisam ser exibidos lado a lado.

/res/layout/exemplo_linear_layout_form_aninhado.xml
<?xnl verson="1.0" encoding="utf-8"?>
<LinearLayout xmlns:androd="http://schenas.android.con/apk/res/android"
android:orentation="vertica1"
android:layout_wdth="natch_parent" android:1ayout_heght="match_parent" >
<TetVew
android:layout_width="wrap_content" androd:layout_height="wrap_content"
androd:text="@strng/none" />
<EdtTet
android:layout_width="match_parent" android:1ayout_height="wrap_content"
android:textColor="#ff0000" androd:inputType="text"/>
<TetVew
174 Google Android - 4 edio
android:layoutmwidthz"wrapucontent" androd:layout_he9hf:"WfaD~Ctet"
androd:text="@strng/senha" />
<EdtTet androd:inputType="textPassword
androd:layoutwdthz"match_parent" android:layout_heght="WF8D_Ct@t" />
LinearLayout android:orentation="horizonta1"
androd:layout_wdth="match_parent androd:layout_h9h="Wf6D_C0@t"
androd:background="#cccccc" androd:gravty=center">
<Button androd:layout_wdth="wrap_content" androd:layout_height="wrap_content"
androd:text="@string/cancelar />
Button androd:1ayout_wdth="wrap_content" androd:layout_he9ht="WFD_C0"@"
androd:text="@strng/login" />
</LnearLayout
</LnearLayout>

A pre-visualizao dessc layout pode ser vista na figu ra (mil). () LinearLayout principal
da tela e vertical, mas no final foi utilizado um LinearLayout horizontal para comer
s dois lvotes. Observe que layut que contem os dois botes foi definido corn
uma cr de fundo cinza para demonstrar seu tamanho.

F@mu%-Umuuymtmmmr

6.17 Criao de um layout pela API - LinearLayout


(onformc ja estudamos, para criar telas n Android e possvel definir os layouts
em arquivos XMI., ou criar toda a tela usando a API ]ava, o que na maioria dos
cxtss |1 recxrrier1latlr

A seguir, sera fornecido urn exemplo de criacao de um formulrio utilizando a


Cl1SSU LHGBFLHYOU Smlllk' W111 il API Java. Para isso basta criar urna instancia
de LinearLayout e chamar o mtodo setContentVew(view). Assim como no XMI.. .io
criar o layout pela API tambm hrigat'rio definir os atributos layout width
(largura) e layout__heght (altura), que geralmente recebem os valores match oarent
Captulo 6 I Interface grca - gerenciadores de layout 175
e wrap_content. Esses parmetros podem ser informados utilizando a classe
android.widget.LinearLayout.LayoutParams, que por sua vez recebe no construtor as
constantes LayoutParams.MATCH_PARENT e LayoutParams.wRAP_CONTENT.

Para adicionar uma View dentro do layout, utilizado o mtodo addView(view), que
recebe uma subclasse de android.view.View, como Textview, EditText, Inageview, Button
etc. Para testar essa activity abra o projeto deste captulo no Android Studio e
execute no emulador.

ExempIoLinearLayoutAP|Activity.java
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
public class ExemploLinearLayoutAPIActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Cria o layout
LinearLayout layout = new LinearLayout(this);
layout.set0rientation(LinearLayout.VERTICAL);
layout.setLayoutParans(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
layout.setPadding(10, 10, 10, 1@); // Espaanento em pixels
TetView nome = new TextView(this);
nome.setTet("None:");
nome.setLayoutParams(new LayoutParams(LayoutParans.wRAP_CONTENT,
LayoutParams.wRAP_CONTENT));
layout.addView(nome);
EditTet tnome = new EditText(this);
tnone.setLayoutParams(new LayoutParans(LayoutParans.MATCH_PARENT,
LayoutParams.wRAP_CONTENT));
layout.addView(tnome);
// Focus
tnome.requestFocus();
Textview senha = new TextView(this);
senha.setTet("Senha:");
senha.setLayoutParams(new LayoutParams(LyoutParams.wRAP_CONTENT,
LayoutParams.wRAP_CONTENT));
layout.addView(senha);
EditTet tsenha = new EditText(this);
tsenha.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.wRAP_CONTENT));
layout.addView(tsenha);
176 Google Android - 4 edio
// Boto alinhado a direita
Button ok = new Button(this);
ok.setLayoutParams(new LayoutParams(LayoutParans.NRAP_CONTENT,
LayoutParams.NRAP_CONTENT));
ok.setGravity(Gravity.RIGHT);
ok.setText("0K");
layout.addView(ok);
// Informa o layout que foi criado pela API
setContentView(layout);
}

Como executar esse exemplo vou deixar por sua conta, pois a nica coisa que ele
faz criar um simples formulrio. Mas saiba que possvel criar todas as telas
utilizando somente a API: basta usar as subclasses de View desejadas. Os mtodos
das classes so semelhantes (quase sempre) aos atributos do XML. Por exemplo, a
classe Textview tem o mtodo setText(x), que corresponde ao atributo android : text do
XML. Fica a seu critrio escolher a maneira mais adequada para criar a interface
grca. Entretanto, criar o layout em XML a forma recomendada pelo Google,
pois separa a interface grca da lgica de negcios.

Nota: para testar os prximos exemplos, recomendo abrir o projeto deste


captulo no Android Studio e executar no emulador. A MainActivity desse projeto
vai mostrar uma lista com cada exemplo que vamos estudar.

6.18 Criao de um layout pela API - Tablelayout


O prximo exemplo de cdigo mostra como criar um layout pela API com a classe
TableLayout. Observe que para cada linha criado um TableRow.

ExemploTabIeLayoutAPIAttivity.java
inport android.widget.Tab1eLayout;
import android.widget.TableLayout.LayoutParans;
public class EemploTableLayoutAPIActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
Captulo 6 I Interface grca - gerenciadores de layout

// Cria o layout
TableLayout tabela = new TableLayout(this);
tabela.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// Expande a coluna 1
tabela.setColumnStretchable(1, true);
// Linha 1
TableRow linhal = new TableRow(this);
TetView nome = new TetView(this);
nome.setText("Nome:");
linha1.addView(nome);
EditTet tnome = new EditText(this);
// Focus no campo nome
tnome.requestFocus();
linha1.addView(tnome);
// Linha 2
TableRow linha2 = new TableRow(this);
Textview senha = new TetView(this);
senha.setTet("Senha:");
linha2.addView(senha);
EditTet tsenha = new EditTet(this);
tsenha.setTransformationMethod(new PasswordTransformationMethod());
linha2.addView(tsenha);
// Linha 3
TableRow linha3 = new TableRow(this);
linha3.setGravity(Gravity.RIGHT);
// Boto alinhado direita
Button ok = new Button(this);
ok.setText(" Login ");
linha3.addView(ok);
// Adiciona as linhas
tabela.addView(linha1);
tabela.addView(linha2);
tabela.addView(linha3);
// Informa o layout
setContentView(tabela);
}

Como exerccio, deixo para voc executar este exemplo no emulador.


178 Google Android - 4 edio
6.19 ScroIIView

Quando existem muitos elementos na tela, surge a fl eccssidade de fazer a rola


gem (scroll). Para isso, podemos utilizar a classe android.wid9@~5UuVleW~ A Classe
Scrollview deve conter apenas um componente-lho e conforme 0 seu tamanho ele
far a rolagem ou no. Portanto, deve-se adicionar um layOUI dentro do 5CfuV1@W
como por exemplo 0 LinearLayout, que por sua vez pode receber outros componentes.
A seguir demonstraremos a utilizao do Scrollviewz

/res/Iayout/actvity_exempIo_scroIIview.xm|
<ScrollView nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height:"wrap_content">
<LinearLayout android:id="@+id/layoutl"
android:layout_width="natch_parent" android:layout_height:"wrap_content"
android:orientation="vertical">

/LinearLayout
/ScrollView

Observe que o Scrollview contm um LinearLayout. Para simular a barra de rolagem,


ser criada uma activity que vai obter o LinearLayout denido pelo id layoutl e vai
adicionar dinamicamente pelo cdigo Java vrios Textview na tela. Como o LinearLayout
foi denido com 0 sentido vertical, cada Textview car embaixo do outro.

Exemp|oScroIIViewActivity.java
public class EenploScrollViewActivity extends Activity {
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getActionBar().setDisplayHoneAsUpEnabled(true);
setContentView(R.layout.activity_exenplo_scrollview);
LinearLayout layout = (LinearLayout) ndViewById(R.id.layout1);
for (int i = 0; i < 100; i++) {
Textview text = new TextView(this);
// obrigatrio o layout_width e layout_height
tet.setLayoutParams(new LayoutParams(LayoutParans.NRAP CONTENT,
LayoutParans.HRAP_CONTENT));
tet.setTet("Texto: " + i);
layout.addView(text);
}

}
Captulo 6 I Interface grca - gerenciadores de layout 179
A figura 6.20 mostra o resultado do exemplo, em que podemos ver que foi criada
a barra de rolagem devido grande lista de elementos adicionados no layout.
Esse exemplo tambm interessante, uma vez que mostra como recuperar o
LinearLayout do arquivo XML e adicionar vrias views dinamicamente pela API.
No prximo tpico vou explicar algo importante sobre a action bar e navegao
de telas, portanto preste ateno nestas duas guras. Voc deve ter percebido
que a primeira gura referente ao projeto de exemplo deste captulo, no qual a
ManActivty mostra todos os exemplos na lista.

Exemplos de layout Texto: 1


Texto: 2
Texto: 3
LinearLayout pela API
Texto: 4
| Texto: 5
TableLayout pela API remo: 6
Texto: 7
Texto: 8
Scrollview
Texto: 9

Gridview Textor 1 1 Texto: IO

Texto: T2

Gallery Texto: 14 Texto: 13

_ ex o 18
Texto: 15

Texto: 17

ImageSw|tcher Texto;TIQ
t
Texto; 20

Figura 6.20 - Exemplo de Scrolllew.

Dica: caso precise fazer a rolagem na horizontal, utilize o Horzonta1Scrol.1View.

6.20 Alguns detalhes sobre a ActionBar e o "up navigation"


Na gura do exemplo anterior, o ttulo da action bar da primeira tela o nome
do projeto, mas o ttulo da segunda activity Scrollvew.
Para entender a explicao deste cdigo, veja o cdigo-fonte do projeto de exemplo
deste captulo.

Isso acontece porque no AndroidManifest.xml eu cadastrei um label para a activitj e


esse label utilizado como o ttulo da action bar. Caso o label no esteja denido
utilizado o nome do projeto.
<activty android:name=".ExemploScro1lVewActvty" androd:label:"@string/scrollvew"
android:parentActvityName=".ManActvity"/>
180 Google Androld - 4' edio
, - - , - ' _ 'z f -` ` - L tie 3 ;
(lliscrve tambem que, ao declarar a activity, cu mloimci .t activity pt q c t
Haimictivity do projeto. lsso e leito cont a tag android:parentActivttYN3"\@=" -MWCVY
Por isso ao entrar no exemplo do Scrollvtew voc pode clicar no botao de P
uavigation`I que a sctinha para esquerda na action bar. e automaticamente o
sistema vai voltar para a tela anterior.
l.embra ndo que, conforme explicado no captulo 5, sobre action bar, para habilitar
o botao up navigation basta utilizar a seguinte linha de codigo:
getActionBar().setDisplayHomeAsUpEnabled(true);
// Ou com a biblioteca de compatibilidade v7
getSupportActtonBar().setDtsplayHomeAsUpEnabled(true);

6.21 Layoutlnater - inando arquivos XML


No exemplo anterior, vimos como adicionar views dinamicamente no Scrollview.
Para isso foi preciso instanciar no cdigo objetos do tipo Textview. Como j foi
explicado anteriormente, o recomendado sempre fazer o layout em XML. Ento
como fazer nos casos em que preciso criar views dinamicamente?
Para isso podemos criar um arquivo XML de layout com apenas a parte da view
que precisamos adicionar, que neste caso um Textview em cada linha do Scrollview.
Ento podemos ter um arquivo XML de layout como este:

/res/layout/inate_tetview.xmI
<TetView mlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textview"
android:layout_width="wrap_content" android:layout_hetght="wrap_content"
/
Note que este arquivo de layout contm o Textview que estvamos instancianclo
por programao no exemplo anterior. Desta vez esse mesmo Textview ser ins-
tanciado diretamente por meio do arquivo XML, procedimento que conhecido
pelos desenvolvedores Android com inflar um XMI lsso feito cont a classe
Layoutlnater, conforme demonstrado a seguir:

tia ExempIoScroIIVlewActivity.java

public class EemploScrollViewActtvlty extends Activity {


@0verride
protected void onCreate(Bundle ictcle) {
super.onCreate(icicle);
Captulo 6 I Interface grca - gerenciadores de layout 181
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_exemplo_scrollview);
Linearlayout layout = (LinearLayout) ndViewById(R.id.layout1);
for (int i = 0; i < 100; i++) {
// Cria o Textview inando o arquivo de layout, "ina o xml".
Layoutlnater inater = LayoutInater.from(this);
Textview text = (Textview) inater.inate(R.layout.inate_textview, layout, false);
// Agora basta usar a view inada normalmente.
tet.setTet("Teto: " + i);
layout.addView(text);
}

Nota: recomendvel no criar o layout no cdigo Java, mesmo que sejam


poucas partes. Se for necessrio criar um objeto do tipo View no cdigo, utilize
o Layoutlnater para inflar um arquivo XML. Lembre-se de que nem sempre a
view inada ser to simples quanto neste exemplo. No arquivo XML podemos
denir um layout bem complexo, o que justifica utilizar um arquivo XML para
manter o cdigo organizado. _
Os parmetros do mtodo inate(resource, root, attachToRoot) so:
Valor Descrio
resource Arquivo XML com o layout que precisa ser inado. O retorno ser
o objeto View desse layout.
root Layout container no qual esta view ser adicionada. Deve ser
informado para a view conhecer o tamanho e posicionamento do
layout-pai.
attachToRoot Flag que indica se a view deve ser adicionada no layout automatica
mente. E recomendado informar false e chamar o mtodo addView(view)
manualmente para adicionar a view no layout.

6.22 Links teis


Para complementar sua leitura, segue o link da documentao oficial:
Android API Guides - Layouts

http://developer android.com/guide/topics/ui/declaring-layout. html


*sx CAPTULO
Interface grca - View
.\`
\
1

A classe androd.view.Vew a classe-me de todos os componentes visuais do


Android. Este capitulo explica vrias de suas subclasses. No inicio, explicaremos
como trabalhar com recursos de texto, cores e imagens do Android. Depois,
demonstraremos diversos componentes disponveis na API para criar interfaces
visuais ricas.

Tambm aprenderemos a criar uma subclasse de View e desenhar com a API de


Canvas, alm de criar exemplos interessantes, como movimentar objetos e imagens
pela tela.

7.1 Arquivo /res/values/strings.xmI


O arquivo /res/values/stringsxml contm as mensagens de texto do projeto, para
fazer a internacionalizao. Um conceito importante sobre os arquivos XML com
os recursos do projeto que eles podem ter qualquer nome. Se voc quiser criar o
arquivo /res/values/mensagensxml em vez de /res/values/stringsxml, perfeitamente
possvel. O Android sabe o tipo do arquivo pelo seu contedo; assim, no caso de
arquivos de mensagens, eles devem sempre iniciar com a tag <resources e conter
vrias tags <strng> com a chave e o valor do texto.
A seguir, temos alguns textos que vamos utilizar nos prximos exemplos. Para conti
nuar, crie o projeto Demo-Views, ou se preferir abra o projeto de exemplo deste capitulo.

/res/values/strings.xmI
<?xm1 version="1.G" encodng="utf-8"?>
<resources
strng name:"app_name"LvroAndrodCap7-View</strng
<strng name="he11o_wor1d">He11o worId!</strng
<strng name="acton_settngs">Settngs</5trn9>

182
Captulo 7 nilnterface grca - View 133
<strng name "msg_verde_e_branco">Teto verde e branco </strng>
<string name "msg_azu1_e_branco">Texto azul e branco</strng>
<strng name "msg_verme1ho_e_branco">Teto vermelho e branco</strng>
<strng name "teto1">Teto campo 1 azul

Para acessar essas mensagens no cdigo Java, utilize a classe R com a sintaxe
R.strng.chave_mensagem.

Textvew t = (Textview) ndViewById(R.d.tet);


t.setTet(R.string.msg_verde_e_branco);

j no caso de um arquivo XML, utilize a sintaxe (dstrng/chave_mensagem.


<TextView androd:text="@string/msg_verde_e_branco" . . . />

Para traduzir os textos para diferentes idiomas, basta criar uma pasta /res/values
-idioma.

/res/values/values-pt/stringsxml
Caso queira deixar claro que esta pasta para o portugus do Brasil, adicione
a regio.
/res/values/values-pt-rBR/stringsxml

7.2 Arquivo XML com as cores


Da mesma forma que criamos um arquivo para conter as mensagens, podemos
criar um arquivo com as cores. Esse arquivo tambm pode ter qualquer nome, mas
deve ter um formato especco. Cada tag tem o nome da cor e o cdigo RGB.

/res/values/coIors.xmI
<?xml verson="1.0" encodng="utf-8"?>

<color name:"vermelho">#ff0000
<color name="azul">#00@0ff
<color name="verde">#00ff00
<color name:"branco">#ffffff

Se o cdigo RGB tiver seis caracteres, so as cores normais: vermelho, verde e


azul. Mas, se tiver oito caracteres, a primeira dupla referente transparncia.
Por exemplo, o cdigo a seguir define uma cor azul.
<color name="azul">#G900ff
184 Google Android - 4 edio
lim seguida, temos a cor azul com 50% dc transparencl

. . , . z - f . etCo1or cor _
<color name="azu1">#500000ff
Para utilizar uma cor no codigo, utilize o metodo getRe50UrC'S() 9 ( )
Textvew t = (Textview) ndVewById(R.d.tet);
t.setTextCo1or(getResources().getCo1or(R.co1or.branco));
t.setBackgroundCo1or(getResources().getColor(R.co1or.verde));
t.setTet(R.string.msg_verde_e_branco);

Para utilizar as cores no arquivo XML, utilize a sintaxe @co1or/Cor. Podemos vi


sualizar a seguir um Textvew que mostra como deixar o texto branco com a cor
de fundo verde.
<TextView android:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
android:text:"@string/msg_verde_e_branco"
androd:tetColor="@co1or/branco" androd:background="@co1or/verde" />

Observe que nesse exemplo utilizamos os atributos androidzbackground e


androidztexttolor do Textvew para denir a cor de fundo e a cor do texto. Para 0
valor da cor foi usado um recurso denido no arquivo /res/values/colors.xml, mas
tambm poderamos ter usado a cor em RGB diretamente no cdigo.
<TetView android:1ayout_wdth="match_parent" android:1ayout_height="wrap_content"
androd:text:"@string/msg_verde_e_branco"
androd:textCo1or="#ffffff" androd:background="#@0ff00" />

De qualquer forma, recomendado deixar as cores separadas nos arquivos de


recursos, pois dessa forma a cor pode ser alterada de forma centralizada, facili
tando a manuteno do cdigo.

7.3 Arquivo XML para criar um estilo (SS

''I..,
ja ouviu falar de CSS em pginas para internet? No Android, existe algo parecido e so
chamados de esnlos. Um tema que estudamos anteriormente um conjunto de estilos
Com um arquivo de estilo, podemos denir de uma s vez determinado padro
de cor, assim como o tipo da fonte como negrito e itlico Dessa forma possvel
dar um nome a esse estilo e usa-lo no codigo. Um arquivo de estilos tambm pode
ter qualquer nome, desde que seja formado pelas tags <sty1e name="nomeEst1o" c a
tag <1tem> para compor cada elemento.
Captulo 7 I *Interface grca - View 185
/res/values/css.xml
<?ml version="1.0" encoding="utf-8"?>

<style name="estiloEemplo">
<item name android:textSize">14sp
<item name android:textColor">#ffffff
<item name "android:background">#ff00G0
<item name android:textStyle">italic

Para referenciar o estilo no cdigo, utilize a classe R com a constante R . style.nome_estilo.


Textview t = (Textview) ndViewById(R.id.tet);
t.setTetAppearance(this, R.style.estiloExemplo);

Para referenciar o estilo no arquivo XML, utilize a sintaxe (style/nome_estilo.


<TextView android:text:"@string/msg_vermelho_e_branco" style:"@style/estiloExemplo" ... />

7.4 Exemplo completo com estilos


Agora que estudamos como criar arquivos com os recursos de texto, cores e esti
los, criaremos uma tela com trs campos de texto para demonstrar como utilizar
os recursos.

Se voc quiser, possvel criar uma activity com o seguinte cdigo para testar os
exemplos. No entanto, como vou utilizar os arquivos XML de layout para demons
trar, a prpria pr-visualizao do editor j suficiente para entender os exemplos.

ExemploTextoCoresActivity.java

public class ExemploTextoCoresActivity extends Activity {


@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_teto_cores);
}

A seguir, podemos visualizar o cdigo-fonte do arquivo de layout da activity


186 Google Android - 4 edio
=*=i~ /res/layout/activity, exemplo _teto.___tores.mI

`?ni version="1.0" encoding="utf-8"?>


<Linearlayout xmlns:android="http://schemas.android.com/apk/FGS/dfld"
android:iayout_width="match_parent" android:1ayout_height="matCh_DftH
android:orientation="verticai" android:padding="16dp" >

TetView android:iayout_width="match_parent" android:1ayout_height="wFD_C0"@"t"


android:text:"@string/msg_azui_e_branco"
android:background="@coior/azul" android:textCo1or="@coior/branco"
android:tetSty1e="bo1d" />
<TetView
android:iayout_width="match_parent" android:iayout_height="wrap_content"
android:layout_marginTop="10dp"
android:text:"@string/msg_verde_e_branco"
android:background="@coior/verde" android:tetCoior="@co1or/branco"
android:tetStyie="bo1d" />

TetView style:"@sty1e/estiloxempio"
android:iayout_width="match_parent" android:1ayout_height="wrap_content"
android:iayout_marginTop="10dp"
android:text:"@string/msg_verme1ho_e_branco" />
</LinearLayout

PJesse cochggi stiiinlizachas as snitaxes @string/chave_msg, @co1or/nome_cor e


@sty1e/nome_estiio para acessar as mensagens, cores e estilos definidos nos arquivos
de recursos. Na pr-visualizao do layout, podemos ver uma tela com trs textos
coloridos, conforme a gura 7.1. Devido impresso, no possvel visualizar
claramente as cores no livro; portanto, abra o arquivo no editor.

Figura Zi - Estilos 1']ont' e cor:

Nota: os estilos so utilizados em todos os componentes visuais (views) e nao


apenas para definir a cor dos textos. Mas isso e um pouco mais avanado e voc
ll 1'I'"*"lf mm "P<>- VOC se lembra dos temas Hoio e Material? Cada tema
e inn conjunto de estilos que e aplicado em cada view.
Captulo 7 I Interface grca - View 187
7.5 View - A classe responsvel por desenhar elementos na tela
A classe android.view.View utilizada como base para qualquer componente grfi
co, e toda subclasse de View precisa implementar o mtodo onDraw(Canvas canvas), o
qual responsvel por desenhar os elementos. Na lista a seguir, podemos verificar
alguns dos principais mtodos da classe View.
Atributo Descrio
requestFocus() Solicita o foco do componente. Para isso, os mtodos isFocusable()
ou isFocusableInTouchMode() precisam retornar true.
setPadding(esquerda cima, direita, baixo)
Informa o espao em pixels que deve ser inserido esquerda,
direita, acima e abaixo do componente antes de mostrar o seu
contedo. Isso muito utilizado em layouts que fazem padding
para dar uma pequena margem antes de inserir as views filhas.
O atributo correspondente no XML android : padding, o qual de
ne automaticamente o mesmo valor para todos os parmetros.
Caso seja necessrio, no XML, informar corretamente o padding
usado para a esquerda, direita, acima e abaixo da view podem
-se utilizar os atributos android:paddingLeft, android:paddingRight,
android : paddingTop e android : paddingBotton, respectivamente.
setVisibility(v) Pode receber trs valores, denidos pelas constantes View.VISIBLE,
View. INVISIBLE e View . GONE.Ambas as constantes INVISIBLE e GONE no
mostram a View na tela. A diferena que o INVISIBLE no mostra a
vievsg mas deixa o espao que ela ocuparia reservado (em branco).
J a constante GONE literalmente remove a view da tela. O atributo
no XML correspondente a esse mtodo o android:visibility, o
qual recebe os seguintes valores: visible, invisible e gone.
requestLayout() Solicita ao Android para refazer o layout da tela. Ser visto com
mais detalhes posteriormente, quando criarmos um exemplo
com uma.
invalidate() lnvalida a View, solicitando ao Android para desenhar a view
novamente. Ser visto com mais detalhes posteriormente quando
criarmos nossa prpria View customizada.
onSizeChanged(int largura, int altura, int larturaAntiga, int alguraAntiga)
Chamado pelo Android sempre que um componente altera seu
tamanho, informando as novas largura e altura, assim como os
valores antigos.
onDraw(Canvas) Mtodo responsvel por desenhar o componente na tela. Pode ser
implementado manualmente para controlar o que desenhado
na tela.
188 Google Android - 4 edio
Atnbuto Descaolrontj
onKeyDown(int keyCode, KeyEvent event) e onKeyu(t keyCode, KGYVH Vet)
Chamados quando o usurio pressiona uma tecla no caso de
dispositivos com teclado fsico. Voc pode ate sobrescrever esses
mtodos na sua classe de Activity para recuperar as teclas digi
tadas pelo usurio.
onTouchEvent(HotionEvent)
Chamado quando o usurio toca na view
Nos prximos tpicos, vamos estudar vrios tipos de views que podemos utilizar
para criar os layouts de tela no Android.

7.6 TextView e EditText - campo de texto para digitar informaes


A primeira e mais simples das subclasses de View 0 android.widget.TetVew, que
representa um texto/label. E a classe android.wdget.EditText uma subclasse de
Textvew utilizada para criar um campo de texto.
O EdtTet pode ser usado para entrada de texto normal, ou para aceitar apenas
nmeros ou campos de senha. A seguir, podemos visualizar um exemplo de um
formulrio de login, em que demonstrado como criar um campo de texto normal
para o login e um campo de senha.

/res/layout/exempIo_edittext.xmI
<?xn1 verson="1.G" encoding="utf-8"?
<Tab1eLayout xmlns:android="http://schenas.android.con/apk/res/android"
android:1ayout_width="natch_parent" android:1ayout_height="match_parent"
android:stretchCo1unns="1" android:padding="16dp">

<TetView android:text="@strng/usuario" android:padding="3dp" />


EdtText android:id="@+id/campoLogn" android:padding="3dip" android:nputType="text"/>

<TetView androd:tet="@strng/senha" />


<EdtText androd:d="@+id/canpoSenha" androd:inputType="textPassword" /
</TableRow
<Tab1eRow android:gravity="right">
<Button android:id="@+id/login" android:tet="@string/login" />
</Tab1eRow
Captulo 7 I Interface grca - View 189
O EditText precisa denir o tipo de entrada do texto. Isso feito pelo atri
buto android:inputType. O padro para texto normal android:inputType="text",
mas voc pode congurar para o modo de entrada de senha com o atributo
android:inputType="textPassword", ou android:inputType="number" para nmeros, dentre
outras conguraes.

7.7 AutoCompIeteTextView
A classe android.widget.AutoCompleteTetView um campo de texto que completa
automaticamente o texto que o usurio est digitando, e muito til em deter
minados casos. Existem dois parmetros que podem ser informados no momento
de criar essa classe.

android:conpletionThreshold - Nmero de letras que o usurio precisa digitar


para iniciar o autopreenchimento do texto.
android:completionHint - Texto utilizado para exibir uma dica sobre o preen
chimento do texto. O texto exibido na parte inferior do popup com as
opes quando este aberto.
Podemos visualizar a seguir um exemplo que cria um campo de texto que
vai sugerir os nomes dos estados do Brasil. Para preencher a lista, utiliza
da a classe android.widget.ArrayAdapter, que uma implementao da interface
android . widget. ListAdapter. Um adapter utilizado para fazer a ligao entre o contedo
e o componente, ou seja, ele fornece o contedo para preencher determinado compo
nente. Vamos estudar o conceito de adapters mais para frente; portanto, que tranquilo.

ExemploAutoCompleteTextViewActivity.java

public class EemploAutoCompleteTetViewActivity extends Activity {


private static nal String[] ESTADOS = new String[] { "Acre", "Alagoas", "Amap", "Amazonas",
"Bahia", "Cear", "...", "So Paulo", "Santa Catarina", "Sergipe","Tocantins" };
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_auto_complete_textview);
AutoCompleteTextView estados = (AutoCompleteTextView) ndViewById(R.id.estados);
// Adapter para preencher com os estados
ArrayAdapter adaptador = new ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, ESTADOS);
estados.setAdapter(adaptador);
}
no Google Android - 4* edio
zftesiltiytittt/titttvity, mniploh auto rmttplete tt~tviw.mI
`?\nI version-t.@ encodinqutf-8?~
`tineartavout \n\ns:android`http:/Jsehens.ndroid.ron/D\lf@S/df\"
androidziavout_uidthz*nateh_parent andrtdilavouthet0htHwr0Pe0\@'
ndratd:orientationverttial" android:paddtn\odD"
etextriew
android:lavoutUuidthw'wrao_content android:1avout1he\ghtwfP.\@
android:re\tv`stados` ;~
~AutoonplereIe\tview android:td2"@+\d/estados"
android:layouthuidthznatch_parent android:Iavoutfheight="wraP,f0\@"'
ndroid:conp\etionThreshld1`
andraidzao pIetionHint=Digite o none de um estado lt
Butten
android:Iayout1width=wrap_eontent android:leyoutHheight="wrap,content
android:text=0k /
</Lineartayout

~
Nesse exemplo. o atributo attdroidzconplettonthreshold toi dettido mm o valor I.
indicando que o popup mm o autopteetteltiatento deve ser aberto quando o usa
ario digitar a primeira letra. U atributo androidzcompletionint delitte .t tttensagettt
que apate\\~ tia parte mt~z~o~ do popitix .~\ ligura 7.2 exibe o resultado. Como lot
digitado o texto l`ar` os estados Para. Paraiba e i`arati.i tiiram sugeridox
1a301
.QfW*a@
_,._~1.; \ :. zzt- _.'5.~~.zzwz.zirz:t@:zi;t~
ti .z'.~,f~,z'-.;,,_ t z -v..r .., z~-. -. :

....'3'
" _*
' 4_I
_.~_.z
_ `L * *f
-. . *L-~
* `N" \'Vll'*
,. ' 'z i, Six*
rz~.iii,
-.z; 5 -~.\. ..
;_-'*':: 5g"
' :g1Z
__..._ _.va ' V r -.,_. z

HKINU -3 `\`f`"|{'U da rlassc .-\utot `ontI^ti^ 2.\`t\`i'it:

os exemplos. `
pari! nvlhf UHPHhu\`l\\ da `\x(\`*utt\ `\ \\'i(\\ dv (\\(\`l\ln
deste capitulo no etnttlador. A aetwity ittieial eotttettt tuna Iist=wz.~tii ootit todos
Captulo 7 I Interface grca - View 191
7.8 Button e ImageButton
As classes android.widget.Button e android.widget.InageButton so utilizadas para criar
um boto na tela. A diferena que a classe InageButton permite usar uma imagem
para desenhar o boto. O exemplo a seguir utiliza a classe ImageButton, que exibe
um alerta quando o boto clicado.

ExempIoImageButtonActivity.java
public class ExemploInageButtonActivity extends Activity {
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exenplo_inage_button);
ImageButton botaolmageml = (InageButton) ndViewById(R.id.ing1);
nal Context context = this;
botaolmageml.set0nClickListener(new View.0nClickListener() {
public void onClick(View v) {
Toast.nakeTet(context, "Imagem 1 OK", Toast.LENGTH_SHORT).show();
}

});
InageButton botaoImagem2 = (ImageButton) ndViewById(R.id.img2);
botaoInagem2.setImageResource(R.drawable.snile2);
botaoImagem2.set0nClickListener(new View.0nClickListener() {
public void onClick(View v) {
Toast.makeText(contet, "Imagem 2 OK", Toast.LENGTH_SHORT).show();
}

});
}

/res/Iayout/activity_exempIo_imagem_button.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xnlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<InageButton android:id="@+id/ingl" android:layout_width="natch_parent"
android:layout_height="wrap_content" android:src="@drawable/snilel" />
<ImageButton android:id="@+id/ing2" android:layout_width="wrap_content"
android:layout_height="wrap_content" />
192 Google Android - 4 edio
Observe que a primeira imagem denida diretamente no QYQUIVO de layout COm
o atributo androd:src="@drawab1e/sm1e1". Entretanto, a segunda imagem e definida
dinamicamente no cdigo.
botaoImagem2 . setImageResource(R.drawab1e. smi1e2);

Os mtodos que podem ser utilizados para alterar a imagem dinamicamente


podem ser visualizados na lista a seguir. Esses mtodos esto denidos na classe
Imagevew, a superclasse de ImageButton.
Mtodo Descriao
'` g_ gw V, g
setImageBitmap(btmap) Recebe um androd.graphcs.Btmap para exibir a imagem.
setImageResource(id) Recebe o id da imagem utilizando a constante R .drawab1e. imagem.
setImageURI(ur) Recebe uma URI para exibir a imagem. Uma URI criada
com o mtodo Uri . parse(strng) e pode representar o caminho
para um arquivo ou um link para uma imagem na web.
A gura 73 demonstra o resultado deste exemplo executando no emulador. Ob
serve que a primeira imagem ocupou a largura da tela inteira, porque sua largura
foi denida como match_parent.

Figura Z3 - Exemplo de ImageButton.


Outro conceito importante sobre botes so os s eletores de estado (selectors),
utilizados para denir uma imagem diferente dependendo do estado do boto,
como, por exemplo, se o boto est clicado ou no. Par a isso, voc deve inserir
na Pasta /'65/dmwabla Um arquivo XML com a tag com cada estado da
imagem. Esse arquivo agrupa um con`unr0 d
J e imagens, e ser transformado em
uma imagem pelo compilador.
Captulo 7 I Interface grca - View 193
/res/drawable/exempIo_seIetores.xm|
<?xml verson="1.@" encoding="utf-8"?>
<selector xmlnszandrod="http://schemas.androd.com/apk/res/androd">

<tem androd:state_pressed="true" androd:drawab1e="@drawable/button_pressed" />

<tem androd:state_focused="true" android:drawable="@drawable/button_focused" />

<tem androd:state_hovered="true" androd:drawab1e="@drawable/button_focused" /


<tem androd:drawab1e="@drawab1e/button_norma1" />

O arquivo XML dene uma imagem para cada tipo de estado do boto. Portanto,
voc precisa pedir aos designers para criar essas imagens, ou pelo menos duas:
normal e selecionado. Com esse arquivo de seletores (selectors) em mos, basta
utiliz-lo como a imagem de fundo para o boto. Na prtica, o arquivo XML
vai virar uma imagem e voc pode utilizar as notaes @drawab1e e R.drawable para
acessar esse recurso.

<Button androd:layout_heght="wrap_content" androd:1ayout_wdth="wrap_content"


androd:background="@drawab1e/exemp1o_se1etores" />

7.9 CheckBox e ToggIeButton


Um checkbox pode ser criado no Android com a classe androd .wdget.CheckBo, que
pode ser inserida facilmente em um arquivo de layout conforme o exemplo a seguir.
<CheckBo androd:d="@+d/checkReceberEmal"
androd:layout_wdth="wrap_content" androd:layout_heght="wrap_content"
android:tet="Receber email" />

Para verificar se o checkbox est marcado ou no, utilize o mtodo sChecked().


CheckBox check = (CheckBox) ndViewById(R.d.checkReceberEma1);
boolean receberEmal = check.isChecked();

Outra classe que pode ser utilizada para selecionar uma opo, similar ao
checkbox, a androd.wdget.ToggleButton.

<ToggleButton androd:id="@+id/toggle"
androd:layout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
android:textOn="Lgado"'android:tet0ff="Desligado" />
194 Google Android - 4 edio
Essa classe tambm contm o mtodo isChecked(). Com OS 8ITbUf05 '3dfd5tet9"
recem no boto quando ele
e androidztextff, possvel controlar os textos que apa I d 1
est selecionado ou no. A seguir, podemos visualizar um exemp o as C asses
CheckBo e ToogleButton.

/res/layout/activity_exempIo_toogIe_button.xmI
?nl version="1.6" encoding="utf-8"?
<LinearLayout mlns:android="http://schemas.android.com/apk/rES/dfd"
android:layout_width="match_parent" android:layout_height="matCh_P3rent"
android:orientation="vertical">
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Exemplo de CheckBox e ToggleButton" />
<CheckBo android:id="@+id/check1"
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Check 1" />
<CheckBo android:id="@+id/check2"
style="?android:attr/starStyle"
II
android:layout_width="wrap_content" android:layout_height="wrap_content
android:text="Check 2" /
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="0 ToggleButton mostra os textos Ligado ou Desligado..." /
<ToggleButton android:id="@+id/toggle"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet0n="Ligado" android:tet0ff="Desligado" />
<Button android:id="@+id/bt0K"
android:layout_width="wrap_content" android:layout_height=wrap_content"
android:tet="0K />
</LinearLayout

ExempIoToggIeBut1onActivity.java

public class ExemploToggleButtonActivity extends Activity {


@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_toogle_button);
nal ToggleButton toggle = (ToggleButton) ndViewById(R.id.toggle);
Button b = (Button) ndViewById(R.id.bt0K);
b.set0nClickListener(new View.0nClickListener() {
public void onClick(View v) {
Captulo 7 I Interface grca - View 195
boolean selecionado = toggle.isChecked();
Toast.makeText(Eemp1oToggleButton.this, "Selecionadoz " + selecionado,
Toast.LENGTH_SHORT).show();
}

});
}

A figura 7.4 demonstra o resultado desse exemplo. Observe que o segundo


checkbox est parecido com uma estrela porque foi definido um estilo customi
zado com o atributo style="?android:attr/starSty1e".
Esse um exemplo legal para explicar algo importante do Material Design: como
as cores so aplicadas no tema. Se voc executar o projeto de exemplo no emulador,
ver que a cor da action bar azul e a cor dos componentes vermelha. Por exemplo,
ao marcar o checkbox, a cor da seleo ca vermelha. Isso configurado de forma
simples no arquivo /res/values~1/21/styles.xml, que sobrescreve algumas propriedades
de cores. Veja que o projeto Demo-Views no utiliza a biblioteca de compatibilidade,
e por isso o tema Material foi congurado apenas para API Level 21 ou superior.

Exemplo de CheckBo e ToggIeButton


Check 1
Check 2
O ToggleButton mostra os textos Ligado ou
q Desligado...
i

' Desligadb

'mc
l

Figura 24 - Exemplo de ChecleBox e ToggleButton.

/res/values-v21/styIes.xmI
<?xml version="1.0" encoding="utf-8"?>

<style name="AppTheme" parent="android:Theme.Material.Light">

iten name="android:co1orPrinary">@co1or/primary
196 Google Android - 4 edio
_ _ . . . extual a bars -->
<!~- Vartaao escura da cor primaria para a status bar e cont PP
<iten nane="android:colorPrinaryDark"@color/prn8fY-fk<it">

<color nane="prinary">#63A9F4
<color name="prnary_dark">#61579B/color>~
<color nane="accent"#F44336 <!~- Vermelho -->

<color name="vermelho">#ff00G0
<color name="azul">#0@09ff
<color name:"verde">#00ff09
<color name="branco">#ffffff

Nota: no captulo 11, sobre Material Design, vamos voltar a estudar essas cores.
De qualquer forma, importante voc entender que a cor primria (primary)
representa a cor da marca do cliente ou do seu aplicativo, e a cor de acentuao
(accent) utilizada para destacar as views e determinados componentes na tela.

7.10 RadioButton

O componente radio button permite selecionar apenas uma nica opo de uma
lista. No Android, as classes androld.wdget.Radoroup e androd.wdget.RadioButton
so utilizadas para isso. A classe RadoGroup dene o grupo que contm a lista de
opes, na qual cada opo representada por um RadoButton.
O exemplo a seguir cria dois botes com os textos Sim e No, respectivamente:
<RadioGroup androd:layout_width="match_parent"
BHFO113)/U_h@9ht="W`6D_Content" androd:orientation="horizontal"
Captulo 7 n Interface grca - View 197
androd:d="@+d/group1">
<RadoButton androd:d="@+d/radoSm"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="Sm" androd:checked="fa1se" />
<RadoButton androd:d="@+d/radioNao"
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:tet="No" androd:checked="fa1se"/>
</RadoGroup>

Observe que cada RadoButton tem um id, para que posteriormente o id do boto
selecionado possa ser recuperado, chamando o mtodo getCheckedRadoButtonId()
da classe Radoroup. Nesse exemplo, foram denidos os ids radoSim e radoNao. Isso
possibilita utilizar o seguinte cdigo para descobrir qual boto foi selecionado:
boolean sim = R.d.radoSm == group.getCheckedRadoButtonId();

Dessa forma, o id do RadoButton selecionado est sendo comparado com um id


conhecido que foi denido no arquivo XML de layout. Para demonstrar o uso do
RadoButton e do CheckBox, criaremos um exemplo de um formulrio.

/res/Iayout/activity_exempIo_check_radio_form.xmI
<?xml verson="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:orientaton="vertca1">
<TetVew
android:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content
androd:text="Nome" />
<EdtText androd:d="@+id/tetNome"
androd:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content" />
<TextView
androd:1ayout_wdth="wrap_content" androd:1ayout_height="wrap_content"
android:tet="Concorda?" />
<RadoGroup androd:1ayout_width="match_parent"
android:iayout_heght="wrap_content" androd:orentaton="horzonta1"
androd:d="@+d/group1">
<RadioButton androd:d="@+d/radoSim"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content
androd:text="Sm"
androd:checked="fa1se" />
<RadioButton android:d="@+d/radioNao"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
android:tet="No"
androd:checked="fa1se"/>
198 Google Android - 4 edio

<TetView
android:layout_width="wrap_content" android:layout_height="wFD_C"t@"t"
android:tet="Receber Email ?" />
<CheckBo android:id:"@+id/checkReceberEmail" H
android:layout_width="wrap_content" android:layout_height= wFD_Cf9"t
android:tet="Receber email" />
<Button android:id="@+id/buttonEnviar"
android:layout_width="wrap_content" android:layout_height="wrP_C0t@"t
android:tet="Enviar" />

O seguinte cdigo mostra como vericar se o checkbox e radio button esto


selecionados:

ExemploCheckRadioFormActivity.java
public class EemploCheckRadioFormActivity extends Activity {
private static nal String TAG = "livro";
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eenplo_check_radio_form);
nal EditText tetNone = (EditTet) ndViewById(R.id.tetNone);
nal RadioGroup group = (RadioGroup) ndViewById(R.id.group1);
group.set0nCheckedChangeListener(new Radioroup.0nCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedld) {
boolean sim = R.id.radioSim == checkedldg
boolean nao = R.id.radioNao == checkedld;
if (sin) {
Log.i(TAG, "Marcou radio Sim: " + checkedld)
} else if (nao) {
Log.i(TAG, "Marcou radio No: " + checkedld)
}

});
nal CheckBo check = (CheckBo) ndViewById(R.id.checkReceberEnail);
// Dene um listener para executar quando alterar o check
check.set0nCheckedChangeListener(new CheckBo.0nCheckedChangeListener() {
public void onCheckedChanged(ConpoundButton buttonview, boolean isChecked) {
Log.i(TAG, "checkz " + isChecked);
1

});
Capitulo 7 I Interface grca - View 199
Button b = (Button) ndViewById(R.id.buttonEnviar);
b.set0nClickListener(new 0nC1ickListener() {
public void onC1ick(View v) {
Log.i(TAG, "0K");
// Compara o id do radioSin
booiean concorda = R.id.radioSim == group.getCheckedRadioButtonId();
boolean receberEmai1 = check.isChecked();
StringBuffer sb = new StringBuffer();
sb.append("Nome: ").append(tetNome.getText())
.append("\nReceber Email: ").append(receberEmai1 ? "Sim" : "No")
.append("\nConcorda: ").append(concorda ? "Sim" : "No");
Toast.makeTet(Exemp1oCheckRadio.this, sb.toString(), Toast.LENGTH_SHORT).show();
}

});
}

Observe que ambas as classes CheckBo e Radioroup contm o mtodo


setOnCheckedChangeListener(listener) para congurar o listener que deve ser executado
quando o estado do checkbox ou radio for alterado. Porm, como demonstrado
no exemplo, os mtodos recebem diferentes interfaces (tm os mesmos nomes,
mas esto em pacotes diferentes). Ao executar esse exemplo, podemos visualizar o
formulrio, conforme mostra a gura 15. Ao preencher o formulrio e pressionar
o boto, um alerta com as opes selecionadas exibido.

NOITIE

__ .,
Concorda?
{ ,,.,i Sim ,; No
Receber Email ?
[ Receber email

Enviar

Figura Z5 - Exemplo de CheclzBox e RadioButton.


200 Google Android - 4 edio
7.11 Spinner
A classe android.widget.Spinner e utilizada paracombo com. opes na tela.
criar um
Para denir a lista que deve ser exibida no combo, usada uma implementao
de android.widget.SpinnerAdapter que herda de android.widget.AdaDt@f

A seguir, podemos visualizar um exemplo que cria um combo na tela que lista
os nomes dos planetas do sistema solar. Ao selecionar algum plafl, sua foto e
exibida na tela.

i /res/Iayout/activity_eempIo_spinner.xmI
<?ml version="1.0" encoding="utf-8"?>
LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wFD_C"@"t"
android:orientation="vertical">
<TextView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Selecione una opo" />
<Spinner android:id="@+id/conboPlanetas"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:drawSelector0nTop="true"
android:prompt:"@string/teto_combo" />
<InageView android:id="@+id/img"
android:layout_width="match_parent" android:layout_height="match_parent" />

ExempIoSpinnerActivity.java
public class EemploSpinnerActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus, R.drawable.terra,
R.drawable.marte, R.drawable.jupiter, R.drawable.saturno, R.drawable.urano,
R.drawable.netuno, R.drawable.plutao };
private String[] planetas = new String[] { "Mercrio", "Vnus", "Terra", "Marte",
"Jpiter", "Saturno", "Urano", "Netuno", "Pluto"};
@0verride \
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eemplo_spinner);
nal Inageview imagem = (ImageView) ndViewById(R.id.img);
nal Spinner combo = (Spinner) ndViewById(R.id.conboPlanetas);
ArrayAdapter adaptador = new ArrayAdapter<String(this,
android.R.layout.sinple_spinner_iten, planetas);
Captulo 7 I Interface grca - View 201
adaptador.setDropDownViewResource(android.R.layout.sinple_spinner_iten);
combo.setAdapter(adaptador);
// Se selecionar algun planeta atualiza a imagem
combo.set0nItenSelectedListener(new Adapterview.0nItemSelectedListener() {
@0verride
public void onItemSelected(AdapterView<?> parent, View v, int posicao, long id) {
// Atualiza a imagem
imagem.setInageResource(imagens[posicao]);
}

@0verride
public void onNothingSelected(AdapterView<?> parent) { }
});
}

Se for necessrio obter o item selecionado, utilize algum dos seguintes mtodos:
Mtodo Descrio
Object getSelectedItem() Retorna o item selecionado.
long getSelectedIternId() Retorna o id do item selecionado.
int getSelectedItenPosition() Retorna a posio do item selecionado. Essa posio
equivalente ao array fornecido para o spinner.
Nesse exemplo, preferimos monitorar o estado do Spinner em tempo de execuo.
Para isso, implementamos a interface Adapterview.OnItemSelectedListener e o mtodo
onItenSelected(parent,view,posicao,id), com o objetivo de atualizar a imagem auto
maticamente quando um planeta for selecionado. A gura 16 mostra o planeta
Terra selecionado no combo.

, . Terra
Selecione uma opo Selecione uma opo
' Mercuno gf
.
` Vnus
fe -;z . ,. .V W~ . ,um ~-_. 7_,, _.,x __y_ a , I,
$?* : ff?? ,..1 f.. ;.. 513
- f ~=. .~ =,=;:-";,_,..u.;s i. fz z,'.^z..;<1g z, z== ;. _z
zk
i z z f. pq, 4-3;z-
z-V Yzzzz .^z,=>-
;o....: .ez-^_zM=z^~.
:jaz .- zf -~ _,y,.
~ .rj:._fe%*3zf:i.:sfz- ';g;.*?i*z+2'^.r'E; 7:

Marte

,.
Jupiter

1 SBUITK)

UFGTID "__ Nr,

Figura 76 - Planeta Terra selecionado.


202 Google Android - 4 edio
Observe que foi utilizado o recurso nativo android.R.1By0U-Sl'lDl@_5P1""@"-1te"l Pam
criar o drop-down com a lista de planetas.
adaptador.setDropDownViewResource(android.R.layout.simple_SP@f_);

Voc pode criar seu prprio recurso XML se desejar customizar a interface,
ou at utilizar outro padro da plataforma com o 0 recurso android.R.layout.
simple_spinner_dropdown_iten. Altere o exemplo para esse cdigo e conra o resultado.
adaptador . setDropDownViewResource( android . R . layout . simple_spinner_droDdW"_t@f'\);

Nota: a classe android.R acessa os recursos nativos do Android. Cada projeto


contm sua prpria classe R com o seu pacote especco; portanto, sempre preste
ateno ao fazer os imports.

7.12 ProgressDialog -janela de progresso


Voc j deve ter visto aquela mensagem de Por favor, aguardem em alguma aplicao
que faz algum processamento demorado. No Android, existe uma classe especial
justamente para exibir uma janela na tela com uma mensagem para aguardar. Para
isso, usaremos a classe android.app.ProgressDialog, lha da classe android.app.Dialog.

A seguir, podemos ver um exemplo muito simples de como usar a classe Prog ressDialog.
ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("Eemplo");
dialog.setMessage("Buscando imagem, por aguarde...");
dialog.setIndeterninate(true); // Indica para executar por tempo indeterminado
dialog.setCancelable(true); // Se pode cancelar caso pressione o voltar
dialog.show();

Esse cdigo exibir uma janela de progresso com a mensagem informada. O pr


prio Progressialog j exibe uma animao com uma imagem girando para encher
os olhos do usurio. A janela car aberta por um tempo indeterminado at que
O ITIOCO diSriSS(> Seja chamado para encerr-la. Um atalho para essas linhas
simplesmente chamar o mtodo esttico show(), conforme demonstrado a seguir.
que faz a mesma coisa que o exemplo anterior.
ProgressDialog dialog = ProgressDialog.show(this,"Exemplo" D

"Buscando imagem, por favor, aguarde...", false,true);

Essa janela de progresso extremamente til em aplicaes que acessam a in


ternet Para buSf1f0fma;0es,nas quais o tempo de resposta indeterminad0~
Captulo 7 I Interface grca - View 203
Demonstraremos um exemplo que faz o download de uma imagem da internet.
Enquanto o download est em andamento, um alerta de progresso exibido na
tela. O arquivo de layout a seguir contm apenas a imagem que ser atualizada
depois que o download terminar.

/res/layout/activity__exempIo_progress_diaIog.xmI
<?ml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<ImageView android:id="@+id/img" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="tCenter" />

ExempIoProgressDiaIogActivty.java
public class EemploProgressDialogActivity extends Activity {
private static nal String URL = "http://livroandroid.com.br/imgs/livro_android.png";
private Progressbialog dialog;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_progress_dialog);
// Abre a janela com a barra de progresso
dialog = ProgressDialog.show(this,"Exemplo",
"Buscando imagem, por favor, aguarde...", false,true);
downloadImagem(URL);
}

// Faz o download da imagem em uma nova thread


private void downloadImagem(nal String urlImg) {
new Thread() {
@0verride
public void run() {
try {
// Faz o download da imagem
URL url = new URL(urlImg);
Inputtream in = url.openStream();
// Converte a InputStream do Java para Bitmap
nal Bitmap imagem = BitmapFactory.decodeStream(in);
in.close();
// Atualiza a tela
204 Google Android - 4 edio
atualizaInagen(inagen);
} catch (IOEception e) {
// Una aplicao real deveria tratar este err0
Log.e("Erro ao fazer o download: ", E-9tM5S99()@)
}

}.start();
}

private void atualizaImagem(nal Bitmap imagem) {


run0nUiThread(new Runnable() { // Este cdigo necessrio, pois foi aberta uma thread
@0verride
public void run() {
// Fecha a janela de progresso
dialog.disniss();
Inageview ingview = (InageView) ndViewById(R.id.ing);
ingview . setInageBitnap(inagen) ;
}} );
}

Para o exemplo funcionar, declare a permisso INTERNET no arquivo AndroidManifest.xml.

AndrodManifest.xmI
<manifest . . . />
uses-permission android:nane="android.pernission.INTERNET" />
<application ... />
</manifest

A gura Z7 mostra a janela de progresso aberta enquanto a aplicao est fazendo


o download da imagem em segundo plano. Depois que o download concludo,
o mtodo disniss() da classe ProgressDialog chamado para fechar a janela e a
imagem exibida.
A parte do cdigo que faz o ProgressDialog teoricamente simples, mas esse exempl0
mostrou dois problemas que voc vai encontrar ao criar qualquer funcionalidade
que acesse a internet.
1. obrigatrio iniciar uma thread para fazer qualquer operao de I/O, como
acessar a internet, ler arquivos e consultar banco de dados, ou qualquer ope
rao demorada. Se a thread no for criada, dependendo do caso, o Android
pode lanar uma exceo ou a aplicao pode mostrar o erro ANR (Android
Not RSP0dig). Para mais detalhes, consulte o captulo 10, sobre threads.
Captulo 7 I Interface grca - View 205
2. No Android, cada aplicao executa em um nico processo, e cada processo
contm uma thread dedicada. Essa thread tambm responsvel por desenhar
e tratar todos os eventos da interface grca, conhecida popularmente como
UI Thread ou Main Thread. Existe uma regra no Android que diz: somente a
UI Thread pode atualizar a interface, ou seja, somente ela pode chamar qual
quer mtodo que vai atualizar uma view Por isso, neste exemplo foi utilizado
o mtodo run0nUiThread(runnab1e) para sincronizar o cdigo que vai atualizar
a view com a UI Thread. Na prtica o mtodo runOnUiThread(runnab1e) faz com
que o Runnable passado como parmetro execute na UI Thread. Se voc no
zer isso a aplicao vai lanar uma exceo. Para mais detalhes sobre esse
assunto, consulte o captulo 10, sobre a classe Handler.

., . l V

A
v.

Goo Ie
Aprenda a criar aplicaes para dispositivos mveis 5
com o Android SDK `

H OVGGC Ricardo R. Lncheta

Figura Z7 - Exemplo de Pr0gressDialog.

7.13 ProgressBar - barra de progresso


A classe android.wdget.ProgressBar utilizada para exibir uma barra de progresso
na tela, que pode durar por tempo indeterminado ou no. Com uma barra de
progresso, possvel incrementar o valor da barra, para ela ser preenchida aos
poucos, medida que o processamento vai terminando. Dessa forma, possvel
dar ao usurio uma ideia de quando determinada tarefa ser concluda.
A seguir, veremos um exemplo que utiliza a classe ProgressBar.
206 Google Android - 4 edio
Qi /res/layout/activity__exempIo_progressbar.mI
?ml version="1.B" encoding="utf-8"?>
<LinearLayout mlns:android="http://schemas.android.com/apk/FGS/3"dfd"
android:layout_width="match_parent" android:layout_height="wF6P_C"tet"
android:orientation="vertical" android:padding="16dp">
<TetView android:tet="Barra de Progresso"
android:layout_width="wrap_content" android:layout_height="wFD_C"t@t" />
ProgressBar android:id="@+id/barraProgresso"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent" android:layout_height="wfD_C0"@t"
android:nax="100" />
<Button android:id="@+id/bt0K" android:tet="Simular Tarefa"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout

Nota: o atributo style="?android:attr/progressBarStyleHorizontal" no ProgressBar aplica


o estilo da barra de progresso. Caso esse estilo no seja adicionado, o ProgressBar
vai exibir uma animao de uma bolinha girando.

Observe que o atributo android:ma="100" dene o valor mximo da barra de pro


gresso. Por isso, no cdigo demonstrado a seguir, o valor ser incrementado de
O a 100. Para atualizar a barra de progresso, usado o mtodo setProgress(valor).

ExempIoProgressBarActivity.java
public class EemploProgressBarActivity extends Activity {
private static nal String TAG = "livro";
private ProgressBar mProgress;
@0verride
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_progressbar);
// Barra de Progresso
nProgress = (ProgressBar) ndViewById(R.ld.barraProgresso);
Button b = (Button) ndViewById(R.id.bt0K);
b.set0nClickListener(new Button.0nClickListener() {
@0verride
public void onClick(View view) {
new Thread(new Runnable() {
public void run() {
for (int i = 0; i <= 100; ++) {
}

l
Captulo 7 I Interface grca - View 207

}});

i rl
}
}

}).start();
nal int progress = i;
// Atualiza a barra de progresso
run0nUiThread(new Runnable() {

});
try {
public void run() {

}
Log.d(TAG,">> Progress: " + progress);
mProgress.setProgress(progress);

Thread.sleep(200);
} catch (InterruptedEception e) {}

Log.d(TAG,"Fim.");

Nesse cdigo, uma thread criada para simular o processamento, e para demo
rar um pouco foi feito um Thread.sleep(200), que coloca a thread para dormir por
ZOO milissegundos a cada iterao. A gura 728 mostra o resultado com a barra
de progresso sendo atualizada. Lembre-se de que, se voc executar o projeto de
exemplo deste captulo no emulador, a barra de progresso ser vermelha, que foi

r
mag;is31zz l r
a cor definida para "accent color" do tema Material.

Progressor Progresslar
1.rV._i
Barra de Progresso Barra de Progresso l
s

r Simular Tarefa Simular Tarefa ;


1

J -_ ssss_ suuWsu,__-_______. _u_i

Figura Z8 - Exemplo de ProgressBa1:


4

4
208 Google Android - 4' edio
7.14 Toast - alertas rpidos
A classe androd.wdget.Toast utilizada para inostrarlcrtas
a S Para o usurio. Cada
alerta pode ser visualizado na tela por um tempo curto, especificado pela constante
Toast.LENGTH_SHORT, ou por um tempo longo se utilizar a constante Toast.LENGTH_LONG.
A forma mais simples de criar um alerta com o m todo Toast .makeTet(conteto,
mensagem, tempo):

Toast toast = Toast.makeText(ths, "Teste de Mensagem", Toast.LENGTH_5H0RT);


toast.show();

Na maioria dos casos, somente o mtodo anteriorj suciente para exibir men
sagens de alerta para o usurio. Entretanto, pode ser que seja necessrio exibir
um alerta com uma interface mais complexa. Para isso, a classe Toast contm o
mtodo setVew(vew), o qual pode ser chamado para congurar a View que ser
exibida no alerta, que pode ser simplesmente uma imagem definida pela classe
Imagevew ou mesmo uma tela com um layout complexo.
Por exemplo, para criar um alerta que no lugar de algum texto exiba uma imagem,
poderamos utilizar o seguinte trecho de cdigo:
Imagevew imagen = new ImageVew(ths);
imagen.setImageResource(R.drawab1e.smile);
Toast toast = new Toast(this);
toast.setVew(magen);
toast.setDuraton(Toast.LENGTH_LONG);
toast.show();

Observe que o mtodo setVew(vew) pode receber qualquer subclasse de View,


inclusive um gerenciador de layout, o que permite exibir um alerta com uma
interface bem customizada.

Nota: um toast aquele alerta rpido muito conhecido por desenvolvedores e


usurios do Android. Esse alerta no tem vnculo com o que foi ou est sendo
executado, de forma que o usurio pode continuar fazendo o que quiser no
celular. Por exemplo, no aplicativo do Gmail, logo depois de enviar um mal` um
toast mostra uma mensagem de que o email est sendo enviado. Mas voc pode
continuar usando o celular normalmente, pois o toast apenas uma mensagem
de informao sem estado.
Captulo 7 I Interface grca - View 209
7.15 AlertDialog - alertas para o usurio conrmar
A classe Toast que estudamos anteriormente mostra uma mensagem temporria
com a qual o usurio no pode interagir. Se for necessrio que o usurio pressione
no boto OK do alerta, ou responda uma pergunta do tipo Sim ou No, podemos
utilizar a classe AlertDialog.
Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.ic_launcher);
builder.setTitle("Titulo");
builder.setMessage("Mensagem");
builder.setPositiveButton("Sim", new Dialoglnterface.0nClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeTet(getBaseContet(), "Clicou em Siml", Toast.LENGTH_SHORT).show();
return;
}});
builder.setNegativeButton("No", new Dialoglnterface.0nClickListener() {
public void onClick(DialogInterface dialog, int No) {
Toast.makeText(getBaseContet(), "Clicou em Sim!", Toast.LENGTH_SHORT).show();
return;
}});
AlertDialog dialog = builder.create();
dialog.show();

A gura Z9 mostra o resultado do alerta desse cdigo. O alerta obriga o usurio


a pressionar o boto Sim ou No, o que diferente do Toast, que exibe temporaria
mente a mensagem e desaparece.

Toggleutton '
t. mw
SpinnerMula: . , Check e Radio

Mensagem Progressialog .
No Sim, ProgressBar
AlertDialog
, . Clicou em Sim!
L|stV|ew -

Listview ~ exemplo imagem

Figura Z9 -Alerta com botes de sim e no.


210 Google Android - 4 edio
7.16 Layoutlnater - inando um arquivo XML
H' , H
A classe android.view. Layoutlnater e utilizada pa ra converter um arquivo XMLpara um
objeto do tipo view; o que conhecido pelos desenvolvedores como lnarum layout ,
O Layoutlnater um servio do sistema e deve ser recuperado com o seguinte codigo:
Layoutlnater inate = (Layoutlnater)
context.getSystemService(Contet.LAYOUT_INFLATER_SERVICE);

Nesse caso, o context uma referncia para a classe Activity, ou muitas vezes e o
this. Tambm podemos utilizar 0 seguinte atalho para obter 0 Layoutlnater.
Layoutlnater inate = Layoutlnater.from(context);

Depois de obter o Layoutlnater, basta chamar o mtodo inate(id,parent), o qual


recebe o id do arquivo XML desejado e retorna uma instncia de um objeto do
tipo View. O seguinte cdigo mostra como inar o arquivo /res/layout/inate_teste.xml
e retornar um objeto do tipo View que pode ser utilizado no cdigo. Isso permite
criar uma view como XML e inar o objeto, em vez de utilizar diretamente a API.
View view = (View) LayoutInater.from(this).inate(R.1ayout.inate_teste, layout, false);

O caso de uso mais comum do Layoutlnater para criar as views de um adapter;


por exemplo, para preencher os componentes ListView e ViewPager.

7.17 ListView

A classe android.widget.ListView um dos componentes visuais mais utilizados e


representa uma lista na tela. O exemplo a seguir mostra um arquivo de layout
com um ListView.

/res/layout/actvity_exempIo_|istview.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:orientation="vertica1" android:padding="16dp" >
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:1ayout_height="0dp"
android:1ayout_weight="1"
android:1ayout_margin="10dp" /
<Vew

android:1ayout_width="natch_parent" android:1ayout_height="z@dp"
android:background="@co1or/primary" /
</LinearLayout
Captulo 7 I Interface grca - View 2"
Quem fornece os dados para preencher o Listview um adapter, que uma classe que
implementa a interface android .widget . ListAdapter. Opcionalmente podemos estender
a classe android.widget.BaseAdapter que j implementa essa interface e deixa poucos
mtodos abstratos para terminarmos a implementao. O cdigo a seguir mostra um
adapter que vai preencher o ListView com uma lista que tem os nomes dos planetas.
Para cada planeta ser criada uma vievig que nesse exemplo um simples Textview.
Veja os comentrios no cdigo para entender o que faz cada mtodo do adapter.

SimpIesAdapter.java
public class SimplesAdapter extends BaseAdapter {
private String[] planetas = new String[] { "Mercrio", "Vnus", "Terra", "Marte",
"Jpiter","Saturno", "Urano", "Netuno", "Pluto"};
private Context context;
public SimplesAdapter(Context context) {
SUDer();
this.contet = context; // O context necessrio para criar a view
}

@0verride
public int getCount() {
return planetas.length; // Retorna a quantidade de items do adapter
}

@0verride
public Object getItem(int position) {
return planetas[position]; // Retorna o objeto para esta posio
}
@0verride
public long getItemId(int position) {
return position; // Retorna o id do objeto para esta posio
}

@0verride
// Retorna a view para esta posio
public View getView(int position, View convertview, ViewGroup parent) {
String planeta = planetas[position];
Textview t = new TextView(context);
oat dip = 56;
oat densidade = context.getResources().getDisplayMetrics().density; // Densidade
// da tela
int px = (int) (dip * densidade + 0.5f);
t.setHeight(px);
t.setTet(planeta);
return t;
}

}
212 Google Android - 4 edio
. _ , - .-, .. zo e definindo a
~ 1 ` ` ado diretamente
-.. ..-.-.-.. - s, conforme
U codigo do adapter esta criando um Textview por Pmffama

. -u 50
altura como 5Odp. Veja no codigo que 0 numero 50 nao L utilll
para definir a altura do Textview, pois isso traria resultados Cllffemff fl
resoluao e densidade da tela do dispositivo. Por isso, essc calculo converte dip
para 5Opx. Mas nem sempre 5Odp 5Opx, pois 0 f5Ulf3d0 da 0nVf5a0 P) C
ser lOOpx, l50px, 200px etc., conforme a densidade da tela. Por isso a conversao e
necessaria. Mas no se preocupe com isso agora, estou apenas alertando sobre um
problema comum. Ns vamos estudar esse assunto em mais detalhes no capitulo
30, sobre como criar aplicativos compatveis com diferentes tamanhos de telas.
Para nalizar o exemplo, segue o cdigo-fonte da activity Para preencher a lista,
preciso chamar o mtodo setAdapter(adapter) do Listview informando o adapter.
A lista ter a quantidade de linhas que o adapter retornar no metodo getCount().

ExempIoListViewActivity.java
public class EenploListViewActivity extends Activity inplements 0nItemClickListener {
protected static nal String TAG = "livro";
private Listview listview;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exenplo_listview);
// Listview
listview = (tistview) ndViewById(R.id.listview);
listView.setAdapter(new SinplesAdapter(this));
listview.set0nItenClickListener(this);
}

public void onItemClick(AdapterView<? parent, View view, int idx, long id) {
// Objeto selecionado, que neste caso era de um array de strings
String s = (String) parent.getAdapter().getItem(idx);
Toast.makeTet(this, "Texto selecionado: " + s + ", posio: " + idx,
Toast.LENGTH_SHORT).show();
}

Ao clicar em algum item da lista, o mtodo onItenClick() ser chamado, informan


do a posio e o id do objeto selecionado. Com base nisso, podemos recuperar o
objeto selecionado no cdigo. Ao executar esse exemplo, voc ver o Listview no
centro e na parte interior da tela uma view quadrada que preenchi com o fundo
azul (mesma cor da action bar), conforme
a figura 7.lO. No cdigo do arquivo de
layout, veja que a altura do Listview foi definida co mo Gdp, para o Listview respeitar
Captulo 7 n Interface grca - View 213
o peso que lhe foi atribudo e esticar at onde ele pode, respeitando essa view
que est localizada na parte inferior do layout. Se o Lstvew estivesse com a altura
como match_parent, ele iria esticar sem respeitar os outros componentes, e a view
de baixo seria jogada para fora da tela.

* 1. I
Parabns! Voc acabou de fazer o primeiro exemplo de ListViev\g um dos compo

.-. .
nentes mais utilizados em aplicativos.

1 I
-.. _ _zll
1 Mefcm
Vnus
Q H... ,la. .l_pop.. r,,, _.-, 1

, gtn,
Jgne-

5 Urano

l Texto selecionado: Terra, posio; 2

l
I

Figura Z10 - Listlew.

No cdigo desse ltimo exemplo, o mtodo getVew() da classe Simp1esAdapter que


zemos est criando o TextVieW pela API, o que no o recomendado. O ideal
sempre criar uma view utilizando um arquivo XML e inflar esse layout com a
classe LayoutInater. Vamos ento criar o seguinte layout para o adapter:

/res/layout/adapter_simpIes.xmI
<?xml version="1.0" encoding="utf-8"?>
<LnearLayout xmlns:android="http://schemas.androd.com/apk/res/android"
android:1ayout_width="match_parent" androd:1ayout_heght="wrap_content" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="50dp" />

Feito isso, altere o cdigo do mtodo getVew() para inflar o layout. Depois de fazer
isso, o resultado ser o mesmo de antes, mas desse jeito o cdigo ca bem mais
organizado. Nem sempre o layout do adapter ser to simples; o recomendado
sempre separar a interface em arquivos XML.
214 Google Android - 4 edio
gfu SimpIesAdapter.java

public class SimplesAdapter extends BaseAdapter {

@Override
public View getView(int position, View convertview, VIGWGFOUD Df@t) {
String planeta = planetas[position]; .
View view = Layoutlnater.fron(context).inate(R.layout.adapter_siD1S P3f@t flS);
Textview t = (Textview) view.ndViewById(R.id.tet);
t.setTet(planeta);
return view;
}

Note que no cdigo XML do arquivo /res/layout/adapter_simples.xml foi denida a


altura do TextView como 5Odp, que a notao da medida de densidade do Android.
Dessa forma, conforme a resoluo da tela do dispositivo, 5Odp ser convertido
para 5Opx, 75px, 100px etc. Foi isso que zemos no cdigo quando utilizamos a
API, mas aqui no XML basta utilizar a notao dp e tudo feito automaticamente.

7.18 ListVew com adapter customizado


No prximo exemplo, vamos criar uma lista de objetos e exibi-la no Listview com um
adapter customizado. A classe android .widget . Listview um dos componentes visuais
mais utilizados e representa uma lista na tela. Entender esse exemplo vital para seu
futuro como desenvolvedor Android, at porque demonstra a utilizao de adapters.
O exemplo a seguir mostra um arquivo de layout com um ListView:

/res/Iayout/activity_exempIo_Iistview.xmIz
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height="match_parent"
android:orientation="vertical" android:padding="16dp"
<ImageView android:src="@drawable/ic_launcher"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="Gdp"
android:layout_weight="1"
android:layout_margin="10dp" />
<InageView android:src="@drawable/ic_launcher"
android:layout_width="wrap_content" andro`d:l h
1 ayout_ eight="wrap_content" />
Captulo 7 n Interface grca - View

Para preencher a lista do Listview, vamos criar uma lista de planetas, entao crie a
classe Planeta. Para o exemplo funcionar, copie as guras de cada planeta e 1ns1ra na
pasta /res/drawable. Elas podem ser encontradas no projeto de exemplo deste capitulo

P|aneta.java
public class Planeta {
public String nome;
public int img; // R.drawable.xx
public Planeta(String nome, int img) {
this.nome = nome;
this.img = img;
}

public static List getPlanetas() {


List planetas = new ArrayList();
I
panetas add \ I`lW P-aneta(" Mercrio", R.drawable.planeta_01_mercurio))
planetas addl [new P-aneta(" Vnus", R.drawable.planeta_02_venus));
planetas add\ HGW P-aneta(" Terra", R.drawable.planeta_03_terra));
p-anetas addl\ HGW P-aneta(" Marte", R.drawable.planeta_04_marte));
I
panetas add \ new P-aneta(" Jpiter", R.drawable.planeta_05_jupiter));
p-anetas add(new P.aneta(" Saturno", R.drawable.planeta_06_saturno));
panetas add(new Paneta(" Urano", R.drawable.planeta_07_urano));
p.anetas add(new P-aneta(" Netuno", R.drawable.planeta_08_neptuno));
planetas add( new P-aneta(" Pluto", R.drawable.planeta_09_plutao));
return planetas;
}

A classe Planeta permite criar a lista de planetas. No cdigo-fonte da activity basta


criar essa lista e congurar o adapter no Listview.

ExempIoListViewActivity.java
public class EemploListViewActivity extends Activity implements 0nItemClickListener {
protected static nal String TAG = "livro";
private Listview listview;
private List planetas;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_listview);
// Listview
listView = (Listview) ndViewById(R.id.listview);
planetas = Planeta.getPlanetas();
216

}
}. listView.setAdapter(new PlanetaAdapter(this, planetas))
listview.set0nItemClickListener(this);
Google Android - 4 edio

public void onItemClick(AdapterView<?> parent, View VEW, Int 1d 1"9 ld) {

}
Planeta p = this.planetas.get(id);
Toast.nakeTet(this, "Planetaz " + D.0W, TOGS-LENGTH_5H0RT)-5h0W();

Para 0 cdigo compilar, precisamos criar a classe de adapter.

PIanetaAdapter.java
public class PlanetaAdapter extends BaseAdapter {
private nal Context context;
private nal List planetas;
public PlanetaAdapter(Context context, List planetas) i
this.contet = context;
this.planetas = planetas;
}

@0verride
public int getCount() {
return planetas != null ? planetas.size() : 0;
1

@0verride
public Object getIten(int position) {
return planetas.get(position);
}

@0verride
public long getItenId(int position) {
return position;
}

@0verride
public View getView(int position, View convertview, ViewGroup parent) {
// Ina a view
View view = Layoutlnater.fron(contet).inate(R.layout.adapter_planeta, parent, false);
// Faz ndViewById das views que precisa atualizar
Textview t = (Textview) view.ndViewById(R.id.tNonePlaneta)
Inageview img = (lnageview) view.ndViewById(R.id.ingPlaneta);
// Atualiza os valores das views
Planeta planeta = planetas.get(position);
t.setTet(planeta.nome);
img.setImageResource(planeta.img);
Captulo 7 nz Interface grca - View

// Retorna a view deste planeta


return view;
}

O arquivo de layout do adapter pode ser visualizado a seguir:

/res/layout/adapter_pIaneta.xmI
<?xml version="1.0" encoding="utf-8"?>
<LnearLayout xmlns:android="http://schemas.androd.com/apk/res/androd"
android:layout_width="natch_parent"
androd:layout_height="?android:attr/listPreferredItemHeight"
android:gravtty="center_vertcal"
android:orientation="horlzontal">

<ImageView android:id="@+id/ingPlaneta"
android:layout_wdth="0dp" android:layout_height="wrap_content"
android:layout_weight="3"
android:src="@drawable/planeta_03_terra" />

<TetVew android:id="@+id/tNomePlaneta"
androd:layout_wdth="0dp" android:layout_hetght="wrap_content"
android:layout_weight="7"
android:layout_marginLeft="10dp" android:textColor="#000G00" />

Dica: no cdigo do layout XML, a altura da view do adapter foi denida como
androd:layout_height="?android:attr/lstPreferredItenHeight". Isso acessa um atributo de
dimenso nativo do Android, que retorna a altura recomendada pela plataforma
para uma linha do ListVieW.

A figura Z11 mostra o resultado desse exemplo com a lista de planetas.

Nota: existe uma classe especial de activity que a ListActivtty, a qual j declara
seu prprio layout com um nico Listview. Mas eu prefiro sempre estender minhas
classes diretamente de activity e adicionar um Listview no layout, pois assim
o layout ca mais exvel e voc tem controle do que est fazendo. Para sua
consulta, a MainActivity do projeto de exemplo deste captulo filha de ListActivity.
218 Google Android - 4 edio

* M
V

" l Marte

z . ..- ~
-, z.i___.T-.
"z~
-z zV z.~- ~i..
_ -..M

Figura ZH - ListView.

7.19 GridVew

A classe androd.wdget.GrdVew utilizada para exibir os componentes em formato de


grid com linhas e colunas. Seu uso mais clssico para exibir vrias imagens como em
um lbum de fotos. A seguir, podemos ver um exemplo de como utilizar um Grdvew.

/res/Iayout/activity_exempIo_gridview.xmI
<?m1 verson="1.0" encodng="utf-8"?>
<LnearLayout m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:1ayout_wdth="match_parent" androd:layout_heght="match_parent"
androd:orentation="vertca1" android:paddng="16dp" >
<TetVew

androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:text="Eemp1o de GrdVew:" />
GridView android:d="@+id/grd1"
androd:1ayout_wdth="match_parent" android:layout_heght="match_parent"
androd:paddng="1@dp" androd:gravty="center"
androd:vertca1Spacng="1Gdp" BDFO2h0FZOt81SDCg:"ldp"
android:numCo1umns="auto_t" android:co1umnwidth="40dp" />
</LnearLayout>

Na tag <GrdVew> so denidos alguns parmetros de espaamento e nmero de


colunas. Nesse exemplo, os parmetros relevantes so:
Captulo 7 1 Interface grca - View 219
Parmetro Descrio
columnwidth Largura de cada coluna do grid. Sempre utilize a notao com dp
(densyindependentrnxekl
numColuns Nmero de colunas do grid. Nesse caso, foi informado o valor auto_fit
para ajustar automaticamente o nmero de colunas com base na largura
da coluna.

Para exibir as imagens no Gridview, necessrio criar um adapter que retorne uma
lista com as imagens necessrias. Sendo assim, foi denido no arquivo XML um
id para o Gridview, para que ele possa ser recuperado no cdigo.
A seguir, temos uma classe de adapter que recebe um array de imagens e cria um
Imageview para cada uma.

ImagemAdapter.java
public class ImagemAdapter extends BaseAdapter {
private Context ctx;
private nal int[] imagens;
public AdaptadorImagem(Context c, int[] imagens) {
this.ctx = c;
this.imagens = imagens;
}
@Override
public int getCount() { return imagens.length; }
@0verride
public Object getItem(int posicao) { return posicao; }
@0verride
public long getItemId(int posicao) { return posicao; }
@Override
public View getView(int posicao, View convertview, ViewGroup parent) {
// Ina a view que est no XML
View view = LayoutInater.from(this.ctx).inate(R.layout.adapter_imagem_gridview,
parent,false);
// Utiliza o ndViewById para obter o Imageview
Imageview img = (ImageView) view.ndViewById(R.id.img);
// Altera a imagem (baseado na posio do array)
img.setImageResource(imagens[posicao]);
// Retorna a view
return view;
}

Para o cdigo compilar, crie este arquivo XML de layout que ser inado pelo
cdigo do adapter. Veja que o nome do arquivo segue a notao adapter_nome.xml.
no Google Android - 4' edio
f /res/layout/adapter_imagem.xml
<?ml version="1.9" encoding="utf-8"?
<LinearLayout mlns:android="http://schemas.android.com/apk/FGS/"dfd" H
android:layout_width="wrap_content" android:layout_hei9ht="wrap-C"te"t >
<ImageView android:id="@+id/img" android:layout_width="wFD_Ct@"t"
android:layout_height="wrap_content" />

Nota: observe que o mtodo getView(posicao,view,parent) deve retornar a View que vai ser
inserida em determinada posio do Gridview. O conceito de adapters (adaptadores)
muito utilizado no Android, e quanto antes voc entender isso melhor. Note que
para criar a view foi inflado um layout XML com ajuda da classe Layoutlnater.

Para nalizar o exemplo, este o cdigo da activity que vai preencher o Gridview
com um array de imagens. A classe ImagemAdapter deve retornar a quantidade de
imagens que precisam ser adicionadas no Gridview.

ExempIoGridViewActvity.java
public class EemploGridViewActivity extends Activity {
// Array com os ids das imagens
private int[] imagens = { R.drawable.smile1, R.drawable.smile2,
R.drawable.smile1, R.drawable.smile2, R.drawable.smile1,
R.drawable.smile2, R.drawable.smile1, R.drawable.smile2 };
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_eemplo_gridview);
Gridview grid = (Gridview) ndViewById(R.id.grid1);
grid.set0nItemClickListener(onGridViewItemClick());
// Informar o adapter para preencher o Gridview
grid.setAdapter(new ImagemAdapter(this, imagens));
}
// Evento ao clicar no item do grid
private 0nItemClickListener onGridViewItemClick() {
return new 0nItemClickListener() {
public void onItemClick(AdapterView<? pargnt, View V, int poScao,10ng id) {
_ Y IS, magem selecionada: " +
Toast.makeText(EemploGridViewActivit .th` "I
posicao, Toast.LENGTH_SHORT).show();
}

};
}

}
Captulo 7 I Interface grca - View 221
Observe que o mtodo set0nItemC1ckLstener(lstener) da classe Grdvew pode ser
utilizado para tratar os eventos gerados caso o usurio selecione e pressione al
guma imagem. No mtodo onItemC1ck(parent,vew,poscao,d), possvel recuperar
qual imagem foi selecionada. A gura 112 exibe o resultado desse exemplo.

G
00 0
Exemplo de GridView:

Figura Z12 - Exemplo de Gridl/iew.

Nota: durante o livro, vamos estudar outros exemplos sobre os adapters. Se voc
perceber, uma simples classe que deve implementar o mtodo getCount() para
informar quantas views existem, e depois o mtodo getVew() chamado N vezes
para criar cada view. Seja para criar um grid com Grdvew ou uma lista com Lstvew
, os adapters so gurinhas carimbadas no desenvolvimento para Android e so
responsveis por fornecer o contedo e preencher esses componentes.

7.20 Gallery
Sabe quando voc abre o lbum de fotos no Android e faz o gesto de swipe (deslizar)
para os lados para ver as fotos? A classe androd.wdget.GaU.ery faz justamente isso.
Nesse exemplo, criaremos uma galeria de imagens com as fotos de alguns planetas.

/res/Iayout/activity_exempIo_gaIIery.xm|
<?xml verson="1.0" encodng="utf-8"?>
<LnearLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
222 Google Android - 4 edio
android:layout_width="match_parent" android:layout_hei9ht="match-parentH
android:orientation="vertical" >
<TetView
android:layout_width="wrap_content"androidzlayout _h 01 9ht="wrap_content"
android:text="Eemplo de Gallery" android:gravity="centF" />
<Gallery android:id="@+id/gallery"
android:layout_width="match_parent" androidzlayout _h 6i ht="match_parent"
9
android:gravity="center" />

A forma de usar o Gallery idntica do Gridview. Este prximo exemplo cria uma
galeria de fotos a partir de um array de imagens.

ExempIoGaIIeryActivty.java
public class ExemploGalleryActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus,
R.drawable.terra, R.drawable.marte, R.drawable.jupiter,
R.drawable.saturno, R.drawable.urano, R.drawable.netuno,
R.drawable.plutao };
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_gallery);
Gallery g = (Gallery) ndViewById(R.id.gallery);
g.setAdapter(new ImagemAdapter(this, imagens));
9.set0nItemClickListener(onGalleryItemClick(this));
}

private 0nItemClickListener onGalleryItemClick(nal Context context) {


return new 0nItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int posicao,long id) {
// Exemplo de alerta com Toast com uma view dentro
// Geralmente o Toast apenas um texto
Imageview imgview = new ImageView(contet);
imgview.setImageResource(imagens[posicao]);
Toast t = new Toast(context);
t.setView(imgView);
t.show();
}

};
}

}
Captulo 7 I Interface grca - View

Observe que, para deixar o exemplo mais completo, o mtodo onItemC1ick( parent
v1ew,poso,d) foi implementado para recuperar a posio da imagem selecio
nada. Nesse caso, a classe Toast foi utilizada para exibir um alerta na tela, que na
verdade desenhado pelo Imagevew da imagem selecionada.

Nota: a mesma classe ImagemAdapter que utilizamos para preencher as imagens


do Grdvew foi utilizada agora com o Gallery, exatamente da mesma forma. Isso
mostra a grande utilidade dos adapters. Outro detalhe importante sobre a classe
Gallery que ela foi recentemente descontinuada (deprecated) pelo Google, que
recomenda utilizar a classe ViewPager que tem o mesmo comportamento, mas
bem mais flexvel. Demonstrei o Gallery para seu aprendizado, mas vamos
sempre usar o ViewPager na prtica.

A gura 113 mostra a galeria de fotos com o planeta Terra selecionado. Para testar
os exemplos, navegue na galeria fazendo o gesto de swipe para os lados.

Lxemplo de Gallery

Figura ZJ3 - Exemplo de Gallery.

7 21 ViewPager
A classe androd . support.v4.vew.VewPager faz parte da biblioteca de compatibilidade
No Android Studio, a biblioteca de compatibilidade configurada no arquivo
app/buildgradle.
Lembrando que a biblioteca de compatibilidade baixada pelo SDK Manager pelo
item Android Support Library e recomenda~se sempre mant-la atualizada. Depois de
224 Google Android - 4 edio
. . o as c . - - f ~ ~ / d `d_sdk/ext
baixar o item Android Support library, a biblioteca sera instalada em an~ roz biblioterzzz/
android/support. L, voce vai encontrar as pastas v4 v7 C v13 que Sa 714 HS
dc compatibilidade com cada API Level, conforme mostra a gura .

A ideia bsica sobre as bibliotecas de compatibilidld QU _*42@1<f;ff*V@l


com Android 1.6 (API Level 4), a v7 compativel com o An roi I . . eie
7), e por a vai. A classe android.support.v4.vew.Vi.ewPager, como 0 proprio nome 0
pacote j informa, est na biblioteca v4.
H /\n<l:ii <lK ~'lf!!ld(i^Y
'
liiiz i ._ l
Willi l"|`

i 5. 4
\*\(l'll*(l l<1`vv A

pc fi Il i C instiiieti
/\Yii`lf(`iil 4-' (`\l l l

L1p.
1T1lfl
l install llcl
I _ '1l!`il
l lv Q i il <)f`l(l''l('l l 'll fl
l

l
l

V1 `

~ ' woiict zw
hi
\ bixo-x!~af\ V I I 1 b L' Rigrpitg. H;
\p\[( \,_. 3 \.i\.*"t"
l 5 `^.i(\~.l lt?

Figura Z14 - Biblioteca de compatibilidade.

A dependncia da biblioteca de compatibilidade v4 deve ser adicionada no arquivo


app/buildgradle. Feito isso voc poder utilizar as classes da biblioteca de suporte
que cam no pacote android.support.v4, como por exemplo a classe android.support.
v4.view.VewPager.

app/buiId.grad|e
apply plugin: 'con.android.appllcation'

dependencias {
conpile leTree(dir: 'libs', include: ['*.jar']) // Adiciona arquivos .jars da pasta
I/ libs como dependncia
Captulo 7 I Interface grca - View 225
// Dependncia da biblioteca de compatibilidade v4
compile "com.android.supportzsupport-v4:21+"
l

Com a biblioteca de compatibilidade v4 congurada no projeto, vamos partir


para a prtica, portanto crie o seguinte arquivo de layout.

/res/layout/activity_exempIo_view_pager.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:tet="Eemplo de ViewPager" />
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" />

Note que a tag precisa conter o nome completo da


classe, pois esse componente no faz parte dos nativos do Android. Para preencher
as views do ViewPager, deve-se informar um tipo especial de apdater, que uma imple
mentao da classe android.support.v4.view.PagerAdapter. Lembre-se de que um adapter
apenas retorna uma view para determinada posio. Dessa forma, se o adapter disser
que existem dez pginas, ele dever retornar uma view para cada uma delas.
No prximo exemplo, vamos criar a classe ImagemPagerAdapter que estende
android.support.v4.view.PagerAdapter e retorna a view que ser utilizada para criar a
galeria de fotos, da mesma forma que zemos com o Gallery.

|magemPagerAdapter.java
public class AdaptadorImagem_ViewPager extends PagerAdapter {
private Context ctx;
private nal int[] imagens;
public ImagemPagerAdapter(Context c, int[] imagens) {
this.ct = c;
this.imagens = imagens;
}

@0verride
public int getCount() { // Quantidade de views do adapter
return imagens != null ? imagens.length : 0;
l
226 Google Android - 4 edio
@Override _ _
// Ina a view ,
public Object instantiateltem(ViewGroup container, int D51t1") {

View view = Layoutlnater.from(this.ct).inate(R-lYU-adapter-Imagem'


container,false);
Imageview img = (lmageview) view.ndViewById(R.id.im9);
img.setImageResource(imagens[position]); .
((ViewGroup) container).addView(view); // Adiciona ao layout ViewGrouD

@Override _
}
return view;

public void destroyItem(ViewGroup container, int position, Object view) { // Remove a


// view do container
((ViewPager) container).removeView((View) view);
}

@0verride
public boolean isViewFromObject(View view, Object object) {
// Determina se a view informada igual ao object retornado pelo instantiateltem
return view == object;
}

cada mtodo:
Mtodo ` Descriao p
Para auxiliar o entendimento do cdigo-fonte dessa classe, segue a explicao de

getCount Retorna a quantidade de elementos do adapter.


instantiateltem Retorna um objeto-chave chamado de key object, que utilizado
internamente para controlar o ViewPager. Durante a implementao
do mtodo instantiateltem, voc deve adicionar a view criada no
container, que o ViewGroup informado como parmetro. Para sim
plicar, voc pode retornar diretamente a view neste mtodo 011
outro objeto-chave qualquer.
isViewFromObject Nesse mtodo, voc deve validar se a view informada como par
metro corresponde ao objeto-chave informado. Esse objeto-chav
aquele que foi retornado do mtodo instantiateltem. i
destroyltem Este mtodo chamado para destruir uma view associada a um
objeto-chave. Portanto, as views que foram adicionadas ao container
devem ser removidas aqui. importante que esse mtodo seja S0
brescrito e o super.destroyItem() da classe-me no seja chamado. A0
navegar pelo ViewPager, o Android pode ir destruindo as views que
nao estao sendo utilizadas para economizar memria.
Vamos Finalizar o exemplo e escrever o cdigo da activity que vai utilizar o ViewPa9@f~
Captulo 7 I Interface grca - View 227
ExempIoVewPagerActivity.java
public class EemploViewPagerActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus,
R.drawable.terra, R.drawable.marte, R.drawable.jupiter,
R.drawable.saturno, R.drawable.urano, R.drawable.netuno, R.drawable.plutao };
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_exemplo_view_pager);
ViewPager g = (ViewPager) ndViewById(R.id.viewPager);
g.setAdapter(new ImagemPagerAdapter(this, imagens));
g.set0nPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {// Informa que determinada pgina
// foi selecionada
Toast t = Toast.makeTet(getBaseContext(), "Imagem: " + position,
Toast. LENcTH_sHoRT);
t.show();
}

@Override
public void onPageScrolled(int position, oat position0ffset,
int position0ffsetPiels) {
}

@Override
public void onPageScrollStateChanged(int state) {
}

});
}

Ao executar esse exemplo, o funcionamento ser idntico ao do componente


Gallery. Note que, ao navegar de uma pgina para outra no ViewPager, um alerta
exibido com o Toast, porque o mtodo onPageSelected(position) chamado. Dessa
forma, podemos realizar alguma ao sempre que o usurio navegar entre as
views desse componente.
A classe ViewPager uma das mais utilizadas no desenvolvimento para Android, e
se integra muito bem com a famosa API de Fragments que ainda vamos estudar
durante o livro. Lembre-se de que a classe Gallery foi descontinuada pelo Google,
e o recomendado sempre utilizar o ViewPager.
228 Google Android - 4 edio
Provavelmente voc ja deve ter visto alguns aplicativos que usam t2lbS, C0m0 por
exemplo: o Google Play, o qual permite navegar pelas tabs utilizando os gestos de
swipc lateral. lsso tambm feito com o ViewPager, inclusive vamos fazer exatamente
isso ao desenvolver o aplicativo dos carros.

Dica: uma biblioteca muito conhecida por desenvolvedores Android a


VewPagerIndcator (http://viewpagerindicator.com). Ela funciona em conjunto com o
ViewPager e contm vrias views utilitrias. Por exemplo, bem comum em aplicativos
ter aquele marcador com bolinhas em baixo do ViewPager, pa ra indicar a pgina que
voc est visualizando. Essa biblioteca contm esse marcador e muito mais.

7.22 ViewPager + TitIeStrip ou TabStrip


No exemplo anterior, mostramos a gura de cada planeta no ViewPager, mas no
mostramos o nome do planeta em lugar nenhum.
Para mostrar o nome do planeta, podemos implementar a interface
OnPageChangeLstener para monitorar a troca de pginas do ViewPager, e nesse caso
mostrar 0 nome do planeta em algum Textvew. Mas 0 ViewPager j tem duas classes
que facilitam justamente esse trabalho, a classe androd.support.v4.vew.PagerTt1eStrp
e androd.support.v4.vew.PagerTabStrp.

Para ter uma ideia do que estou falando, a gura Z15 mostra o resultado ao utilizar

'ile1
O ViewPager com o PagerTt1eStrp.

.
Rf,'lj}`~* :II \v.Ic":f\ zT `
.. .- M -` .... _
..._j
f'~.,~.v,@_ 3`_Qg~;_.. _. .!Z<._\_;>'_f. > sz _,w ,, -,.;; z. j

Figura 115 - PagertleStrip e PagerTabStrip.


Captulo 7 I Interface grca - View 229
A classe android . support.v4.view.PagerTitleStrip mostra um ttulo acima do ViewPager
facilitando o entendimento do usurio. J a classe android . support.v4.view. PagerTabStrip
mostra um indicador com uma tab e inclusive permite ao usurio clicar na tab para
navegar entre as pginas. Para utilizar um desses componentes, basta inclu-lo como
lho do ViewPager no layout. O exemplo a seguir mostra como utilizar o PagerTabStrip.

/res/layout/activity_exempIo_view_pager__tab_strip.xmI
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout . . . >
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip android:id="@+id/viewPagerTabStrip"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_gravity="top" android:background="#33b5e5"
android:textColor="#fff" />

Para mostrar o ttulo acima do ViewPager, preciso que a classe do adapter imple
mente o mtodo getPageTitle(int page) que deve retornar o ttulo da pgina. Nesse
exemplo, vamos usar novamente a classe Planeta, a qual tem o nome do planeta
e o inteiro com o recurso da imagem. A classe do adapter recebe uma lista de
planetas e retorna a view que contm a foto do planeta, tambm retorna o nome
do planeta no mtodo getPageTitle(int page).

PlanetasPagerAdapter.java
public class PlanetasPagerAdapter extends PagerAdapter {
private Context ctx;
private nal List planetas;
public PlanetasPagerAdapter(Context c, List planetas) {

// Para o cdigo completo veja nos exemplos do livro. Esse um adapter simples.
// O importante o mtodo getPageTitle(page) retornar o titulo.
@0verride
public CharSequence getPageTitle(int page) {
// Titulo para mostrar no PagerTitleStrip ou PagerTabStrip
Planeta planeta = planetas.get(page);
return planeta.nome;
}

}
230 Google Android - 4' edio
, . . . - -. _ . . . a ~ ` a acia
O codigo da activity somente cria a lista dc planetas c configura ptcr no
ViewPager. O resto tudo automatico, c o ttulo retornado pelo adapter sera mos
trado na tah do viewPager.

i'=fi MainActivity.java

public class EenploViewPagerTabStripActivity extends Activity {


@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eenplo_view_pager_tab_strip);
getActionBar().setDisplayHoneAsUpEnabled(true);
// Planetas
List<Planeta planetas = Planeta.getPlanetas();
// ViewPager
ViewPager g = (ViewPager) ndViewById(R.id.viewPager);
g.setAdapter(new PlanetasPagerAdapter(this, planetas));
}

Dlca: para alterar por programao a pgina que o ViewPager est mostrando,
utilize o metodo setCurrentIten(int page).

7.23 lmageSwitcher
Outra classe bastante til a android.widget.ImageSwitcher, utilizada para mostrar
uma imagem aps outra de forma animada.

) /res/Iayout/activity_eempIo_image_switcher.mI
<?nl version="1.0" encoding="utf-8"?>
LinearLayout nlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="natch_parent"
android:orientation="vertical" android:padding="10dp" >
Button android:id="@+id/btProina"

BHVO21Y0Ut_wldth="wrap_content" android:layout_height="wrap content"


android:tet="@string/proxima" / _
<ImageSwitcher android:id="@+id/inageSwitcher"
6f\dI'd=165/out_width="natch_parent" android:layout_height="natch parent"
android:layout_nargin="10dp" /> _
/LinearLayout>
Captulo 7 I Interface grca - View 231
ExemplolmageSwtcherActivity.java
public class ExemploImageSwitcherActivity extends Activity {
// Planetas
private int[] imagens = { R.drawable.mercurio, R.drawable.venus,
R.drawable.terra, R.drawable.marte, R.drawable.jupiter,
R.drawable.saturno, R.drawable.urano, R.drawable.netuno, R.drawable.plutao };
private ImageSwitcher imageSwitcher;
private int idx = 0;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_eemplo_image_switcher);
// Congura o ImageSwitcher e os efeitos
imageSwitcher = (ImageSwitcher) ndViewById(R.id.imageSwitcher);
imageSwitcher.setFactory(new ImageSwitcher.ViewFactory(){
@0verride
public View makeView() {
Imageview img = new ImageView(getBaseContet());
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
img.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
return img;
}

});
imageSwitcher.setInAnimation(Animationtils.loadAnimation(this,android.R.anim.fade_in));
imageSwitcher.set0utAnimation(Animationtils.loadAnimation(this,android.R.anim.fade_out));
View btProima = ndViewById(R.id.btProima);
btProxima.setOnClickListener(new 0nClickListener() {
@0verride
public void onClick(View arg0) {
if(idx == imagens.length) { idx = 0; }
imageSwitcher.setImageResource(imagens[idx++]);
}

});
}

Para a classe ImageSwitcher funcionar, necessrio chamar o mtodo setFactory(viewFactory)


informando uma implementao de android .widget.ViewSwitcher .ViewFactory. Essa
interface dene o mtodo makeView() que deve retornar uma View, que a imagem
que deve ser exibida. Ao chamar o mtodo setImageResource(imagem) no ImageSwitcher,
232 Google Android - 4 edio
e _ , _ _ , - , . . -ala sem re ue o bom)
a .animacao c realizada. Neste exemplo .i imagem L altci e P Q t
Prxima foi' clicado. contiorinc a ligura 7.l(w.

PXME

Figura 7.l - Excnzplo dc IrnageSwitchex

7.24 WebView

Se por algum motivo voc precisar exibir uma pgina web dentro do aplicativo. a
classe androd .webktwebvew pode ser til. O funcionamento desse componente iden
tico ao browser do Android, isso porque internamente utilizada a engine \VebKit.
Essa e uma das views mais utilizadas nos aplicativos, principalmente pelos adeptos
da criao de aplicativos hbridos que utilizam HTML5 e JavaScript para criar a
interface grca. Primeiramente, para ter acesso a internet, declare a permisso
INTERNET no arquivo AndridMani>st..\m1. Quando o usurio baixar o aplicativo dd
Google Play; sero mostradas para ele todas as permisses que o aplicativo precisa
utilizar, e baseado nisso o usuario pode aprovar ou no a instalaao.

AndroidManifest.xmI
<manfest . . . />
uses-permission android:name="android.permission.INTERNET" /
<applcaton ... />
</manfest>

Para demonstrar 001110 exibir uma pgina de internet usando o Nebvew, vamo
criar um simples exemplo que exibe a pgina www_1W(,a,,dm(_mm_,,_
Captulo 7 I Interface grca - View 233
/res/Iayout/activity_exempIo_webview.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xnlns:android=http://schemas.android.com/apk/res/android" ... />
<FraneLayout android:layout_width="match_parent" android:layout_height="match_parent" >
<webView android:id="@+id/webview" android:layout_nargin="10dp"
android:layout_width="natch_parent" android:layout_height="match_parent" />
ProgressBar android:id="@+id/progress" android:layout_gravity="center"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

ExempIoWebViewActivity.java
public class EemplowebViewActivity extends Activity {
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_eemplo_webview);
nal Nebview webview = (webview) ndViewById(R.id.webView);
nal View progress = ndViewById(R.id.progress);
progress.setVisibility(View.INVISIBLE);
webview.loadUrl("http://www.livroandroid.com.br");
webview.setwebViewClient(new webViewClient(){
@Override
public void onPageStarted(webView view, String url, Bitmap favicon) {
progress.setVisibility(View.VISIBLE); // pgina comeou a ser carregada
}

@Override
public void onPageFinished(webView view, String url) {
progress.setVisibility(View.INVISIBLE); // pgina foi carregada
}

@Override
public void onReceivedError(webView view, int errorCode,String description,
String failingUrl) {
// Erro ao carregar a pgina do webview (endereo errado, ou erro de conexo)
}

});
}

A gura Z17 mostra a pgina do site do livro aberta dentro do webview. Veja que
utilizamos o FrameLayout para inserir o ProgressBar por cima do webview. Enquanto
a pgina est sendo carregada, o ProgressBar ca visvel para mostrar a animao.
234 Google Android - 4 edio
l wmpiu de Wvbvrrw """l'k"1"W"hV""'
Google \
di o Android 3
E nbqoig
livro campeao em vendas e
i no Android
l mcgmondado para iniciantes
jTpau, qugm deseja aprender
concertos principais doos
desenvolvimento para Andr0d
Explica do bsnco ao avanado.
confira o sumalio

Figura Z17 - Exemplo do WebView.

Nota: lembre-se de que necess rio declarar a permisso INTERNET para o WebView
funcionar.

O WebView realmente muito simples de ser utilizado. Nem vou me estender


muito nos exemplos porque muito fcil encontrar material sobre o webvew. Ape
nas para ter uma ideia, voc at pode enviar um HTML em string e pedir para o
webvew renderizar a pgina.
webvew.loadDataNthBaseURL( " ", "<font color=' blue' >HTML aqu" ,
Iltext/htm1'II,IIUTF_8II, Illl);

Geralmente os aplicativos deixam esse cdigo HTML em arquivos texto dentro


do projeto, ento basta ler o arquivo e converter para string. Outra tcnica que e
muito utilizada injetar cdigo JavaScript no Nebvew, para os mais variados ns,
e isso pode ser feito assim:
webVew.getSettings( ) . setJavaScrptEnabled(true);
webVlew.loadUrl( "javascrpt:alert( 'Oi leitor ' ); " );

E5525 tcnicas de injetar cdigo HTML ejavaScript no Nebvew esto fazendo a festa
dos adeptos de HTML5 e aplicativos hbridos, e com base nelas que populares
frameworks como o PhoneGap funcionam. Esse tipo de abordagem permite que
desenvolvedores com vasta experiencia em web entrem no mercado dos aplicativos
IUVS, C UfiliZ1n cdigo HTML para criar a interface grca

1e,_.
Mas no estou aqui para discutir sob re este assunto nativo vs. HTML. O objetivo
deste livro ensinar a criar aplicativos nativos para Android alm de eitplieir o
principais conceitos da plataforma.
Captulo 7 I Interface grca - View 235
7.25 Movimentando uma imagem pela tela com touch
Neste prximo exemplo, vamos movimentar uma imagem pela tela, utilizando
touch screen. No emulador voc poder utilizar o mouse para movimentar a ima
gem e naturalmente em um celular real voc vai arrastar a imagem com o dedo.
A classe View contm o mtodo onTouchEvent(MotionEvent), que sempre chamado
quando um toque na tela realizado. Como parmetro temos um objeto do tipo
MotionEvent, com o qual possvel recuperar as posies x e y do toque.
public boolean onTouchEvent(MotionEvent event) {
oat x = event.getX();
oat y = event.getY();
return true;
}

Esse mtodo deve retornar true caso a view tenha tratado o evento, ou false se
para delegar a tarefa para as outras views da tela. Se nenhuma view tratar o
evento, o mesmo mtodo ser chamado na activity responsvel pela tela. A seguir
podemos ver um exemplo completo que permite mover a imagem do boneco do
Android pela tela. A gura foi inserida em /res/drawable/android.png.

TouchScreenView.java
public class TouchScreenView extends View {
private static nal String TAG = "livro";
private Drawable img;
int x, y;
private boolean selecionou;
private int larguraTela;
private int alturaTela;
private int larguralmg;
private int alturalmg;
public TouchScreenView(Context context) {
super(context, null);
// Recupera a Imagem
img = context.getResources().getDrawable(R.drawable.android);
// Recupera a largura e altura da imagem
larguralmg = img.getIntrinsicwidth();
alturalmg = img.getIntrinsicHeight();
// Congura a View para receber foco e tratar eventos de teclado
setFocusable(true);
}

@0verride
Google Android - 4 edio

// Chamado quando a tela redimensionada, ou iniciada...


protected void onSizeChanged(int width, int height, int oldw, int oldh) {
super.onSizeChanged(width, height, oldw, oldh);
this.larguraTela = width;
this.alturaTela = height;
= width / 2 - (larguralmg / 2);
y = height / 2 (alturalmg / 2);
Log.i(TAG, "onSizeChanged x/y: " + + "/" + y);
}

@0verride
// Desenha a tela
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Fundo branco
Paint pincel = new Paint();
pincel.setColor(Color.wHITE);
canvas.drawRect(0, 0, larguraTela, alturaTela, pincel)
// Dene os limites/rea para desenhar
img.setBounds(x, y, + larguralmg, y + alturalmg);
// Desenha a imagem
img.draw(canvas);
}

@0verride
// Move a imagem
public boolean onTouchEvent(MotionEvent event) {
oat = event.getX();
oat y = event.getY();
Log.i(TAG, "onTouchEvent: x/y > " + x + "/" + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DONN:
// Inicia o movimento se pressionou a imagem
selecionou = img.copyBounds().contains((int) x, (int) y);
break;
case MotionEvent.ACTION_MOVE:
// Arrasta o boneco
if (selecionou) {
thS = (t) X - (larguralmg / 2);
thS-Y = (t) y - (alturalmg / 2);
}

break;
case MotionEvent.ACTION_UP:
// Finaliza o movimento
selecionou = false;
Captulo 7 n Interface grca - View 237
break;
}

// O invaiidate vai chamar o mtodo onDraw(canvas) novamente


inva1idate();
return true;
}

Feito isso, crie uma activity e congure esta view no mtodo setContentView(view):

TouchScreenViewActivity.java
public class TouchScreenViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TouchScreenView(this));
}

Nota: neste exemplo, ao tocar na tela a coordenada x/y da gura atualizada.


Ao chamar o mtodo invaiidate(), o Android vai desenhar a view novamente
chamando o mtodo onDraw(canvas).

Ao testar o exemplo no emulador, utilize o mouse para simular o touch screen e


mover a imagem (Figura 718).

e cz 1
Mova o objeto com o touch
l

_l

Figura Z18 - Exemplo de touch screen.


238 Google Android - 4 edio
_ ._. _ .,,.-.f z esnatelae
7.26 Desenho manual com a classe Canvas

s criar nossa r 1
O Android tem varias classes prontas para desenhar compm Cada
uma dessas classes e uma subclasse de V1.ew.Scra que podLm0 P Pfld
classe-lha de View? A resposta sim.

Neste prximo exemplo, criaremos um componente customizado que ser uma


subclasse direta de android.view.View e aprenderemos a desenhar quadrados, ci rculos
e linhas coloridas na tela. Para isso, sobrescreveremos 0 mtOd0 eDI'W(CVS), 0
qual chamado pelo Android para desenhar um componente (VICW). As classes
Textview, EditText, Imageview, Button etc. j implementam esse mtodo, mas chegou
a hora de criarmos nosso prprio componente do zero e desenhar tudo na tela.
Para isso CI`aI'ITlOS uma classe chamada Mlhvlew, COfOI`I`l1 demonstrado a SgLllI'I

MinhaView.java

package br.com.livroandroid.cap07_view.canvas
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class Minhaview extends View {
// Para denir a cor RGB
private Paint pincelvermelho;
private Paint pincelPreto;
private Paint pincelAzul;
public MinhaView(Context context) {
this(context,null);
}

public MinhaView(Context context, AttributeSet attrs) {


super(context, attrs);
setBackgroundColor(Color.LTGRAY);
// Vermelho
pincelvermelho = new Paint();
pincelvermelho.setARGB(2SS, 255, 0, 0)
// Preto
pincelPreto = new Paint();
pincelPreto.setARGB(255, 0, 0, 0);
// Azul
pincelAzul = new Paint();
pincelAzul.setARGB(255, 0, 0, ZSS);
Captulo 7 I Interface grca - View 239
// Congura a View para receber foco e tratar eventos de teclado
setFocusable(true);
}

@0verride
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Desenha um quadrado
canvas.drawRect(20, 20, 200, 200, pincelAzul);
// Desenha uma linha
canvas.drawLine(200, 200, 400, 400, pincelPreto);
// Desenha um circulo
canvas.drawCircle(400, 400, 100,pincelVermelho);
}

Observe que existem dois construtores na classe: um que recebe apenas o


android.content.Context e outro que tambm recebe o android.util.AttributeSet. Se
essa classe for utilizada diretamente pela API Java, o construtor com apenas o
parmetro android.content.Context chamado; caso contrrio, se for utilizada pelo
XML, o Android chamar o construtor com os dois parmetros. Se for necessrio, a
classe android . util.AttributeSet utilizada para ler os parmetros denidos no XML.
A classe Minhaview implementa o mtodo onDraw(Canvas) e desenha um retngulo, uma
linha e um crculo, utilizando os mtodos da classe android.graphics.Canvas. Alm das
coordenadas x e y para desenhar as formas geomtricas, foi criado um objeto do
tipo android.graphics.Paint, o qual dene as cores do desenho. Para isso, foram cria
dos trs objetos Paint (pincel) e para cada um foi atribuda uma cor RGB diferente.
Para utilizar essa nova classe em algum arquivo de layout, basta inserir uma tag
com seu nome completo, neste caso: .
/res/Iayout/activity_exempIo_minha_view.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<br.com.livroandroid.cap07_view.canvas.MinhaView
android:id="@+id/canvas"
android:layout_width="match_parent" android:layout_height="match_parent" />
240 Google Android - 4 edio
._~. .~~~ .a izaaoaeaaer
editor, mesmo com um

_ _' ~'
te' a voce. acnv' ~
A gura 7.19 mostra a pr-visualizao dessa tela pelo ll ._ d t I 'I I
componente desenhado manualmente. Como a pre visua li; 1 / J 1 u o
suficiente para entender o exemplo, nao criaremos aqui nen uma^ ir;
que utilize esse arquivo de layout. Isso ca como exercicio par

Figura Z19 - Componente customizado desenhado manualmente.

7.27 Nunca utilize pixels


j foi falado anteriormente para nunca utilizar pixels ao definir tamanhos de
views e espaamentos em arquivos de layout XML. O mesmo conceito vale ao
desenhar cdigos utilizando a API de Canvas.
g P CI
No exem lo anterior, utilizamos o se uinte cdi o ara desenhar um uadrado:
// Desenha um quadrado
canvas.drawRect(20, 20, 200, 200, pnceIAzu1);

No entanto, esse cdigo que desenha o quadrado tem um problema, pois esta
usando valores xos em pixels. Isso vai trazer resultados diferentes em telas qu
apresentem diferentes resolues e densidade. O correto seria utilizar valores em
dp (density independent pixel) e converter o valor para pixel utilizando a den
sidade de cada tela. Por exemplo, 100px pode ser convertido para 75px, 100px.
15Opx, 200px, 300px, conforme a densidade da tela do aparelho. Essas converse
PfC5am SCF feiras HO COdig0 para garantir que o resultado ser o mesmo ind
pendentemente da resoluo e densidade da tela.

A 5gU1f,P0dm05 VU Uma funo que converte o valor de dp para pixel.


Captulo 7 n Interface grca - View 241
// Converte um valor em dp para pixels
public oat toPixels(oat dip) {
Resources r = getContet().getResources();
oat densidade = r.getDisplayMetrics().density; // Densidade da tela
int px = (int) (dip * densidade + 0.5f);
return dip;
}

Portanto, o correto para desenhar um quadrado utilizar um cdigo assim:


// Desenha um quadrado
canvas.drawRect(toPiels(20), toPiels(20), toPiels(200), toPiels(200), pincelAzul);

Acredito que talvez seja cedo para explicar por que isso necessrio. Prero
continuar o livro com conceitos simples e focar no que voc vai utilizar no dia a
dia. Ainda temos alguns captulos essenciais pela frente e logo vamos comear o
desenvolvimento do aplicativo dos carros.
A metodologia do livro explicar cada conceito de uma vez. Quero focar nos
conceitos principais, aqueles de que, tenho certeza, voc vai precisar no dia a dia.
Conheo muitos desenvolvedores que fazem aplicativos e no sabem explicar o
que dp (density independent pixel), por isso creio que esse assunto possa car
para depois. Para mais detalhes, leia o captulo 30, sobre como suportar diferentes
tamanhos de telas.
f . CAPTULO
Fragments
\_`_
`

Um fragment um componente de cdigo reutilizavel, responsavel por criar sua


prpria view, tratar os eventos e gerenciar o seu proprio c0ntUClO.
Uma activity pode conter um ou mais fragments que podem ser adicionados no
layout como se fossem views, porm os fragments tm comportamento proprio
e so autogerenciveis.

8.1 Como surgiram os fragments no Android 3.0 Honeycomb


Com a popularizao dos tablets e a grande busca dos usurios por esses dispo
sitivos, surgiu a necessidade de otimizar e customizar o Android para usufruir ao
mximo do tamanho de tela disponvel nesses dispositivos. Foi assim que surgiu
o Android 3.0 Honeycomb, a primeira verso do Android otimizada para tablets.
E com o Honeycomb, nasceu a API de fragments, originalmente utilizada para
organizar a grande tela dos tablets em pequenos componentes, mas aos pouc0S
todos perceberam que fragments eram muito mais do que isso.
Ao desenvolver para smartphones, geralmente temos uma tela simples, pois 0
espao disponvel limitado. Dessa forma, o modelo tradicional com uma activity
e uma view, no qual a activity controla toda a lgica da tela, sempre atendeu HS
necessidades. Mas criar aplicaes para tablets uma arte, e vrios fatores precisam
ser levados em Considerao. O mais importante de todos o tamanho da tela.
que dv ser 3Pf0ViI8Cl0 ao mximo. Muitas vezes, necessrio preencher a rel
com varias views, cada uma com um contedo diferente

A gura 8.1 compara o modelo tradicional de uma aplicao do tipo lista e dl'
lhes, executando no smartphone Qu tablet N o smartphone, duas telas precisam
ser utilizadas para fazer a navegao da lista para a tela de detalhes No tablet
podemos utilizar uma nica tela, aproveitando ao mximo o espag disponvel.

242
. * si 5
(aPtuIo 8 n Fragments 243
_jp.rAA
^V"Y ^ Avify B Activity A mm aos fragmems
Figura 8.1 - Fragment que divide a tela em pedaos.

Dica: se quiser ver um aplicativo com duas telas de lista e detalhes, abra o projeto
Planetas-Activity no Android Studio. Neste projeto, temos uma lista de planetas
e ao clicar na lista o planeta mostrado em outra_tela. Durante este captulo,
vamos trabalhar em cima deste exemplo.

Nesta gura podemos ver que a aplicao para smartphone utiliza a Activity A
e a Activity B como de costume. Na aplicao para tablet, como existe somente
uma tela, ou seja, apenas uma activity dentro dela o contedo separado em dois
fragments. Basicamente um fragment um componente que pode ser inserido
dentro da activity e esse componente ca responsvel por:
1. Criar a view para preencher determinado espao, alm de controlar o seu
estado e tratar os eventos.
2. Pela lgica de negcios para buscar os dados de um web service ou banco
de dados.

3. Por atualizar a sua view de forma independente da activity e de qualquer


outro fragment da tela.
Eu costumo dizer que um fragment uma mnactvty, que tem sua prpria view e
lgica, alm de ser responsvel por gerenciar seu prprio conteudo.

Nota: talvez este captulo seja um pouco avanado neste momento, tudo depende
do seu perl. O fato que antes de comearmos a desenvolver o aplicativo dos
carros euaproveito
assunto 7
preciso explicar o que alguns
para abordar um Fragment. E como
topicos mais vou ter de
avanados, explicr
como ta etso
e gerenciamento de estado do fragment. Mesmo que o assunto seja um pouco
avanado continue estudando, pois o livro servira de consulta mais tarde.
7
244 Google Android - 4 edio
8.2 Fragments muito mais do que dividir a tela em duas partS
lista explicao de duas activities no smartphone C llP"5 Ulflil 1lL`UVll)' corn
dois lragments no tablet e classica. inclusive a documentacao ohctal do Androitl
utiliza este exemplo. Mas isso mttitas vezes confunde quem esta ilPl`L`ll(iL`ll\ii>. e
alguns desenvolvedores acham que liragments servem para dividir a teia em duas
partes nos tablets.
Na verdade, os lragments sao componentes que ficam espalhados na tela. sendo
qtte um de setis principais objetivos e reutilizar a logica entre a versao 1i.}Til"ii\I`;
e tablet. Para comearmos bem, vamos acabar com o mito de que um if' fiflift
serve para dividir a tela do tablet em duas partes. A ligura 8.2 mostra algo z_litt-tarte.
a tela do tablet dividida em tres partes.
:rf

{' l,ivtoAnlrt-id

imgnwnt' Tivxt 2

Frqmentl TQIIO 1.

Frqmontl Tutu 3.

if' sz-i~*'
:guru i em tres ttrres rom rttgrnenls.
t z ttn tt_v ivididti

lisse tipo de tela e bem connnn nos tablets, pois assim voc consegue mostrar
varias iniormat`es ao mesmo tempo para o usuario, /\ principal razo de quebrar
a tela em pedaos e para simplihcar o codigo da activity, pois podemos dizer que
cada tragment e responsavel por determinada parte do layout.
A figura 83 mostra um dos meus primeiros projetos para tablets, qurmdti Qgmdei
lragments pela primeira vez. Neste aplicativo, cada pedao da tela e um fragment.
1 .' \ \_' .. t \-~, , ',_ `f .:. . _ _ _
cmo a lista de indices, lista de notcias, lista de videos. area central. grtilice
etc ( s beneltuos dessa organizaeao sao imensos, pois os lragments deixam tt
Captulo 8 n Fragments

cdigo muito mais lim o e o '


I' z
P 8an1Zad0 P018 Cada componente faz apenas o que
tem de fazer. Cada parte da tela um gm
245

P0nente separado e independente dos


outros. Por exemplo, a lista de ndices busca os d d os em um web service e cria

, pois e a no f
o Lstview de forma independente do resto da tel a. No nal, o cdigo da activity
fica bastante reduzido ou talvez vazio ` 1 8
de1ga do aos fragments. az nada, e todo o trabalho e

,,
f,ml-...-_.zz
,,:*~Ef- 2 ff yr,

Ponrusus
A TUTORIAL

saem:
wasrts j

3M&FBO'E.

Figura 8.3 - Dividindo a tela em fragments.

A gura 8.4 mostra outro aplicativo para tablet, em que mais uma vez este conceito
de dividir a tela em partes foi utilizado. Cada parte um fragment, e novamente
isso ajudou a separar as responsabilidades e organizar o cdigo. Se um aplicati
vo desse tamanho fosse criado apenas 'com uma activity o XML de layout seria
imenso e o cdigo-fonte para gerenciar toda a lgica tambm. No entanto, com
fragments a activity apenas divide o seu layout em partes e insere cada fragment
no seu devido espao. Cada fragment por sua vez vai criar a view e gerenciar o
contedo. Simples, no ?
Trabalhar com fragments, porm, muito mais do que apenas separar a tela em
pedaos, pois um dos principais objetivos da API criar um componente de c
digo reutilizvel. Afigura
8.5 mostra uma aplicao compatvel com smartphones
d d afiaura
e tablets. A parte esquer g mostra a verso para tablets. Veja que o
tablet est mostrando a lista de ndices ein determinado local da tela. E na parte
da direita podemos ver a mesmaaplicao no smartphone, corn a mesma lista
h (iooqlv Android 4-'1111W
_\, \ \ _ \ . . 1..1 1 114 1.11.
1

, 1 1 .. . 1 .`\.1..1~1.
1 1 ._
11~111111111~ l'~l1' 1' 11111 1~\1'1111 '111 1111 \I1I 11 11~. 11.11 HH '111 ~' UI 1 ""~11

11
\ "\ 1 `\ \|(. `\( "
111111111111111111' 11-111111 .1\111~1\1.111|1111111|11.1111.1111111111`.11111 -HU 1 * 111111

111\1
1 111111 1 |`1l 1 1111 1 11 111111|11~ 1111 \\1~11~1~1\111 11111111-1'|'|l "1`1'1
1
1

1111 111 1\111111 11 11\ 111111111111 |111~1 1~..1 1111111111111 .11 11 1.l\1'111 |\.l|-1 1 111111111 1111 111
1

V" ` ' V 7 V N
1|n,. '
E1' kd 1
111.11 11111111111 1111 1.11111'l.

nu-zum-.~un.~..,..

vlmm UUOL (11I110\ -..... ,,A

^1`
______ _ _ ___ ___- --f_ ; ~ ~-_ ::f1;_^^

otl
' ` ,1zz1z
1 n-. :O
1;-:.\.fz
A Mvwuz..
>|11.11

u.u-4.1.4 14.z~-' -1 .5
PM (em H 1muu11u111\|- 111\M1 111111111 "\.'|l'\ 1111 _1gu~v- 1111 Riu ,mmu
lap 13
I\0\11~1 "I '\ 1 l 1 *" *' 7`<\ ` ` `\,\1; 1
x ` ` ' 1 1\\I\\\ \ ....
.nu - ' .`,,,,___,,,_,,,_,.. ,_ ,,._ _. _ _ _ ,.;,.1,__,. M`
7 h1num1 `
1 11.
A Mmh.
_11.1 \ h.
` ` 1.. \n:;\`..=Q1.
v 'up ,,_1._g,1,_ __, _ .J. ;
\l.J1I4y|J1 qm
1m11v~1oJ~ 1o111~ . ,_ ,, . . . .
NI11|n1|111
^ = ' "' "" ""*"1- 1 ' *' `V , pru
_ . ~ 11
1
:r-:~:_~ ~'~' ' Y-~fl\~_~^_~ ' _ _ _ _ :_ 7 _ _____v__

11111114
NUM:-u
'OHMm1\u
0.=q141w x4s111.
vuguw rw-.14|1\vu 111!
x mqu||4h1N\\vJ1 I1
1114 11.1 zmkmu O 11 1 Vnw u1.zz1vu 1 V'
_ ' ''"'
"'"
1ih\ll
um Bmw Jtanvoiudia ` 1-,
10.1 qzuu dr|||\wn\11 nv
.,,,
mniy npnu
r1qyn\u _, 41h-quo:
. _ 1nUudumm!-cilcul ' 11- ~
1- 1. AbAvzwuL\u
1hm.\h&-1
new
In--up U1 um k11donbc1| vou hn pano 00"'~*
111- 1 uvunwu anti 1'' L ml Hbaciu.
I uR\\$"M

11g11.11\.-1 1)11'111111111.!1'1.11'111111\1;1111'n1

1*1_Q11118.$ R1'11!111;.1111l.HW,(.,,,, .\. . ,_


^\111;111111~
(\ \\]`) z~_
I`h\\- l"`
- . `l`_*CI\\_"I\'iI`\`\Hl"
.-1 / 111. 111111 tl 111..111111111111'.111.1111l111111'.

`|
\ ` `\ \ ' 1 \ 1 \\
1

l[11|\.lll\11s lI11111\III11.11411115I11;1I`l|1|111 U h,u. Hb mU`. U uxh .u\1 111


.\
Captulo 8 I Fragments 247
Google Play que utiliza tabs na action bar. Ao selecionar uma tab, o contedo
do centro atualizado, as semvoce^ reparar ng f ~ entre telas.
eita a navegaao
J aprendemos no livroque
criar uma nova activi - .
Sempf que for aberta uma nova tela, necessrio
_ I I o ty mas neste caso o aplicativo permanece na mesma tela,
Pois e necessario
apnas O conteudotrocar
centralfque mostra a tab selecionada.
Por isso, ao utilizar tabs na action bar, o contedo da tela precisa ser um fragment,
ois um fra ment um co ~ . _
P g mP0nH que pode ser inserido, removido e substi

fl _
tudo de determ inado local do layout. Ao selecionar uma tab, basicamente um

9 ._
fragment substitudo por outro,

za W Q.
` ( z
Aws._ ~. z z- O* ~aeza
ll- iii!P P _ ` z~
CTERORS HOM roppggg mpmes WES ~
w
waities GAMES _ Io
i M *I afff*
i?zumz=@s
,_ ,HH,
Hacks ieferemce , _ _
1 ECE _
' ws.. L.
Hom "f'&...f...'; ;1-._ ,f
- ~ - _ ` _ _ _ ._ _ . . __ _ ,m ,a;z,,.z mp mn wma 7 PN0 TOP Fm rovoaossmc mv NEWPMI

Crzmfcs
., _, ` _ ~ . zw* Aplicativos Sociais 2 What Asp M g

Euction
***** -mw .nn _, _
.....

CIz.mmumz,atcn mstagram mzszme whatzzzp z


P (Twuter E Mssengcf

e r wtndfi. ._
Elhmmm T F! commendedfo You 5 F b < vi g

Figura 8.6 -Aplicativo do Google Play.

A figura 8.7 mostra o padro de navegao Navigation Drawer, muito utilizado nos apli
cativos modernos. S para dar uma ideia, o Gmail eYouTube utilizam esse padro,
que conhecido popularmente por menu lateral' Tal padro de navegao, da
mesma forma que as tabs, representam a navegao top-level do aplicativo com as
sees mais comuns. Nesse caso existe apenas uma activity pois, ao abrir o menu
lateral e selecionar uma opo, apenas o contedo central alterado, sem fazer a
navegao de telas. Na prtica, ao selecionar uma opo do menu, um fragment
substitudo por outro.
A gura 8.8 mostra outro caso no qual podemos utilizar um fragment, para
solucionar o problema de i nserir o banner na parte inferior da tela. Como esse
e ser inserido em todas as telas, o ideal que ele
banner um componente e dev
-lo no layout. O fragment sozinho vai criar a
seja um fragment, pois basta inseri
view e buscar o contedo necessrio para desenhar O b21Y1nf
Google Android -- 4 edio

zh
uu- """"" 1 n
"' `,:.
i
.z.2., "*"i**^~>.

Q 0(iVa' 1
O nV Q 4

|(\V n H --

._ vf

' #'=_ .
..$;.

!iAqum 8.7 ~- Mwm mm mL'(QUlI (IIIWUIL

'll{lll' 8.8 - /\l1..:!i1''nunumms.

a:cs~.;\;I ~ ._
h \F 'm
~. \1_lll\5 xnnplm um qu.u m1cnm5 uulwdl. A \l)I kh.
~ . .1 .\@\\*|'l. SIl\II`l|)hUHL' uu p.n.\
K ' 'm` sc ;\ w;~;
\ H \ m`m'/" h!
` ` ` ( , L` | ~
lulull/..n'u111px\c1m~. /\lllLlIIl\L'I\lL` |1\cs1\\s\z(,ux ,. W um] H ix l 1 U M
. Hk`UIH\'Il\l.l l -n ' ~ ' ~ ~' z z .
` '|I"' ~\1:~l.1 IL`Il\'Il)' cm um I`;\gl1\L`I\l' ;\ssim\ W
um Im vmc )I`k`\`I8.lI` r ~.\ w' V- - .~. o . . ..
* I 'U\Lll.\| x asa u|\1p|\c|1lc,nd<L~- |UmU_
Captulo 8 I Fragments 2 49
8.3 API de Fragments

Antes de comearmos a estudar os exemplos com cdigo-fonte, vamos aprender


as principais classes da API de Fragments,

android.app.Fragment

Classe que o fragment deve estender. necessrio sobrescrever o mtodo


onCreateView(inater, container,bundIe)}para.criar a vievv

android.app.FragmentManager

Classe que gerencia os fragments pela API. Contm os mtodos


findFragmentById(id) e findFragmentByTag(tag) utilizados para encontrar os
fragments no layout, de forma similar ao mtodo findviewById(id) que uma
activity utiliza para buscar uma view

android.app.FragmentTransaction

Classe utilizada para adicionar, remover ou substituir os fragments dina


micamente no layout.
Essas trs classes s podem ser utilizadas no Android 3.0 ou superiores, por isso
foi criada a biblioteca de compatibilidade v4, compatvel com Android 1.6 (API
Level 4) ou superior. Para congurar a dependncia para a biblioteca v4, basta
adicionar uma linha no arquivo app/builzigradle.

app/buiId.gradIe

dependencies {

// Dependncia da biblioteca de compatibilidade v4


compiie "com.android.supportzsupport-v4:21+"
I

.. - inavas.
Feito isso, podemos utilizar as classes da biblioteca de suporte que cam HO
ara manter a compatibilidade com verses anteriores,
pacote android.support.v4. P
recomenda-se utilizar as seguintes Cl21SSS, U0 lugar das
~ android.support.v4.app.FF9|@t

android.support.v4.app.FF9F\@tMaa9er

android.support.v4.DP-Ffa9etTra"SaCton
250 Google Android - 4' edio
. .__....de,.z
A hihlioteca
-_-._z,, ,..z
compatibilidade
`na
e dtstribuida
ere
existem odere~
pelo SDK M3 8 v `
.P Wber
atualizaoes, por isso recomendo que vote sempre verif-lue se HSOS
novas da hihlioteca.

Nota: neste livro, todos os exemplos de fragments utilizam a classe androtd.support_


v4.app.Fragment; lemhre-se desse detalhe ao escrever o codigo e testar os exemplos.

Para fechar o assunto, falta um pequeno detalhe. A classe Fragmentanager e uma


das principais classes da API e ela recuperada com o seguinte codigo dentro de
uma activity ou fragment.
android.app.FragmentManager fm = getFragmentManager();

O mtodo getFragmentManager() retorna a verso nativa da classe, ento no


podemos utiliz-lo. Por isso, todas as activities do projeto devem estender
android.support.v4.app.FragmentActvty ou android.support.v7.app.AppCompatActvty, que
contm o mtodo getSupportFragmentManager(), o qual retorna a classe de compatibilidade

androd.support.v4.app.Fragmentllanager fm = getSupportFragmentHanager();

Nota: a classe FragmentActi.vty me de AppCompatActivty. Portanto, sempre que voc


utilizar a classe AppCompatActivty para utilizar a action bar de compatibilidade,
ganhamos de brinde o acesso biblioteca dos fragments.

8.4 Hello World fragment


Para car mais fcil de entender o assunto, vamos ver um pouco de cdigo-fonte
e brincar um pouco com os fragments. Crie o projeto com o nome HelloFragments.
com a activity ManActvty e o template Blank Attivity.

Na activity principal no vamos fazer nada, e seu cdigo ser simples


conforme demonstrado a seguir. Note, porm, que ela dgvg ser filha de
androd.support.v4.app.FragmentActvty. Se voc quiser, pode configurar a depen
dncia da biblioteca v7 da action bar de compatibilidade e utilizar a classe
androd.support.v7.app.AppCompatActvity, pois a classe AppCompatActvty lha de
FragmentActvity. Ento, para estes exemplos, tanto faz_
Captulo 8 I Fragments
251

MainActivity.java
public class MainActivity extends d '
@0Verrde an r1d-SUPPOF-V4-pp.FragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity nain)~
// 0 FragnentManager nececessrio para brincar com os fragnents

} android'supprtV4'app'Fragmentaaef fm = getSupportFragnentManaqer()~
}

Para utilizar a biblioteca de compatibilidade V4, congure o arquivo build gmdle


app/buiId.gradIe

dependencies {

conpile "con.android.support:support-v4:21+"
}

Feito isso, crie a classe Fragnentl conforme demonstrado a seguir.

Fragment1.java
public class Fragmentl extends android.support.v4.app.Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedlnstancetate) {
View view = inater.inate(R.layout.fragnent_1, container, false);
// O fragnent livre para ter qualquer lgica aqui
return view;
}

Note que essa classe lha de android.support.v4.app.Fragnent. Um fragment deve


criar e retornar a view no mtodo onCreate iew - ,
V' (inater, container,bundle) Aseguir
podemos ver o arquivo de layout que o fragment vai inar para retornar a view

/res/layout/fragment_1.xml
<?ml version="1.0"
tf-8"?> encoding="U _ N
<LinearLayout xmlns:android="hD// _HH
schenas.android.con/apk/res/android

andr0d_1ay0Ut wdth:"matCh parent" andr0id:layout_height= match_parent


252 Google Android - 4 digo
androd:orientaton="vertica1" androd:gravty="center">
<TextView
androd:id="@+d/text"
1 t ayou
android:1ayout_wdth="match_parent" androd: hei ht="wraD_Ct@f"
_9
android:gravity="center"
android:text="Fragment 1" />
</LnearLayout>

Com o fragment criado, basta adiciona-lo no layout da activity seja de lorma


esttica no XML ou dinamicamente pela API. Para adicionar um fragment no
arquivo XML de layout da activity utilizada a tag <fF9I'\t> f0fm9Cl0 a
largura e altura do fragment como se fosse uma view O atributo class recebe 0
nome completo da classe do fragment.
Nota: Eu particularmente no gosto de utilizar 0 Re1atveLayout.Veja que nos exem
plos do livro voc vai encontrar o LnearLayout ou FrameLayout como a raiz do layout.

/res/layout/activity_main.xml
<LnearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
mlns:too1s="http://schemas.androd.com/tools"
androd:1ayout_width="match_parent" android:1ayout_heght="match_parent"
fragment android:id="@+d/fragi"
android:1ayout_width="match_parent" android:1ayout_heght="match_parent"
c1ass="br.con.Iivroandrod.cap09_he11ofragnents.Fragment1"
tools:1ayout="@1ayout/fragment_1" />
</LnearLayout>

Ateno: cuidado ao digitar o nome da classe do fragment no arquivo XML


da activity. Se voc errar o nome, a aplicao vai lanar um erro em tempo de
execuo. Note que o Android Studio inclusive ajuda a completar o nome da
classe para evitar erros. Outra dica , sempre depois de digitar o nome da classe.
segure a tecla Ctrl e clique com o mouse para abrir o arquivo. Se o arquivo da
classe abrir, est tudo ok.

A gura 8.9 mostra a pr-visualizao do arquivo da activity O segredo da pf'


-visualizao do layout o atributo tools:1ayout="@1ayout/fragment_1", que utilizadv
somente pelo editor visual e faz com que o layout do fragment seja inserido neste
local, apenas para fazer a pr-visualizao.
Captulo 8 I Fragments
253

HGU W Id? g

Figura 8.9 - Pr-visualizao.

Pronto! Se voc executar o projeto no emulador, deve ver a mensagem Hello World
Fragment na tela. Para fechar esse tpico, veja que o fragment tem um identificador
que foi declarado no layout.
<fragment androd:id="@+d/fragl" ...

Isso signica que em qualquer local do cdigo podemos recuperar esse fragment
com o mtodo fndFragmentById(d) da classe androd.support.v4.app.FragmentManager. O
cdigo a seguir demonstra como encontrar um fragment pelo id.

ManActvity.java
public class MainActtvty extends androd.support.v4.app.FragmentActivty {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.layout.activty_ma);
F ntMana er();
androd.support.v4.app.Fragmentanager fm = 9f5UPPf"`t "a9' 9
Fr3gment1 frag1 = (Fragmenti) fm.ndFragmentById(R.td.frag1);
f rag1 ..vocePodeChama r0MetodoQuePrecsarA<IU( )5
}

}
254 Google Android - 4' edio
.. ,_.ultimo
komo _. , __exemplo,v:1mos
'vz]
r1APl ara adicionaru
Qdcmonstrarcomo
za do utiliza
la ouPm
lragment dinamicamente no layout. Nesse caso, fCm0 * g Y . . . ~ _ . radoa se uir.
da activity e dcixc o layout vazio, conforme demonst z 8

t /res/layout/activity_main.xml
<FrameLayout minszznr0i="hiipz//szhemz.anroi.com/pk/res/dfd"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" androidzlayou _ 619
android:id="@+id/layoutFrag">
</span></span> <span class='ocr_word' id='word_1_80' title="bbox 804 1149 996 1200"><span class='xocr_word' id='xword_1_80' title="x_wconf -3">Fragment</span></span> <span class='ocr_word' id='word_1_81' title="bbox 1027 1147 1120 1190"><span class='xocr_word' id='xword_1_81' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_82' title="bbox 1150 1144 1344 1189"><span class='xocr_word' id='xword_1_82' title="x_wconf -1">inserido</span></span> <span class='ocr_word' id='word_1_83' title="bbox 1374 1144 1468 1196"><span class='xocr_word' id='xword_1_83' title="x_wconf -1">aqui</span></span> <span class='ocr_word' id='word_1_84' title="bbox 1498 1154 1544 1185"><span class='xocr_word' id='xword_1_84' title="x_wconf 0">no</span></span> <span class='ocr_word' id='word_1_85' title="bbox 1573 1141 1718 1194"><span class='xocr_word' id='xword_1_85' title="x_wconf 0">layout</span></span> <span class='ocr_word' id='word_1_86' title="bbox 1751 1125 2170 1188"><span class='xocr_word' id='xword_1_86' title="x_wconf -8">"@+d/YOUFFHQ"</span></span> <span class='ocr_word' id='word_1_87' title="bbox 2235 1137 2304 1161"><span class='xocr_word' id='xword_1_87' title="x_wconf -5">"></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_14' title="bbox 590 1227 934 1283"><span class='ocr_word' id='word_1_88' title="bbox 590 1227 934 1283"><span class='xocr_word' id='xword_1_88' title="x_wconf -3"></FraneLayout></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_7' title="bbox 1900 948 2479 1008">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_15' title="bbox 1900 948 2479 1008"><span class='ocr_word' id='word_1_89' title="bbox 1900 969 1918 1008"><span class='xocr_word' id='xword_1_89' title="x_wconf -6">t</span></span> <span class='ocr_word' id='word_1_90' title="bbox 1950 962 1969 1006"><span class='xocr_word' id='xword_1_90' title="x_wconf -5">h</span></span> <span class='ocr_word' id='word_1_91' title="bbox 2005 962 2011 970"><span class='xocr_word' id='xword_1_91' title="x_wconf -6">'</span></span> <span class='ocr_word' id='word_1_92' title="bbox 2051 948 2479 1005"><span class='xocr_word' id='xword_1_92' title="x_wconf -6">ht="natch_parent"</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_8' title="bbox 590 1328 2881 1594">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_16' title="bbox 590 1329 2880 1414"><span class='ocr_word' id='word_1_93' title="bbox 590 1349 704 1414"><span class='xocr_word' id='xword_1_93' title="x_wconf -3">Veja</span></span> <span class='ocr_word' id='word_1_94' title="bbox 732 1365 835 1414"><span class='xocr_word' id='xword_1_94' title="x_wconf -2">que</span></span> <span class='ocr_word' id='word_1_95' title="bbox 862 1366 894 1399"><span class='xocr_word' id='xword_1_95' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_96' title="bbox 921 1354 1226 1408"><span class='xocr_word' id='xword_1_96' title="x_wconf -1">LinearLayout</span></span> <span class='ocr_word' id='word_1_97' title="bbox 1255 1343 1468 1397"><span class='xocr_word' id='xword_1_97' title="x_wconf -3">recebeu</span></span> <span class='ocr_word' id='word_1_98' title="bbox 1496 1361 1587 1395"><span class='xocr_word' id='xword_1_98' title="x_wconf -1">um</span></span> <span class='ocr_word' id='word_1_99' title="bbox 1615 1334 1985 1393"><span class='xocr_word' id='xword_1_99' title="x_wconf -2">identicador</span></span> <span class='ocr_word' id='word_1_100' title="bbox 2009 1329 2772 1388"><span class='xocr_word' id='xword_1_100' title="x_wconf -2">android:id="@+id/layoutFrag",</span></span> <span class='ocr_word' id='word_1_101' title="bbox 2794 1347 2880 1398"><span class='xocr_word' id='xword_1_101' title="x_wconf -7">que</span></span></span>
<span class='ocr_line' id='line_1_17' title="bbox 590 1416 2881 1505"><span class='ocr_word' id='word_1_102' title="bbox 590 1439 702 1488"><span class='xocr_word' id='xword_1_102' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_103' title="bbox 728 1435 979 1489"><span class='xocr_word' id='xword_1_103' title="x_wconf -3">utilizado</span></span> <span class='ocr_word' id='word_1_104' title="bbox 1004 1455 1126 1505"><span class='xocr_word' id='xword_1_104' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_105' title="bbox 1151 1435 1418 1489"><span class='xocr_word' id='xword_1_105' title="x_wconf -2">adicionar</span></span> <span class='ocr_word' id='word_1_106' title="bbox 1440 1433 1887 1487"><span class='xocr_word' id='xword_1_106' title="x_wconf -2">dinamicamente</span></span> <span class='ocr_word' id='word_1_107' title="bbox 1912 1445 1945 1479"><span class='xocr_word' id='xword_1_107' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_108' title="bbox 1971 1423 2235 1493"><span class='xocr_word' id='xword_1_108' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_109' title="bbox 2262 1438 2416 1473"><span class='xocr_word' id='xword_1_109' title="x_wconf -2">nesse</span></span> <span class='ocr_word' id='word_1_110' title="bbox 2441 1416 2622 1487"><span class='xocr_word' id='xword_1_110' title="x_wconf -2">layout</span></span> <span class='ocr_word' id='word_1_111' title="bbox 2648 1416 2761 1486"><span class='xocr_word' id='xword_1_111' title="x_wconf -2">pela</span></span> <span class='ocr_word' id='word_1_112' title="bbox 2781 1424 2881 1476"><span class='xocr_word' id='xword_1_112' title="x_wconf -2">API.</span></span></span>
<span class='ocr_line' id='line_1_18' title="bbox 591 1511 2794 1594"><span class='ocr_word' id='word_1_113' title="bbox 591 1528 640 1578"><span class='xocr_word' id='xword_1_113' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_114' title="bbox 664 1525 852 1594"><span class='xocr_word' id='xword_1_114' title="x_wconf -2">cdigo</span></span> <span class='ocr_word' id='word_1_115' title="bbox 874 1525 940 1579"><span class='xocr_word' id='xword_1_115' title="x_wconf -2">da</span></span> <span class='ocr_word' id='word_1_116' title="bbox 963 1530 1167 1594"><span class='xocr_word' id='xword_1_116' title="x_wconf -2">activity</span></span> <span class='ocr_word' id='word_1_117' title="bbox 1189 1545 1292 1594"><span class='xocr_word' id='xword_1_117' title="x_wconf -2">que</span></span> <span class='ocr_word' id='word_1_118' title="bbox 1315 1530 1479 1578"><span class='xocr_word' id='xword_1_118' title="x_wconf -2">insere</span></span> <span class='ocr_word' id='word_1_119' title="bbox 1501 1544 1534 1576"><span class='xocr_word' id='xword_1_119' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_120' title="bbox 1557 1522 1816 1591"><span class='xocr_word' id='xword_1_120' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_121' title="bbox 1838 1538 1909 1572"><span class='xocr_word' id='xword_1_121' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_122' title="bbox 1932 1516 2112 1586"><span class='xocr_word' id='xword_1_122' title="x_wconf -1">layout</span></span> <span class='ocr_word' id='word_1_123' title="bbox 2135 1511 2280 1584"><span class='xocr_word' id='xword_1_123' title="x_wconf -1">pode</span></span> <span class='ocr_word' id='word_1_124' title="bbox 2302 1530 2386 1565"><span class='xocr_word' id='xword_1_124' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_125' title="bbox 2405 1525 2543 1563"><span class='xocr_word' id='xword_1_125' title="x_wconf -3">visto</span></span> <span class='ocr_word' id='word_1_126' title="bbox 2566 1529 2592 1563"><span class='xocr_word' id='xword_1_126' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_127' title="bbox 2617 1529 2794 1579"><span class='xocr_word' id='xword_1_127' title="x_wconf -3">seguir.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 594 1688 1153 1767">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 594 1688 1153 1767"><span class='ocr_word' id='word_1_128' title="bbox 767 1701 1153 1767"><span class='xocr_word' id='xword_1_128' title="x_wconf -2">MainActivity.java</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 591 1826 2660 2804">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 591 1827 2468 1890"><span class='ocr_word' id='word_1_129' title="bbox 591 1835 735 1889"><span class='xocr_word' id='xword_1_129' title="x_wconf -1">public</span></span> <span class='ocr_word' id='word_1_130' title="bbox 764 1836 884 1881"><span class='xocr_word' id='xword_1_130' title="x_wconf -1">class</span></span> <span class='ocr_word' id='word_1_131' title="bbox 912 1838 1207 1890"><span class='xocr_word' id='xword_1_131' title="x_wconf -3">HainActivity</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1235 1836 1404 1880"><span class='xocr_word' id='xword_1_132' title="x_wconf -1">extends</span></span> <span class='ocr_word' id='word_1_133' title="bbox 1433 1830 2418 1889"><span class='xocr_word' id='xword_1_133' title="x_wconf -3">android.support.v4.app.FragnentActivity</span></span> <span class='ocr_word' id='word_1_134' title="bbox 2451 1827 2468 1883"><span class='xocr_word' id='xword_1_134' title="x_wconf -2">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_21' title="bbox 678 1919 898 1971"><span class='ocr_word' id='word_1_135' title="bbox 678 1919 898 1971"><span class='xocr_word' id='xword_1_135' title="x_wconf -3">@0verride</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_22' title="bbox 678 2001 1968 2056"><span class='ocr_word' id='word_1_136' title="bbox 678 2003 898 2056"><span class='xocr_word' id='xword_1_136' title="x_wconf -1">protected</span></span> <span class='ocr_word' id='word_1_137' title="bbox 926 2003 1021 2047"><span class='xocr_word' id='xword_1_137' title="x_wconf -3">void</span></span> <span class='ocr_word' id='word_1_138' title="bbox 1050 2002 1419 2055"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">onCreate(Bundle</span></span> <span class='ocr_word' id='word_1_139' title="bbox 1449 2002 1916 2054"><span class='xocr_word' id='xword_1_139' title="x_wconf -2">savedInstanceState)</span></span> <span class='ocr_word' id='word_1_140' title="bbox 1951 2001 1968 2056"><span class='xocr_word' id='xword_1_140' title="x_wconf -1">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_23' title="bbox 768 2086 1625 2139"><span class='ocr_word' id='word_1_141' title="bbox 768 2086 1625 2139"><span class='xocr_word' id='xword_1_141' title="x_wconf -2">super.onCreate(savedInstanceState);</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_24' title="bbox 768 2169 1726 2224"><span class='ocr_word' id='word_1_142' title="bbox 768 2169 1726 2224"><span class='xocr_word' id='xword_1_142' title="x_wconf -3">setContentView(R.layout.activity_main);</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_25' title="bbox 768 2251 1880 2309"><span class='ocr_word' id='word_1_143' title="bbox 768 2251 811 2305"><span class='xocr_word' id='xword_1_143' title="x_wconf 0">//</span></span> <span class='ocr_word' id='word_1_144' title="bbox 840 2252 1035 2296"><span class='xocr_word' id='xword_1_144' title="x_wconf -2">Adiciona</span></span> <span class='ocr_word' id='word_1_145' title="bbox 1064 2266 1085 2297"><span class='xocr_word' id='xword_1_145' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_146' title="bbox 1115 2252 1308 2307"><span class='xocr_word' id='xword_1_146' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1336 2253 1656 2298"><span class='xocr_word' id='xword_1_147' title="x_wconf -3">dinamicamente</span></span> <span class='ocr_word' id='word_1_148' title="bbox 1686 2254 1781 2309"><span class='xocr_word' id='xword_1_148' title="x_wconf -2">pela</span></span> <span class='ocr_word' id='word_1_149' title="bbox 1809 2259 1880 2299"><span class='xocr_word' id='xword_1_149' title="x_wconf -1">API</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_26' title="bbox 767 2335 1555 2391"><span class='ocr_word' id='word_1_150' title="bbox 767 2335 1283 2388"><span class='xocr_word' id='xword_1_150' title="x_wconf -2">if(savedInstanceState</span></span> <span class='ocr_word' id='word_1_151' title="bbox 1312 2354 1357 2372"><span class='xocr_word' id='xword_1_151' title="x_wconf -1">==</span></span> <span class='ocr_word' id='word_1_152' title="bbox 1387 2336 1503 2390"><span class='xocr_word' id='xword_1_152' title="x_wconf -1">null)</span></span> <span class='ocr_word' id='word_1_153' title="bbox 1538 2336 1555 2391"><span class='xocr_word' id='xword_1_153' title="x_wconf -2">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_27' title="bbox 854 2418 2660 2482"><span class='ocr_word' id='word_1_154' title="bbox 854 2418 1795 2477"><span class='xocr_word' id='xword_1_154' title="x_wconf -3">android.support.v4.app.FragnentHanager</span></span> <span class='ocr_word' id='word_1_155' title="bbox 1824 2423 1872 2468"><span class='xocr_word' id='xword_1_155' title="x_wconf -3">fn</span></span> <span class='ocr_word' id='word_1_156' title="bbox 1899 2441 1921 2461"><span class='xocr_word' id='xword_1_156' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_157' title="bbox 1950 2428 2660 2482"><span class='xocr_word' id='xword_1_157' title="x_wconf -3">getSupportFragnentHanager();</span></span></span>
<span class='ocr_line' id='line_1_28' title="bbox 856 2503 2016 2563"><span class='ocr_word' id='word_1_158' title="bbox 856 2504 1321 2555"><span class='xocr_word' id='xword_1_158' title="x_wconf -3">FragnentTransaction</span></span> <span class='ocr_word' id='word_1_159' title="bbox 1352 2503 1396 2547"><span class='xocr_word' id='xword_1_159' title="x_wconf -3">ft</span></span> <span class='ocr_word' id='word_1_160' title="bbox 1425 2522 1446 2539"><span class='xocr_word' id='xword_1_160' title="x_wconf 0">=</span></span> <span class='ocr_word' id='word_1_161' title="bbox 1475 2504 2016 2563"><span class='xocr_word' id='xword_1_161' title="x_wconf -3">fm.beginTransaction();</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 856 2585 1688 2643"><span class='ocr_word' id='word_1_162' title="bbox 856 2588 1073 2638"><span class='xocr_word' id='xword_1_162' title="x_wconf -3">Fragmentl</span></span> <span class='ocr_word' id='word_1_163' title="bbox 1103 2585 1221 2639"><span class='xocr_word' id='xword_1_163' title="x_wconf -2">frag1</span></span> <span class='ocr_word' id='word_1_164' title="bbox 1251 2604 1272 2621"><span class='xocr_word' id='xword_1_164' title="x_wconf 0">=</span></span> <span class='ocr_word' id='word_1_165' title="bbox 1301 2599 1372 2630"><span class='xocr_word' id='xword_1_165' title="x_wconf -3">new</span></span> <span class='ocr_word' id='word_1_166' title="bbox 1402 2590 1688 2643"><span class='xocr_word' id='xword_1_166' title="x_wconf -3">Fragment1();</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 855 2667 1914 2731"><span class='ocr_word' id='word_1_167' title="bbox 855 2667 1416 2724"><span class='xocr_word' id='xword_1_167' title="x_wconf -3">ft.add(R.id.layoutFrag,</span></span> <span class='ocr_word' id='word_1_168' title="bbox 1451 2671 1914 2731"><span class='xocr_word' id='xword_1_168' title="x_wconf -3">frag1,"Fragment1");</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_31' title="bbox 856 2750 1141 2804"><span class='ocr_word' id='word_1_169' title="bbox 856 2750 1141 2804"><span class='xocr_word' id='xword_1_169' title="x_wconf -4">ft.connit();</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 590 2831 2879 3210">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 767 2833 785 2887"><span class='ocr_word' id='word_1_170' title="bbox 767 2833 785 2887"><span class='xocr_word' id='xword_1_170' title="x_wconf -3">}</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 678 2917 696 2972"><span class='ocr_word' id='word_1_171' title="bbox 678 2917 696 2972"><span class='xocr_word' id='xword_1_171' title="x_wconf -3">}</span></span></span>
<span class='ocr_line' id='line_1_34' title="bbox 678 3000 696 3054"><span class='ocr_word' id='word_1_172' title="bbox 678 3000 696 3054"><span class='xocr_word' id='xword_1_172' title="x_wconf -3">}</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 590 3116 2879 3209"><span class='ocr_word' id='word_1_173' title="bbox 590 3121 713 3170"><span class='xocr_word' id='xword_1_173' title="x_wconf -1">Para</span></span> <span class='ocr_word' id='word_1_174' title="bbox 747 3120 946 3178"><span class='xocr_word' id='xword_1_174' title="x_wconf -2">inserir,</span></span> <span class='ocr_word' id='word_1_175' title="bbox 974 3116 1256 3170"><span class='xocr_word' id='xword_1_175' title="x_wconf -2">substituir</span></span> <span class='ocr_word' id='word_1_176' title="bbox 1285 3138 1354 3172"><span class='xocr_word' id='xword_1_176' title="x_wconf -1">ou</span></span> <span class='ocr_word' id='word_1_177' title="bbox 1388 3140 1621 3178"><span class='xocr_word' id='xword_1_177' title="x_wconf -2">remover</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1651 3147 1743 3183"><span class='xocr_word' id='xword_1_178' title="x_wconf -2">um</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1777 3130 2042 3204"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_180' title="bbox 2074 3140 2194 3209"><span class='xocr_word' id='xword_1_180' title="x_wconf -1">pela</span></span> <span class='ocr_word' id='word_1_181' title="bbox 2224 3146 2330 3197"><span class='xocr_word' id='xword_1_181' title="x_wconf -2">API</span></span> <span class='ocr_word' id='word_1_182' title="bbox 2365 3148 2393 3198"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_183' title="bbox 2425 3144 2682 3200"><span class='xocr_word' id='xword_1_183' title="x_wconf -3">utilizada</span></span> <span class='ocr_word' id='word_1_184' title="bbox 2715 3163 2741 3198"><span class='xocr_word' id='xword_1_184' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2772 3140 2879 3195"><span class='xocr_word' id='xword_1_185' title="x_wconf -2">clas-</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 590 3212 2881 3307">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_36' title="bbox 590 3214 2880 3306"><span class='ocr_word' id='word_1_186' title="bbox 590 3226 645 3260"><span class='xocr_word' id='xword_1_186' title="x_wconf -1">se</span></span> <span class='ocr_word' id='word_1_187' title="bbox 679 3214 1236 3270"><span class='xocr_word' id='xword_1_187' title="x_wconf -5">Fragnenransaction,</span></span> <span class='ocr_word' id='word_1_188' title="bbox 1268 3228 1295 3261"><span class='xocr_word' id='xword_1_188' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_189' title="bbox 1328 3229 1360 3262"><span class='xocr_word' id='xword_1_189' title="x_wconf -1">0</span></span> <span class='ocr_word' id='word_1_190' title="bbox 1395 3214 1619 3270"><span class='xocr_word' id='xword_1_190' title="x_wconf -2">mtodo</span></span> <span class='ocr_word' id='word_1_191' title="bbox 1653 3230 1875 3287"><span class='xocr_word' id='xword_1_191' title="x_wconf -3">connit()</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1913 3226 2106 3285"><span class='xocr_word' id='xword_1_192' title="x_wconf -2">efetiva</span></span> <span class='ocr_word' id='word_1_193' title="bbox 2142 3251 2200 3287"><span class='xocr_word' id='xword_1_193' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_194' title="bbox 2235 3234 2553 3306"><span class='xocr_word' id='xword_1_194' title="x_wconf -2">alteraes.</span></span> <span class='ocr_word' id='word_1_195' title="bbox 2589 3241 2639 3292"><span class='xocr_word' id='xword_1_195' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_196' title="bbox 2676 3228 2880 3291"><span class='xocr_word' id='xword_1_196' title="x_wconf -5">mtodo</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 589 3295 2880 3400">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_37' title="bbox 590 3297 2880 3399"><span class='ocr_word' id='word_1_197' title="bbox 590 3302 923 3359"><span class='xocr_word' id='xword_1_197' title="x_wconf -5">add(1y0U'C,ff</span></span> <span class='ocr_word' id='word_1_198' title="bbox 929 3303 1104 3358"><span class='xocr_word' id='xword_1_198' title="x_wconf -6">9.'C9)</span></span> <span class='ocr_word' id='word_1_199' title="bbox 1134 3297 1311 3351"><span class='xocr_word' id='xword_1_199' title="x_wconf -2">recebe</span></span> <span class='ocr_word' id='word_1_200' title="bbox 1336 3319 1493 3356"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">como</span></span> <span class='ocr_word' id='word_1_201' title="bbox 1519 3312 1814 3373"><span class='xocr_word' id='xword_1_201' title="x_wconf -2">parmetro</span></span> <span class='ocr_word' id='word_1_202' title="bbox 1840 3336 1873 3370"><span class='xocr_word' id='xword_1_202' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_203' title="bbox 1900 3318 2277 3380"><span class='xocr_word' id='xword_1_203' title="x_wconf -2">identicador</span></span> <span class='ocr_word' id='word_1_204' title="bbox 2301 3327 2375 3382"><span class='xocr_word' id='xword_1_204' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_205' title="bbox 2402 3328 2584 3399"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">layout</span></span> <span class='ocr_word' id='word_1_206' title="bbox 2610 3329 2677 3384"><span class='xocr_word' id='xword_1_206' title="x_wconf -2">de</span></span> <span class='ocr_word' id='word_1_207' title="bbox 2702 3324 2834 3383"><span class='xocr_word' id='xword_1_207' title="x_wconf -6">ond</span></span> <span class='ocr_word' id='word_1_208' title="bbox 2855 3336 2880 3370"><span class='xocr_word' id='xword_1_208' title="x_wconf -3">0</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 591 3382 2880 3491">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_38' title="bbox 591 3384 2880 3489"><span class='ocr_word' id='word_1_209' title="bbox 591 3386 848 3455"><span class='xocr_word' id='xword_1_209' title="x_wconf -8">ffagmem</span></span> <span class='ocr_word' id='word_1_210' title="bbox 871 3384 997 3438"><span class='xocr_word' id='xword_1_210' title="x_wconf -7">deve</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1020 3405 1100 3438"><span class='xocr_word' id='xword_1_211' title="x_wconf -7">Sf</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1122 3387 1365 3451"><span class='xocr_word' id='xword_1_212' title="x_wconf -5">lSr1d0,</span></span> <span class='ocr_word' id='word_1_213' title="bbox 1386 3410 1412 3444"><span class='xocr_word' id='xword_1_213' title="x_wconf -4">a</span></span> <span class='ocr_word' id='word_1_214' title="bbox 1439 3395 1693 3454"><span class='xocr_word' id='xword_1_214' title="x_wconf -1">instncia</span></span> <span class='ocr_word' id='word_1_215' title="bbox 1716 3402 1788 3458"><span class='xocr_word' id='xword_1_215' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_216' title="bbox 1812 3406 1993 3477"><span class='xocr_word' id='xword_1_216' title="x_wconf -2">objeto</span></span> <span class='ocr_word' id='word_1_217' title="bbox 2017 3412 2090 3467"><span class='xocr_word' id='xword_1_217' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_218' title="bbox 2116 3414 2382 3488"><span class='xocr_word' id='xword_1_218' title="x_wconf -3">fragment</span></span> <span class='ocr_word' id='word_1_219' title="bbox 2405 3441 2433 3476"><span class='xocr_word' id='xword_1_219' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_220' title="bbox 2458 3441 2584 3477"><span class='xocr_word' id='xword_1_220' title="x_wconf -2">uma</span></span> <span class='ocr_word' id='word_1_221' title="bbox 2610 3425 2774 3489"><span class='xocr_word' id='xword_1_221' title="x_wconf -2">string</span></span> <span class='ocr_word' id='word_1_222' title="bbox 2794 3426 2880 3484"><span class='xocr_word' id='xword_1_222' title="x_wconf -6">qUf</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 590 3473 1689 3632">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_39' title="bbox 590 3474 1689 3544"><span class='ocr_word' id='word_1_223' title="bbox 590 3481 615 3529"><span class='xocr_word' id='xword_1_223' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_224' title="bbox 638 3495 665 3529"><span class='xocr_word' id='xword_1_224' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_225' title="bbox 691 3491 787 3544"><span class='xocr_word' id='xword_1_225' title="x_wconf -2">tag.</span></span> <span class='ocr_word' id='word_1_226' title="bbox 806 3479 851 3527"><span class='xocr_word' id='xword_1_226' title="x_wconf -1">A</span></span> <span class='ocr_word' id='word_1_227' title="bbox 871 3490 956 3543"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">tag</span></span> <span class='ocr_word' id='word_1_228' title="bbox 978 3474 1118 3543"><span class='xocr_word' id='xword_1_228' title="x_wconf -1">pode</span></span> <span class='ocr_word' id='word_1_229' title="bbox 1140 3495 1221 3528"><span class='xocr_word' id='xword_1_229' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_230' title="bbox 1242 3477 1488 3535"><span class='xocr_word' id='xword_1_230' title="x_wconf -2">utilizada</span></span> <span class='ocr_word' id='word_1_231' title="bbox 1551 3505 1689 3544"><span class='xocr_word' id='xword_1_231' title="x_wconf -6">oszer</span></span></span>
<span class='ocr_line' id='line_1_40' title="bbox 590 3564 1441 3632"><span class='ocr_word' id='word_1_232' title="bbox 590 3586 621 3619"><span class='xocr_word' id='xword_1_232' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_233' title="bbox 644 3564 861 3618"><span class='xocr_word' id='xword_1_233' title="x_wconf -2">mtodo</span></span> <span class='ocr_word' id='word_1_234' title="bbox 882 3570 1441 3632"><span class='xocr_word' id='xword_1_234' title="x_wconf -3">ndFragnentByTag(tag).</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_16' title="bbox 1514 3495 2879 3587">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_41' title="bbox 1514 3496 2879 3586"><span class='ocr_word' id='word_1_235' title="bbox 1514 3504 1547 3554"><span class='xocr_word' id='xword_1_235' title="x_wconf -4">P</span></span> <span class='ocr_word' id='word_1_236' title="bbox 1696 3496 1954 3555"><span class='xocr_word' id='xword_1_236' title="x_wconf -7">OTITICHC</span></span> <span class='ocr_word' id='word_1_237' title="bbox 1979 3523 2104 3573"><span class='xocr_word' id='xword_1_237' title="x_wconf -1">para</span></span> <span class='ocr_word' id='word_1_238' title="bbox 2129 3527 2414 3566"><span class='xocr_word' id='xword_1_238' title="x_wconf -5">I1COU`8I`</span></span> <span class='ocr_word' id='word_1_239' title="bbox 2437 3533 2470 3567"><span class='xocr_word' id='xword_1_239' title="x_wconf -1">O</span></span> <span class='ocr_word' id='word_1_240' title="bbox 2496 3513 2756 3586"><span class='xocr_word' id='xword_1_240' title="x_wconf -5">fI'3gITl(I1</span></span> <span class='ocr_word' id='word_1_241' title="bbox 2779 3518 2879 3561"><span class='xocr_word' id='xword_1_241' title="x_wconf -5">COITI</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_17' title="bbox 678 3698 2245 3794">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_42' title="bbox 678 3699 2245 3793"><span class='ocr_word' id='word_1_242' title="bbox 678 3704 896 3754"><span class='xocr_word' id='xword_1_242' title="x_wconf -3">Fragmentl</span></span> <span class='ocr_word' id='word_1_243' title="bbox 927 3699 1045 3754"><span class='xocr_word' id='xword_1_243' title="x_wconf -3">fragl</span></span> <span class='ocr_word' id='word_1_244' title="bbox 1074 3717 1096 3735"><span class='xocr_word' id='xword_1_244' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_245' title="bbox 1127 3700 1390 3758"><span class='xocr_word' id='xword_1_245' title="x_wconf -3">(Fragmentl)</span></span> <span class='ocr_word' id='word_1_246' title="bbox 1423 3706 2245 3793"><span class='xocr_word' id='xword_1_246' title="x_wconf -6">fn.ndFragmentByTag("Fra9ment1~).</span></span></span>
</p>
</div>
</div>
</body>
</html>
(api
I 8 F uo
t n ragmen s 255
Repare que, antes de inserir o fra gmt pela API foi validada a condi o
lsavedlnstancestate == "u) Para ter Certeza de que a activity estava sendo crida
nesse instante. Se o Bundle no estiver nulo signica ue a act- _t f . d ,d
criada novamente. Isso pode acontecer a q M y O1 estrul a 6

I ' o , ,. ,, .
O girar o dispositivo trocando a orientao
de vertical para horizontal, ou vice-versa
mesmo Bundl ' , . Quando uma activity destruda, o m
todo onSaveInstanceState(bundle) chamad O para o aplicativo salvar o estado da tela.
E556 C Pa55ad0 C0m0 argumento para o metodo onCreate(bundle) ao
facflaf a aCt1V1tY Por 1550, devemos testar a condio if(savedInstanceState == null)
para ter Certeza de que a activity est sendo criada pela primeira vez pois caso
contrario o fragment seria adicionado duas vezes no layout Portanto lembre~se
a transao criada pl0 F"H9P1entTransaction persistida durante o ciclo de vida da
activity e qualquer troca de orientao.

8.5 Utilizando fragments com action bar + tabs


No captulo 5, sobre action bar, zemos um exemplo que mostrou como criar
as tabs. Agora vamos criar outro exemplo que vai utilizar action bar + tabs +
fragments. Embora as tabs com action bar estejam deprecated (depois falamos
mais sobre isso), aprender a utiliza-las muito importante para o seu aprendizado.
No Android, no importa se voc utiliza as tabs ou o menu lateral (Navigation
Drawer) como a navegao top-level do seu aplicativo, sempre que voc selecionar
alguma tab ou opo do menu, o contedo da tela precisa ser atualizado sem
trocar de activity E isso feito com fragments.
O prximo exemplo que vamos estudar o projeto Fragments-ActionBarTabs, disponvel
nos exemplos deste captulo. Neste projeto, foi congurada a action bar com tres
tabs, e ao clicar numa tab um fragment ser substituido por outro dentro do layout.

Nota: lembre~se de que a classe android.support.v7.app.AppCcrfipatACtV)' lha de


demos utilizar os fragments e a
android.support.v4.app.FragmentActvity; por isso, P0
action bar de compatibilidade.

MainActivity.java
Dublc class MainActivity extends android.support.v7.app.AppCompatActtv1.ty {
@0verride
protected void onCreate(Bundl e savedInstance5tate) {
super.onCreate(savedInstance5tate);.
setContentView(R.layout.actvity_matn);
256 Google Android - 4 edio
ActionBar actionBar = getSupportActionBar(); MODE TABS).
actonBar.setNavgationHode(androd.app.ActionBar.NAVIGATION_ _ ,
// Tab 1
ActionBar.Tab tab1 = actionBar.newTab().setTet("Fra9 1 );
tab1.setTabLstener(new MyTabLstener(ths, new Fra9@t1()));
actonBar.addTab(tab1);
// Tab 2
ActonBar.Tab tab2 = actonBar.newTab().setTet("Frag 2");
tab2.setTabListener(new MyTabLstener(ths, new Fragnent2()));g
actonBar.addTab(tab2);
// Tab 3
ActionBar.Tab tab3 = actonBar.newTab().setText("Frag 3");
tab3.setTabLstener(new MyTabLstener(ths, new Fragment3()));
actonBar.addTab(tab3);
}

No cdigo da activity a classe MyTabLi.stener recebe no construtor a instncia do


fragment que deve ser substitudo no layout. Para este exemplo criei as classes
Fragmentl, Fragnent2 e Fragnent3. No arquivo de layout da activity mais uma vez
vamos deixar apenas o layout de marcao, pois os fragments sero inseridos
dinamicamente.

/res/layout/activity_main.xmI
<FrameLayout m1ns:android="http://schemas.android.com/apk/res/android"
xnlns:too1s="http://schemas.androd.com/tools"
androd:layout_wdth="match_parent" android:1ayout_heght="natch_parent"
androd:id="@+id/1ayoutFrag">
</span></span> <span class='ocr_word' id='word_1_118' title="bbox 817 2714 1010 2763"><span class='xocr_word' id='xword_1_118' title="x_wconf -3">Fragment</span></span> <span class='ocr_word' id='word_1_119' title="bbox 1041 2708 1135 2752"><span class='xocr_word' id='xword_1_119' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_120' title="bbox 1165 2706 1360 2751"><span class='xocr_word' id='xword_1_120' title="x_wconf -4">inserido</span></span> <span class='ocr_word' id='word_1_121' title="bbox 1389 2707 1484 2759"><span class='xocr_word' id='xword_1_121' title="x_wconf -4">aqui</span></span> <span class='ocr_word' id='word_1_122' title="bbox 1513 2719 1559 2750"><span class='xocr_word' id='xword_1_122' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_123' title="bbox 1588 2706 1735 2760"><span class='xocr_word' id='xword_1_123' title="x_wconf -4">layout</span></span> <span class='ocr_word' id='word_1_124' title="bbox 1768 2705 2189 2761"><span class='xocr_word' id='xword_1_124' title="x_wconf -4">"@+id/1ayoutFrag"</span></span> <span class='ocr_word' id='word_1_125' title="bbox 2254 2731 2266 2737"><span class='xocr_word' id='xword_1_125' title="x_wconf -1">-</span></span> <span class='ocr_word' id='word_1_126' title="bbox 2302 2721 2323 2746"><span class='xocr_word' id='xword_1_126' title="x_wconf 0"></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 603 2795 949 2849"><span class='ocr_word' id='word_1_127' title="bbox 603 2795 949 2849"><span class='xocr_word' id='xword_1_127' title="x_wconf -2"></FrameLayout</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 603 2887 2899 3275">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 604 2905 2895 2981"><span class='ocr_word' id='word_1_128' title="bbox 604 2916 675 2965"><span class='xocr_word' id='xword_1_128' title="x_wconf -1">As</span></span> <span class='ocr_word' id='word_1_129' title="bbox 699 2910 882 2964"><span class='xocr_word' id='xword_1_129' title="x_wconf -1">classes</span></span> <span class='ocr_word' id='word_1_130' title="bbox 908 2919 1150 2972"><span class='xocr_word' id='xword_1_130' title="x_wconf -3">Fragnentl,</span></span> <span class='ocr_word' id='word_1_131' title="bbox 1174 2917 1398 2970"><span class='xocr_word' id='xword_1_131' title="x_wconf -2">Fragnent2</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1423 2927 1450 2960"><span class='xocr_word' id='xword_1_132' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_133' title="bbox 1475 2918 1701 2971"><span class='xocr_word' id='xword_1_133' title="x_wconf -2">Fragnent3</span></span> <span class='ocr_word' id='word_1_134' title="bbox 1728 2916 1830 2962"><span class='xocr_word' id='xword_1_134' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_135' title="bbox 1855 2917 2004 2964"><span class='xocr_word' id='xword_1_135' title="x_wconf -2">sero</span></span> <span class='ocr_word' id='word_1_136' title="bbox 2029 2910 2267 2965"><span class='xocr_word' id='xword_1_136' title="x_wconf -2">exibidas</span></span> <span class='ocr_word' id='word_1_137' title="bbox 2292 2914 2418 2981"><span class='xocr_word' id='xword_1_137' title="x_wconf -1">aqui</span></span> <span class='ocr_word' id='word_1_138' title="bbox 2443 2929 2568 2980"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_139' title="bbox 2594 2905 2895 2963"><span class='xocr_word' id='xword_1_139' title="x_wconf -2">economizar</span></span></span>
<span class='ocr_line' id='line_1_31' title="bbox 607 2997 2895 3072"><span class='ocr_word' id='word_1_140' title="bbox 607 3020 791 3070"><span class='xocr_word' id='xword_1_140' title="x_wconf -3">espao</span></span> <span class='ocr_word' id='word_1_141' title="bbox 819 3019 888 3052"><span class='xocr_word' id='xword_1_141' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_142' title="bbox 915 2998 1049 3061"><span class='xocr_word' id='xword_1_142' title="x_wconf -1">livro,</span></span> <span class='ocr_word' id='word_1_143' title="bbox 1074 3002 1189 3067"><span class='xocr_word' id='xword_1_143' title="x_wconf -1">pois</span></span> <span class='ocr_word' id='word_1_144' title="bbox 1215 2997 1318 3050"><span class='xocr_word' id='xword_1_144' title="x_wconf -1">elas</span></span> <span class='ocr_word' id='word_1_145' title="bbox 1343 3005 1434 3051"><span class='xocr_word' id='xword_1_145' title="x_wconf -1">so</span></span> <span class='ocr_word' id='word_1_146' title="bbox 1460 2998 1671 3068"><span class='xocr_word' id='xword_1_146' title="x_wconf -1">simples</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1697 3019 1855 3054"><span class='xocr_word' id='xword_1_147' title="x_wconf -1">como</span></span> <span class='ocr_word' id='word_1_148' title="bbox 1882 3021 1909 3054"><span class='xocr_word' id='xword_1_148' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_149' title="bbox 1936 3001 2099 3056"><span class='xocr_word' id='xword_1_149' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_150' title="bbox 2127 3013 2359 3067"><span class='xocr_word' id='xword_1_150' title="x_wconf -3">Fragnentl</span></span> <span class='ocr_word' id='word_1_151' title="bbox 2387 3022 2493 3072"><span class='xocr_word' id='xword_1_151' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_152' title="bbox 2520 3000 2820 3056"><span class='xocr_word' id='xword_1_152' title="x_wconf -6">estudamos</span></span> <span class='ocr_word' id='word_1_153' title="bbox 2844 3004 2895 3042"><span class='xocr_word' id='xword_1_153' title="x_wconf -1">no</span></span></span>
<span class='ocr_line' id='line_1_32' title="bbox 608 3087 2897 3165"><span class='ocr_word' id='word_1_154' title="bbox 608 3089 841 3159"><span class='xocr_word' id='xword_1_154' title="x_wconf -1">exemplo</span></span> <span class='ocr_word' id='word_1_155' title="bbox 856 3093 1082 3142"><span class='xocr_word' id='xword_1_155' title="x_wconf -1">anterior.</span></span> <span class='ocr_word' id='word_1_156' title="bbox 1100 3087 1425 3141"><span class='xocr_word' id='xword_1_156' title="x_wconf -1">Lembrando</span></span> <span class='ocr_word' id='word_1_157' title="bbox 1441 3108 1542 3158"><span class='xocr_word' id='xword_1_157' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_158' title="bbox 1557 3090 1683 3144"><span class='xocr_word' id='xword_1_158' title="x_wconf -2">cada</span></span> <span class='ocr_word' id='word_1_159' title="bbox 1701 3090 1955 3162"><span class='xocr_word' id='xword_1_159' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_160' title="bbox 1971 3109 2075 3147"><span class='xocr_word' id='xword_1_160' title="x_wconf -1">tem</span></span> <span class='ocr_word' id='word_1_161' title="bbox 2091 3114 2184 3148"><span class='xocr_word' id='xword_1_161' title="x_wconf -1">seu</span></span> <span class='ocr_word' id='word_1_162' title="bbox 2200 3099 2414 3165"><span class='xocr_word' id='xword_1_162' title="x_wconf -2">prprio</span></span> <span class='ocr_word' id='word_1_163' title="bbox 2430 3098 2646 3165"><span class='xocr_word' id='xword_1_163' title="x_wconf -2">arquivo</span></span> <span class='ocr_word' id='word_1_164' title="bbox 2661 3091 2726 3146"><span class='xocr_word' id='xword_1_164' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_165' title="bbox 2740 3089 2897 3156"><span class='xocr_word' id='xword_1_165' title="x_wconf -8">layout,</span></span></span>
<span class='ocr_line' id='line_1_33' title="bbox 610 3178 2881 3255"><span class='ocr_word' id='word_1_166' title="bbox 610 3201 705 3251"><span class='xocr_word' id='xword_1_166' title="x_wconf -5">P0f</span></span> <span class='ocr_word' id='word_1_167' title="bbox 723 3179 974 3248"><span class='xocr_word' id='xword_1_167' title="x_wconf -6">XmPl0</span></span> <span class='ocr_word' id='word_1_168' title="bbox 996 3181 1577 3246"><span class='xocr_word' id='xword_1_168' title="x_wconf -8">/TCS/<1)'0WfT1._Zm611_1</span></span> <span class='ocr_word' id='word_1_169' title="bbox 1592 3225 1603 3244"><span class='xocr_word' id='xword_1_169' title="x_wconf -1">,</span></span> <span class='ocr_word' id='word_1_170' title="bbox 1618 3186 2218 3254"><span class='xocr_word' id='xword_1_170' title="x_wconf -4">/res/layout_fragment_2</span></span> <span class='ocr_word' id='word_1_171' title="bbox 2241 3206 2268 3241"><span class='xocr_word' id='xword_1_171' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_172' title="bbox 2286 3178 2881 3255"><span class='xocr_word' id='xword_1_172' title="x_wconf -3">/res/layout_ragment_3-</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 608 3282 2903 3683">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 611 3302 2898 3412"><span class='ocr_word' id='word_1_173' title="bbox 611 3309 661 3360"><span class='xocr_word' id='xword_1_173' title="x_wconf -4">O</span></span> <span class='ocr_word' id='word_1_174' title="bbox 682 3304 899 3412"><span class='xocr_word' id='xword_1_174' title="x_wconf -10">Segfedo</span></span> <span class='ocr_word' id='word_1_175' title="bbox 920 3323 1042 3373"><span class='xocr_word' id='xword_1_175' title="x_wconf -8">para</span></span> <span class='ocr_word' id='word_1_176' title="bbox 1064 3303 1266 3356"><span class='xocr_word' id='xword_1_176' title="x_wconf -7">Uuhzaf</span></span> <span class='ocr_word' id='word_1_177' title="bbox 1284 3323 1344 3356"><span class='xocr_word' id='xword_1_177' title="x_wconf -5">05</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1365 3302 1649 3374"><span class='xocr_word' id='xword_1_178' title="x_wconf -6">ffagmfints</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1669 3328 1790 3363"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">com</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1811 3330 1866 3365"><span class='xocr_word' id='xword_1_180' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_181' title="bbox 1887 3311 2006 3366"><span class='xocr_word' id='xword_1_181' title="x_wconf -2">tabs</span></span> <span class='ocr_word' id='word_1_182' title="bbox 2026 3317 2053 3367"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"></span></span> <span class='ocr_word' id='word_1_183' title="bbox 2074 3333 2107 3367"><span class='xocr_word' id='xword_1_183' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_184' title="bbox 2126 3321 2436 3369"><span class='xocr_word' id='xword_1_184' title="x_wconf -4">TabLstener.</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2451 3318 2571 3385"><span class='xocr_word' id='xword_1_185' title="x_wconf -1">Veja</span></span> <span class='ocr_word' id='word_1_186' title="bbox 2593 3332 2699 3385"><span class='xocr_word' id='xword_1_186' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_187' title="bbox 2719 3330 2745 3365"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_188' title="bbox 2764 3305 2898 3362"><span class='xocr_word' id='xword_1_188' title="x_wconf -6">clas</span></span></span>
<span class='ocr_line' id='line_1_35' title="bbox 610 3393 2900 3475"><span class='ocr_word' id='word_1_189' title="bbox 610 3402 945 3460"><span class='xocr_word' id='xword_1_189' title="x_wconf -4">MyTabL1stener</span></span> <span class='ocr_word' id='word_1_190' title="bbox 970 3393 1148 3447"><span class='xocr_word' id='xword_1_190' title="x_wconf -1">recebe</span></span> <span class='ocr_word' id='word_1_191' title="bbox 1173 3413 1242 3446"><span class='xocr_word' id='xword_1_191' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1265 3413 1359 3447"><span class='xocr_word' id='xword_1_192' title="x_wconf -1">seu</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1381 3413 1683 3452"><span class='xocr_word' id='xword_1_193' title="x_wconf -2">construtor</span></span> <span class='ocr_word' id='word_1_194' title="bbox 1705 3420 1738 3454"><span class='xocr_word' id='xword_1_194' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_195' title="bbox 1763 3400 2026 3473"><span class='xocr_word' id='xword_1_195' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_196' title="bbox 2049 3425 2156 3475"><span class='xocr_word' id='xword_1_196' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_197' title="bbox 2179 3406 2261 3461"><span class='xocr_word' id='xword_1_197' title="x_wconf -2">ela</span></span> <span class='ocr_word' id='word_1_198' title="bbox 2284 3406 2415 3461"><span class='xocr_word' id='xword_1_198' title="x_wconf -1">deve</span></span> <span class='ocr_word' id='word_1_199' title="bbox 2439 3423 2670 3462"><span class='xocr_word' id='xword_1_199' title="x_wconf -2">mostrar</span></span> <span class='ocr_word' id='word_1_200' title="bbox 2691 3422 2754 3459"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">ao</span></span> <span class='ocr_word' id='word_1_201' title="bbox 2777 3397 2900 3452"><span class='xocr_word' id='xword_1_201' title="x_wconf -3">clicar</span></span></span>
<span class='ocr_line' id='line_1_36' title="bbox 612 3479 2900 3568"><span class='ocr_word' id='word_1_202' title="bbox 612 3505 695 3539"><span class='xocr_word' id='xword_1_202' title="x_wconf -1">em</span></span> <span class='ocr_word' id='word_1_203' title="bbox 716 3484 845 3538"><span class='xocr_word' id='xword_1_203' title="x_wconf -1">cada</span></span> <span class='ocr_word' id='word_1_204' title="bbox 865 3483 963 3537"><span class='xocr_word' id='xword_1_204' title="x_wconf -2">tab.</span></span> <span class='ocr_word' id='word_1_205' title="bbox 978 3487 1094 3552"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">Veja</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1114 3502 1218 3552"><span class='xocr_word' id='xword_1_206' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1237 3483 1384 3538"><span class='xocr_word' id='xword_1_207' title="x_wconf -2">basta</span></span> <span class='ocr_word' id='word_1_208' title="bbox 1407 3505 1530 3541"><span class='xocr_word' id='xword_1_208' title="x_wconf -1">uma</span></span> <span class='ocr_word' id='word_1_209' title="bbox 1551 3492 1706 3545"><span class='xocr_word' id='xword_1_209' title="x_wconf -1">nica</span></span> <span class='ocr_word' id='word_1_210' title="bbox 1727 3491 1875 3548"><span class='xocr_word' id='xword_1_210' title="x_wconf -2">linha</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1895 3494 1962 3549"><span class='xocr_word' id='xword_1_211' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1981 3496 2175 3567"><span class='xocr_word' id='xword_1_212' title="x_wconf -2">cdigo</span></span> <span class='ocr_word' id='word_1_213' title="bbox 2196 3518 2323 3568"><span class='xocr_word' id='xword_1_213' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_214' title="bbox 2343 3515 2593 3554"><span class='xocr_word' id='xword_1_214' title="x_wconf -5">executar</span></span> <span class='ocr_word' id='word_1_215' title="bbox 2609 3518 2642 3553"><span class='xocr_word' id='xword_1_215' title="x_wconf -4">0</span></span> <span class='ocr_word' id='word_1_216' title="bbox 2662 3479 2900 3552"><span class='xocr_word' id='xword_1_216' title="x_wconf -6">eomand</span></span></span>
<span class='ocr_line' id='line_1_37' title="bbox 615 3578 2857 3662"><span class='ocr_word' id='word_1_217' title="bbox 615 3579 1337 3638"><span class='xocr_word' id='xword_1_217' title="x_wconf -5">Fragnenransacton.rep1ace(</span></span> <span class='ocr_word' id='word_1_218' title="bbox 1352 3617 1360 3627"><span class='xocr_word' id='xword_1_218' title="x_wconf 0">.</span></span> <span class='ocr_word' id='word_1_219' title="bbox 1378 3618 1386 3628"><span class='xocr_word' id='xword_1_219' title="x_wconf 0">.</span></span> <span class='ocr_word' id='word_1_220' title="bbox 1404 3582 1442 3638"><span class='xocr_word' id='xword_1_220' title="x_wconf 0">.)</span></span> <span class='ocr_word' id='word_1_221' title="bbox 1467 3597 1493 3630"><span class='xocr_word' id='xword_1_221' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_222' title="bbox 1515 3579 1797 3637"><span class='xocr_word' id='xword_1_222' title="x_wconf -2">substituir</span></span> <span class='ocr_word' id='word_1_223' title="bbox 1816 3605 1851 3639"><span class='xocr_word' id='xword_1_223' title="x_wconf -2">o</span></span> <span class='ocr_word' id='word_1_224' title="bbox 1873 3585 2137 3657"><span class='xocr_word' id='xword_1_224' title="x_wconf -2">fragment</span></span> <span class='ocr_word' id='word_1_225' title="bbox 2160 3609 2226 3644"><span class='xocr_word' id='xword_1_225' title="x_wconf -2">ao</span></span> <span class='ocr_word' id='word_1_226' title="bbox 2248 3590 2407 3645"><span class='xocr_word' id='xword_1_226' title="x_wconf -2">clicar</span></span> <span class='ocr_word' id='word_1_227' title="bbox 2427 3610 2513 3646"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">em</span></span> <span class='ocr_word' id='word_1_228' title="bbox 2537 3590 2748 3662"><span class='xocr_word' id='xword_1_228' title="x_wconf -2">alguma</span></span> <span class='ocr_word' id='word_1_229' title="bbox 2771 3578 2857 3638"><span class='xocr_word' id='xword_1_229' title="x_wconf -4">tab-</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 8 n Fragments
257

MyTabListener.java
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;

public class MyTabListener implements ActionBar.TabListener {


private Context context;
private Fragment frag;
public MyTabListener(Context context, Fragment frag) {
this.context = context;
this.frag = frag;
}

@0verride
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// Troca o fragment dinamicamente ao clicar na tab
ft.replace(R.id.layoutFrag, this.frag, null);
}

// Mtodos onTabUnselected e onTabReselected aqui.


}

Ao executar esse projeto, o resultado deve ser como a gura 8.10, que mostra a
segunda tab selecionada e o Fragnent2 no layout.

Fg2

Figura 8.10 - Action bar com tabs + ffagme


258 Google Android - 4' edio
8.6 Utilizando fragments com action bar + tabs + ViewPa9f
. . ainda
' r s mais
itilizar oViewPager ara eo
t P n.
_._ _. ... .- -nav
Para melhorar arentredo
as .'_~
o exemplo
' ' recisa anterior, podemos
trolar os tragmentsepermmrfazero gesto de swipe Iateralpr C8 _ g tibs.
Esse e um padrao de design muito conhecido no Andro1d, VOC P mma lo
O prximo exemplo que vou mostrar o projeto Fra9t'1@t5'T3b5'V"P39" QIU sst
disponvel com os exemplos do livro. Neste projeto foi con8Uf3d0 21 3CFlOl`l bar
com trs tabs, porm o controle de navegao feito pelo VGHPBQGF- SSIO ff 0 mais
importante deste exemplo! O ViewPager faz todo o trabalho e a tab so mostra 3
pgina que est sendo exibida.
O adapter do ViewPager ser formado pelos trs fragments, Fragmentl, Fragnentz
e Fragnent3. Portanto, voc poder utilizar o gesto de swipe para navegar nos
fragments. A tab nessa histria uma mera coadjuvante, pois ela apenas mostra
a pgina selecionada. A seguir, podemos visualizar o cdigo-fonte da HainActivity
que demonstra como utilizar o ViewPager com fragments.

LI MainActvity.java

public class HainActivity extends AppCompatActivity {


private ViewPager viewPager;
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
// ViewPager
viewPager = (ViewPager) ndViewById(R.id.viewPager);
viewPager.setAdapter(neu TabsAdapter(getSupportFragnentHanager()));
// Congura as Tabs
nal ActionBar actionar = getSupportActionBar();
actionBar.setNavigationHode(android.app.ActionBar.NAVIGATION_HO0E_TABS);
actionBar.addTab(actionBar.newTab().setText("Frag 1").setTabListener(new
HyTabListener(viewPager,0)));
actionBar.addTab(actionBar.newTab().setText("Frag 2").setTabListener(new
HyTabListener(viewPager,1)));
actionBar.addTab(actionBar.newTab() setText("Frag 3").setTabListener(new
HyTabListener(viewPager,2)));
// Se o ViewPager troca de pgina, atualiza 3 rab_
viewPager . set0nPageChangeListener( new ViewPager .0nPageChangeListener() {
QO/er ride

public void onPageSe1ected(int idx) {


// 59 ler Swipe no ViewPager, atualiza a tab
actionlar . setSe1ectedNav'lgationIten(idx) ;
Captulo 8 n Fragments 259
-}
@0verride

public void onPageScrolled(int position, oat position0ffset,


int position0ffsetPixels) { }
@0verride

public void onPageScrollStateChanged(int state) { }


});
}

Neste cdigo, estamos monitorando o evento de troca de pgina do ViewPager, pois


precisamos atualizar o ndice da tab selecionada, para corresponder pgina que
o ViewPager est mostrando. O mais importante deste exemplo voc entender
que no precisamos atualizar os fragments dinamicamente na tela com a classe
FragmentTransaction, pois o ViewPager que controla tudo. No arquivo de layout da
activity; basta inserir o ViewPager.

/res/layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<android.support.v4.view.ViewPager android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_height="wrap_content" />

Veja que no cdigo-fonte da activity estamos utilizando a classe TabsAdapter, que


o adapter do ViewPager. Observe que o ViewPager pode utilizar uma implementao
de PagerAdapter para views normais, ou FragmentPagerAdapter quando cada pgina
representada por um fragment.

TabsAdapter.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
QLC class TabsAdapter extends FragmentPagerAdapter {
public TabsAdapter(FragmentManager supportFragmentManager) {
super(supportFragmentManager);
}

@0verride
public int getCount() {
// 0 ViewPager vai ter 3 pginas
return 3;
}
260 Google Android - 4 edio
@0verride
public Fragment getItem(int idx) {
if(id == G) {
return new Fragment1();
} else if(id == 1) {
return new Fragment2();
}

return new Fragment3();


}

Esse adapter apenas fornece o contedo do ViewPager; teremos trs pginas, e cada
uma um fragment. Para concluir o exemplo, a classe MyTabListener que trata dos
eventos das tabs foi alterada, para que, quando selecionar uma tab, a pgina do
ViewPager seja atualizada com o ndice da tab selecionada. Lembre-se de que neste
exemplo o ViewPager quem controla tudo, e as Tabs so meras coadjuvantes.

MyTabListener.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
public class MyTabListener implements ActionBar.TabListener {
private ViewPager viewPager;
private int idx;
public MyTabListener(ViewPager viewPager, int idx) {
this.viewPager = viewPager;
this.id = idx;
}

@Override

public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {


// Navega para a pgina desejada do ViewPager
viewPager.setCurrentItem(id);
}

@0verride

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { }


@0verride

public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { }


l

Nota: a action
vendo al ' fb-' -, .,. .
af COITI tabs f0l descontmuada (deprecated); assim, voce deve estar
guns alertas (wammgs) DO codio. Mas este exemplo e um classico que
Captulo 8 I Fragments 261
voc precisa conhecer, at porque ele tambm mostrou como utilizar o ViewPager +
Fragments. A tab aqui mera coadjuvante e pode ser substituda por qualquer outro
componente. Quando formos desenvolver o projeto dos carros vamos utilizar o
componente TabLayout da biblioteca Android Design Support Library.

8.7 Ciclo de vida de um fragment


Um fragment tem um ciclo de vida bem denido, o qual atrelado ao ciclo de vida da
activity Se voc j entendeu como funciona o ciclo de vida de uma activity; como os
mtodos onCreate(bund1.e), onStart(), onResume(), onPause(), onStop() e onDestroy(), ser bem
simples de entender o ciclo de vida de um fragment, pois ele segue o mesmo conceito.
A gura 8.11 relembra os mtodos do ciclo de vida de uma activity

.._;
'..,,3
. z..
`'l` WW "
5- V- f l `.g
-zap-z I.. * ."*`"`'*, ~._, ._ Em
.;,..`,__zj_r
3 _ tzz..->1. z vv
z ...

onreatei)

de volta para a

onStart() '41-1 OHRGSHHO l


onesume

*f A mw' 'ty
EXQCUBDO j a executar

R notopodapha _ 2 Oul' acvfy enim 1

. g;P31z53()
,. __ A a executar
`
KOJJIHS
A activity visitar?

A activity no est mais 1/SIG

L j ao usurio e ser encenada __*


' onStop(3

onDestroy{)

Figura 8.11 - Ciclo de vida de uma activity.


262 Google Android - 4' Qdiu
. , . . - z ' oficial. Caso voc re '
- . - ue o ca itulo 4
Note que o diagrama esta em ingles, pois e da documentaaf? I P 915
relembrar em detalhes como funciona o ciclo de vida, Vflq P 50bre
a classe Activity.
O ciclo de vida de uma activity contm os tradicionais metodos onCreate( ), que so
chamados uma nica vez quando ela in iciada, e os mtodos como onPause() Q
onStop(), indicando que a activity ser interrompida e movida Para Segundo Plano.
Outro mtodo clssico o onDestroy(), chamado uma mca vez ao destruir a activity.

Seguindo esse mesmo princpio, tambm o ciclo de vida dos fragments contm esses
mesmos mtodos, os quais so amarrados com a activity que declarou o fragment,
conhecida como host activity. Portanto, quando algum mtodo do ciclo de vida de
uma activity for chamado, como por exemplo, o onPause(), o sistema vai chamar Q
mtodo onPause() em todos os fragments dessa activity. Da mesma forma, quando
o mtodo onResume() da activity for chamado, o mtodo onResume() de cada fragnent
tambm ser.
A maioria dos mtodos do ciclo de vida dos fragments um espelho dos mtodos
da activity, mas tambm existem outros mtodos especcos que existem somente
nos fragments, conforme podemos visualizar na figura 8.12. No lado esquerdo da
gura, podemos ver a ordem em que cada mtodo do ciclo de vida chamado.
No lado direito da gura, feita uma comparao com os estados de uma activity
Podemos ver que os mtodos onStart(), onResume(), onPause() e onStop() so sim
ples, e quando os mtodos da activity forem chamados, eles tambm sero
chamados nos fragments. Os mtodos onAttach(activity), onCreate(), onCreateView()
e onActivityCreated() so executados durante a criao da activity durante o on
Create(). Por exemplo, quando uma activity informa o seu layout pelo mtodo
setContentView(view), os fragments so inados e criados, ento os mtodos onAttach().
onCreate() e onCreateView() so chamados no fragment. Mas somente quando 11
activity retornar do mtodo onCreate() dela que o mtodo onActivityCreated() do
fragment ser chamado. importante ter conhecimento disso, pois nesse mo
mento o fragment sabe que a inicializao da activity foi realizada gm Sucesso
0 que Significa que OCOS OS fragments do layout tambm foram inicializados 6
tiveram suas views criadas.

Complementando, quando uma activity destruda durante 0 Seu mtodti


OHDHSFO)/i), OS metodos D@Sf0yV0w(), onDestroy() e onDetach() so chamados em
sequencia para encerrar os recursos e desassociar o fragment da activity que est
sendo destruda.
1
Captulo 8 1 Fragmemg

Mtodos dos Fmgmgm,


263

. . 'E ' '"


~ " l l f _ nmwmzcveaw
r---smnmmoo V. i A Started

"`" Y
l

Usurio pressiona
' v"' I
*a 1

"P"' S'
Oagmeni i` " `' Y
. . . _ . novamenfepois VE
""" mstopo *I l executada . "' ' f^ ''~'~~~f~ e ea -~-~~z
Y

/ ' . . ~ ' " _ A~ -. :.. zf l ' 7. :


~ =:.<_?*. ij

OD6nd1() gm T
._....__
DWUBSYOYO

Figura 8.12 - Ciclo de vida de um ragment.

A lista a seguir mostra os principais mtodos de ciclo de vida especficos dos


fragments:

l\[|t0____g4___g g Descrio
onAttach(acti.vi.ty) Esse mtodo chamado logo depois de o fragment ser as
sociado com a activity o que acontece assim que a activity
infla o layout do fragment pela tag ou o tragment e
adicionado dinamicamente via Fragmenransacton. Note que
o importante deste mtodo que ele recebe como parmetro
a activity que contm o fragment. Somente depois que esse
mtodo chamado (mas no nele), o mtodo getActivity()
do fragment vai retornar a activity host.
onCreate(bund1e) Esse mtodo chamado apenas uma vez e quando o fragment
est sendo criado. Ele recebe o Bundle que foi salvo durante
o mtodo onSaveInstanceState(state).
264 Google Android - 4 edio

Mtodo --'3FS59.(9"-) -- ~- e
onCreateView(inater,viewgroup,bund1e)
Nesse mtodo, o fragment precisa criar a view que ser
inserida no layout da activity Somente depois de esse
mtodo retornar, possvel chamar o mtodo getView()
que retorna a view que o fragment tem.
onActivityCreated(bundle) Esse mtodo chamado logo aps o onCreate() da
activity ter sido finalizado. Esse pode ser um bom
momento para consultar os web services e buscar 0
contedo necessrio para criar 0 layout.
onDestroyView() Esse mtodo chamado quando a view do fragment
foi removida e no pertence mais ao fragment. Depois
desse evento, o mtodo getView() do fragment vai re.
tornar null..
onDestroy() Chamado para indicar que o fragment no est mais
sendo utilizado e ser destrudo.
onDetach() Oposto do mtodo onAttach(actvity), esse mtodo
chamado quando o fragment foi desassociado da
activity Depois desse evento, o mtodo getActivity()
do fragment vai retornar null.

Dica: no se preocupe se no entender tudo sobre o ciclo de vida dos fragments


ou da activity. Continue lendo, e revise esse assunto quando achar necessrio.

8.8 Migrando um projeto que utiliza activity para fragments


Um dos exemplos mais comuns de uso dos fragments e um dos primeiros que

_ i
voce precisa entender como reaproveitar o cdigo entre a verso smartphone e
tablet, conforme a figura 8.13.

y. . ou
Nrvllvh Acuvny a I _ _ A com dos
Figura 8.13 - F
mgment que divide a tela em pedaos.
Captulo 8 n Fragments 265
No prximo exemplo, vamos criar uma lista com os nomes dos planetas e ao
selecionar um item da lista vamos navegar para outra activity que vai mostrar os
detalhes do planeta. O layout ser extremamente simples, pois estamos interessa
dos apenas em estudar os fragments. A gura 8:14 mostra o exemplo funcionando.
Observe que na segunda tela o nome do planeta mostrado no ttulo da action bar.

, . Terra
Mercuro
\____/'

Ci>
VGIUS

MBR!

Jptiet

Saturno

Figura 8.14 - Navegao de telas com duas activities.

Para comearmos o exerccio, abra o projeto Planetas-Activity. O cdigo-fonte


simples. Existe uma activity com um Listview na primeira tela e uma activity com
apenas um Textview na segunda tela. O exemplo j est funcionando, mas no utiliza
fragments. A classe MainActivity a activity que mostra a lista de planetas, e a classe
PlanetaActivity a segunda tela que recebe o nome do planeta por parmetro. Por
favor, abra o projeto de exemplo no Android Studio e execute no emulador. D
uma rpida olhada no cdigo para continuarmos o exerccio, pois vamos migrar
o projeto para utilizar fragments.
A alterao que vamos fazer encapsular a view e lgica dessas duas activities
em fragments. Para comear, vamos criar o fragment PlanetasFragnent com o layout
do Listview. Basicamente, a lgica da classe MainActivity ser transferida para esse
fragment.

P|anetasFragment.java
public class PlanetasFragnent extends android.support.v4.app.Fragment{
@0verride
public View onCreateView(Layoutlnater inater, @Nullable Viewroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_planetas, container, false);
266 Google Android - 4' edio
// LiStView
Listview listview = (ListView) view.ndViewBy 1d(R.id.listview);
listView.setAdapter(new PlanetaAdapter(getActivity(ll);
listview.set0nItemClickListener(onItemClickPlaneta());
return view;
}

private Adapterview.0nItemClickListener onItemClickPlaneta() {


return new Adapterview.0nItemClickListener() {
@0verride
public void onItemClick(AdapterView<?> paret, VEW View t DOSIIOH, long id){
PlanetaAdapter adapter = (PlanetaAdapter) parent.9@t^dDtF();
String planeta = (String) adapter.getIten(position);
Toast.nakeTet(getActivity(), "Planetaz " + Dl6t3 T35t-LENGTH_5H0RT)-Show(
// 0 Context a activity, ento pode utilizar o mtodo getActivity()
// A navegao de telas continua sendo feita pela activity
Intent intent = new Intent(getActivity(),PlanetaActivity.class);
intent.putEtra("planeta",planeta);
startActivity(intent);
}

};
}

A diferena ao copiar o cdigo da activity para fragment consiste na utilizao do


context. Na activity o context ela mesma, portanto usamos o this. No fragment.
o contexto a activity portanto utilizamos 0 mtodo getActivity(). Por exemplo.
a activity utilizava este cdigo:
listView.setAdapter(new PlanetaAdapter(this));

Mas agora, dentro do fragment, o this vira o getActivity():


listView.setAdapter(new PlanetaAdapter(getActivity()));

Para continuar a migrao do cdigo para fragments, 0 fragment vai encapsular


a lista de planetas; portanto, crie o layout do fragmenr com 0 Lgtvew,

/res/layout/fragment_pIanetas.m|
<?nl version="1.9" encoding="utf~8"?
<LinearLayout nlns:android="http://schenas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="math Parent"
android:orientation="vertical" android:padding="15dp" > _

` etg natch'parent />


<ListView android:id="@+id/listview"
android:layout_width="natch _parent" android:layout h ' htz" "
Captulo 8 u Fragments 257
Nota: para acessar a classe android.content.Contet dentro do fragment, utilize o
metodo getActivity(), pois a classe Activity lha de Context.
\

O cdigo-fonte do fragment e layout so os mesmos que antes estavam na


MainAct1vity. Esse e o papel de um fragment. Ele encapsula determinadas view
e logica da tela. A vantagem disso que podemos remover a lgica da classe da
activity e no layout basta incluir o fragment conforme demonstrado a seguir:
I

MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
// 0 cdigo-fonte da activity c vazio.
setContentView(R.Iayout.activity_main);
}

/res/layout/activity_main.xml
<?xm1 version="1.0" encoding="utf-8"?>
<Re1ativeLayout m1ns:android="http://schemas.android.com/apk/res/android"
mins:too1s="http://schemas.android.com/tools"
android:1ayout_width="match_parent" android:1ayout_height="match_parent">
<fragment
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
c1ass="br.con.1ivroandroid.planetas.P1anetasFragment"
tools:1ayout="@1ayout/fragment_planetas" />

Feita essa alterao, o cdigo-fonte da activity ficou vazio, e apenas a tag


foi inserida no layout. Mais uma vez, vou alert-lo para tomar cuidado ao digitar
c .ame da classe do fragment, pois o nome completo e deve conter o pacote
no qual a classe foi criada.
Pronto, j terminamos a primeira activity agora vamos para a segunda. Basica
mente, vamos fazer a mesma coisa e criar a classe PianetaFragment. Note que usamos
planeta no singular, e no no plural, como o fragment que acabamos de criar
para a lista.
268 Google Android - 4' edlm

if PIanetaFragment.}ava
public class Pl88FfQRt extends Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, VBNGFOUP C"t"r
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_Pl6. fif false);
return view;
l
public void setPlaneta(String planeta) {
Textvieu text = (Textview) getView().ndViewBy1d(R-ld-)
text.setText("Planeta: + planeta);
l
1

fi) /res/layout/fragment_pIaneta.xmI
<?xnl version='1.6' encoding='utf-8?>
<FraneLayout xnlns:android='http://schenas.android.con/apk/res/android'
xnlns:tools='http://schenas.android.con/tools'
android:layout_width=natch_parent android:layout_height='natch_parent'
tools:context='br.livroandroid.livroandroidcap8_planetas.PlanetaFragnent'
<TextVieu android:id='@+id/text
android:layout_width='wrap_content' android:layout_height='wrap_content'
android:layout_gravity='center' />

Esse um fragment simples que apenas mostra o nome do planeta no Textvieu.


O nico trabalho da activity inserir esse fragment no seu layout, e passar o
parmetro, que o nome do planeta para o fragment.

) PIanetaActivity.java
public class Planetanctivity extends AppConpatActivity {
@0verride

protected void onCreate(Bundle savedInstanceState) {


super_onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
/I Parnetro enviado pela intent da activity
String planeta = getIntent().getStringExtra('planeta');
/I Pega o fragnent do layout pelo id
PlanetaFragnent f = (PlanetaFragnent)
QGSUDDOTFf9tHana9er().ndFragnentById(R.id.PlanetaFragnent);
I/ Atualiza o contedo do fragnenz
f.setPlaneta(planeta);
Captulo 8 1 Fragments 259
// Congura o nome do planeta como titulo na action bar
getSupportActionBar().setTit1e(planeta);
}

/res/layout/activity_pIaneta.xmI
<?m1 version="1.0" encoding="utf-8"?>
<Re1ativeLayout m1ns:android="http://schenas.android.con/apk/res/android"
xnins:too1s="http://schenas.android.con/tools"
android:1ayout_width="match_parent" android:iayout_height="match_parent"
android:padding="16dp" >
fragnent android:id="@+id/P1anetaFragment"
android:1ayout_width="match_parent" android:iayout_height="match_parent"
c1ass="br.com.1ivroandroid.planetas.P1anetaFragment"
tools:iayout="@1ayout/fragnent_pianeta" />

Veja que o segredo muitas vezes ao utilizar uma activity com fragments como
passar parmetros da activity para o fragment. Como a navegao de telas feita
no nvel da activity e os parmetros so passados pela intent, a activity precisa
ler esses parmetros e atualizar o fragment.
Depois dessas alteraes, o projeto deve continuar funcionando normalmente.
Assim, somente prossiga com a leitura caso seu exemplo esteja funcionando. Se
precisar, confira o exerccio pronto no projeto de exemplo Planetas-Fragnents.
Agora vamos brincar um pouco com fragments e implementar esse exerccio de
outra maneira. No cdigo que zemos at o momento, tivemos de passar um
parmetro para o fragment, e, como esse fragment est inserido de forma esttica
no layout XML, necessrio recuperar o fragment pelo id e chamar um mtodo
setP1aneta(p1aneta) para atualizar o contedo.
Outra abordagem, que eu at prero, adicionar o fragment dinamicamente
pela API. Ento para comear, altere o layout da activity do planeta e remova a
tag . Vamos deixar apenas um FraneLayout com um identificador para
adicionar o fragment.

/res/layout/activity_pIaneta.xmI
<?xn1 version="1.0" encoding="utf-8"?>
<ReiativeLayout . . .
android:id="@+id/1ayoutFrag" >
270 Google Android - 4 edio
, ,. -- ~ ment dinamicame
ossvel assar
O prximo passo e alterar a activity para adicionar o frag nte
no layout e pelo metodo setArguments(bundle) do fragment p P og
assar o mesmo Bundle que a activiti
parmetros. Note que a mgica est em p
recebeu pela intent.

PIanetaActivity.java

public class PlanetaActivity extends AppCompatActivity {


@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
// Mostra o nome do planeta como titulo na action bar
String planeta = getIntent().getStringExtra("planeta");
getSupportActionBar().setTitle(planeta);
if(savedInstanceState == null) {
PlanetaFragment f = new PlanetaFragment();
f.setArgunents(getIntent().getExtras()); // Parmetros: mesmo Bundle da intent
FragmentTransaction ft = getSupportFragmentanager().beginTransaction();
ft.add(R.id.layoutFrag, f, "PlanetaFragment");
ft.commit();
}

Nota: se for necessrio passar um parmetro para o fragment, a nica forma


de faz-lo adicionar o fragment dinamicamente pela API. O construtor do
fragment deve ser sempre vazio, e os parmetros devem ser passados pelo
mtodo setArguments(bundle). Um conceito importante sobre 0 ciclo de vida da
activity e fragments: caso a tela do aplicativo seja rotacionada, o Android vai
destruir a activity e seus respectivos fragments. Nesse momento, o mtodo
onSaveInstanceState(bundle) chamado para dar a chance ao aplicativo de salvar
as informaes no Bundle. Logo depois, o Android vai recriar as activities e
fragments passando nos mtodos onCreate(bundle) esse mesmo Bundle com OS
objetos salvos. importante ter conhecimento de que informaes passadas
pela intent das activities e argumentos dos fragments sobrevivem a esse ciclo de
vida. Portanto, caso um fragment tenha parmetros, eles sobrevivem durante a
destruio e recriao da activity e seus fragments. Faa o teste, gire a tela do
seu smartphone e monitore as chamadas dos m todos do ciclo de vida.
Captulo 8 1 Fragments 271
A activity est passando o Bundle com os parmetros pelo mtodo setArguments(bundle).
Isso significa que precisamos ler tais parmetros no fragment. Para ler esses
parmetros ou argumentos, basta chamar o mtodo getArgunents( ), que vai retornar
o Bundle. Note que uma boa prtica validar se o mtodo getArgunents() no vai
retornar nulo, pois isso pode acontecer caso os argumentos no sejam enviados
ao fragment.

PIanetaFragment.java
public class PlanetaFragnent extends Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,Bundle
savedInstanceState) {
View view = inater.inate(R.layout.fragnent_planeta, container, false);
if(getArguments() != null) { \
String planeta = getArgunents().getString("planeta");
setPlaneta(planeta);
}
return view;
}
@0verride
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(getArgunents() != null) {
String planeta = getArgunents().getString("planeta");
setPlaneta(planeta);
}

}
public void setPlaneta(String planeta) { .
Textview text = (Textview) getView().ndViewById(R.id.text);
text.setTet("Planeta: " + planeta);
l
}

Nota: eu gosto de utilizar o mtodo onActivityCreated(bundle) para iniciar a logica


da tela, porque neste momento sabemos que, pelo ciclo de vida, o metodo
onCreate(bundle) da activity j terminou. Outra vantagem que como o metodo
onCreateView(...) do fragment foi chamado e criou a view, podemos chamar o
mtodo getView() para acessar a view principal do fragment. Se voc chamar o
mtodo getView() durante o mtodo onCreateView(...), o resultado ser null.
272 Google Android - 4 edio
. . _ . _ - . ' ndo mas dessa vez
Pronto! Feito isso, o aplicativo deve continuar funciona , 0
- , ' l nocdio
fragment do planeta foi inserido pela API. Eu particularmente gosto de inserir
todos os fragments pela API, pois ca mais facil vericar todos e es g Java

8.9 Criando um layout dividido em partes nos tablets


Neste tpico, vamos continuar o exemplo dos planetas. O prorcimo passo e dividir
a tela em duas partes no caso dos tablets.
Para criar um layout especfico para tablets, podemos utilizar as seguintes pastas;
Pasta Descrio g *__ ____#___g mw, _
/res/layout-large Para tablets de 7'
/res/layout-xlarge Para tablets de 1O`
Por exemplo, se voc adicionar o arquivo /res/layout-xlarge/activity_main.xml no
projeto, quando o aplicativo for instalado no tablet de 10' esse layout customi
zado ser utilizado pelo Android. Tudo isso de forma automtica, apenas pela
conveno de nomes. Lembrando tambm que, a partir do Android 3.2, foram
criadas estas outras pastas:
Pasta Descrio
/res/layout-sw600dp Para tablets de 7'
/res/layout-sw720dp Para tablets de 1O,

Basicamente as duas pastas indicam o tamanho da tela na horizontal; a notao


sw de smallest width (menor largura). Os tablets de 7 tm pelo menos 60t)dp de
largura e os de 10 tm pelo menos 720dp de largura. Eu particularmente prefiro
utilizar a notao antiga /res/layout-xlarge para tablets de 1O pois funciona desde
o Android 3.0 e no apenas no Android 3.2.
Outra pasta importante a /res/layout-land, na qual o qualicador land de landscap
(l"l0fZ0fal); para customizar o layout para horizontal, basta inserir arquivos nessa
pasta. Voc pode inclusive fazer uma combinao dos identicadores, por exempl0
a pasta /res/layout-xlargc-land utilizada para tablets de 10 apenas na horizontal.
Para brincarmos com esse conceito, crie o arquivo e pasta /res/layout-xlarge-lam
actii/ity_main.xml, conforme mostra a figura 8_15_

. '
' a
_ i a t. z ea
Captulo 8 n Fragmemg

_n layout;
p c . i _ _~
p * zz- E
ft. mem
f dargelat1diactvty_mM,,;

wizwm@wt
z;3(.,;,,,,

alizado a seguir:
3 I Tm l
. .; ' * " c _. ""f`

H F* ldravvable-mdp hbleow
1 V llayout l L_lGridLayout
_' activity__main.xmI i Relativeuyout
* activity _planeta.xrnI C3 WMQS
9 .v
1

l
1

l * ?~*@@M#
Y%iz;zi
jmgnu
6dapter_pIneta.xm
fragment_pIant.mI
fr! rnent hnetasxml

P Small Button i
A *~ Bu
13RadoButton
|33v.zu.,-,,,gz0d, [ChecI<Bc|
AndroidManfest.xml H Swllh i
f E1 -szs=
I .V _tI9ss!sHsfrf=

androd:orentaton="horzonta1"
* r
L h:^--
- ~1._.,f~~~~-~.t
,_
' : ]]::;m`dTest E; ~- z _! ta' l:1N=10' i..})' @0Dmtme m- ). zq.
. V' Chuva ElFrameLayout \ ll W l .
~ Eb;_|vmnd,,d_| m,d p8_pmet ' {:|Lin:arLayout (Horizontal
V res ` E]LinearLayout (Vertical)
P Edrawable-hdpi l:lTabI:Layout

r! mzin TetV|ew
-mal Large Text
l L Medium Text
Small Tzzz

Figura 8.15 - Pr-visualizao do layout para tablet.

C* cdigo-fonte do arquivo /res/layout-xlargaland/actit/ity_main.xml pode ser visu

/res/layout-xlarge-Iand/activity_main.xmI
<?xm1 version="1.0" encodng="utf-8"?>
<LnearLayout xmlns:androtd="http://schemas.androd.com/apk/res/androd"
xmlns:tools="http://schemas.androtd.com/tools"
androd:layout_wdth="match_parent" androd:1ayout_height="match_parent"

fragment androtd:td:"@+d/PlanetasFragnent"
androd:layout_wtdth="Gdp" androd:1ayout_weight="1"
androd:1ayout_hetght="match_parent"
c1ass="br.com.Ivroandroid.planetas.P1anetasFragment
tools:1ayout="@1ayout/fragment_p1anetas" />

<fragnent android:td="@+td/P1anetaFragment"
androtd:layout_wdth="0dp" androd:1ayout_weght="1"
androd:1ayout_heght="match_parent"
cIass="br.com.1vroandrotd.planetas.P1anetaFragment"
tools:layout:"@1ayout/fragment_p1aneta" />
</LnearLayout>

Depois dessa alterao, ao executar o ap licativo em um tablet de 1O na hori


zontal, o layout ser ddivi i o em
d duas artesp (direita e esquerda). E necessrio
alterar o cdigo para que,l ionar
ao seum
ec laneta
p na lista, ele seja atualizado no
l
i
274 Google Android - 4 edio
, . . z - ' ctivities vai conti
fragment que esta na direita; caso contrario, a navegaf) de 3 uaf
acontecendo no tablet.
Um jeito fcil de fazer isso buscar of ragmen
t elo identicador
p com o mtodg
ndFragnentById(id). Se ele existir, sabemos que o apliCativO 6518 XCUf3Cl0 no
tablet de 10 e na horizontal. Nesse caso, basta atualizar o conteudo do fragmem
chamando qualquer mtodo de sua classe. Se o fragment da dlrlt 1130 X1stir,
significa que o aplicativo est executando em um smartphone OU HO tablet na
vertical, e a navegao de telas feita normalmente.

PIanetasFragment.java
public class PlanetasFragnent extends android.support.v4.app.Fragnent {
@0verride
public View onCreateView(Layoutlnater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) [

private Adapterview.0nItenClickListener onItemClickPlaneta() {


return new Adapterview.0nItenClickListener() {
@Override
public void onItenClick(AdapterView<?> parent, View view, int position, long id){
PlanetaAdapter adapter = (PlanetaAdapter) parent.getAdapter();
String planeta = (String) adapter.getIten(position);
Toast.makeTet(getActivity(), "Planetaz " + planeta, Toast.LENGTH_SHORT).show(h
PlanetaFragnent f = (PlanetaFragnent)
getFragnentHanager().ndFragnentById(R.id.PlanetaFragnent);
boolean dualLayout = f != null;
if(dualLayout) {
// Apenas atualiza o fragnent na direita se existe
f.setPlaneta(planeta);
} else {
// Faz a navegao de telas no caso do smartphone
Intent intent = new Intent(getActivity(),PlanetaActivity.class);
intent.putEtra("planeta",planeta);
startActivity(intent);
}

};
}

}
Captulo 8 1 Fragments 275
Execute o projeto no emulador com um tablet de 1O e confira o resultado. Gos
to muito de utilizar o emulador do Genymotion (genymotioncom), pois leve e
rpido. No Genymotion, voc pode criar facilmente o emulador do Nexus 10 e
testar esse exemplo.

Dica: no material de download do livro, voc vai encontrar os dois projetos de


exemplo com a lista de planetas. O primeiro projeto foi construdo apenas com
as activities; o segundo, com a soluo com fragments.

8.10 Exemplos da API dos fragments


Neste tpico, vamos estudar -o projeto DemoFragments, que demonstra vrios recursos dos
fragments. Recomendo que voc abra esse projeto no Android Studio e siga minhas
explicaes para entender cada funcionalidade. Os exemplos demonstrados no projeto
vo lhe fornecer uma base slida de conceitos sobre como utilizar a API de fragments.

No vou explicar o cdigo-fonte inteiro do projeto, pois ele simples. Vamos


apenas s partes principais. A MainActivity apresenta vrias aes na action bar,
que so configuradas no arquivo /res/menu/mena_main.xml. A gura 8.16 mostra
as opes dos itens -da action bar que existem no projeto. Cada opo demonstra
uma funcionalidade bsica -da API de fragments.

Find Fragi By id

Find Fzrag2 By Tag


E

Find Frag3 By Tag

Add Frag2
' HE

Remover Frag2

Repiace Frag

Add Fragd Args

Start Activity

Figura 8.16 - Pr-visualizao das aes da action bai:


276 Google Android - 4 edio
O arquivo /res/layut/attivily __main.xml da activity foi dividtdo`cmduas partsx

f,. =
Na parte dc cima loi inserido o Fragmentl de lorma estattca, L,21 pldflh. C 'diko
licou vazia, pois esse sera espao em que os outros lrmcms fall 'H<Js.

l ,,s
substitudos ou removidos dinamicamente./\ figura 8.17 mstra 21 Pfc'V'5U3l'Z<5<
d arquivo /res/layout/ac'tivity_main.xmI.
i: gq
,_ Rllvlly
W .munrml 1
~* . .
l Io; Liv _~;M!lu4' ' ;' `|'99"'"
1
l tz llll-dll
lb
l. fi
l

.., _. t.
l

"`*L, .__._... ..__., M . . __. .. et

Figura 8.7 - Pr-visualizao do layout da activity.

Depois dessa breve introduo, recomendo que voc execute esse projeto no emu
lador e brinque com cada opo do menu, pois so todas simples. Na sequencia,
estude o cdigo-fonte da activity para entender o que faz cada opo, mas basi
camente elas demonstram como utilizar a classe Fragmenransacton.
Uma vez que voc se familiarizou com o exemplo, vou dar algumas dicas do qUC
acho importante voc saber. Peo que execute as aes exatamente nesta ordem
que vou lhe dizer e conra os resultads.

Dica: no emulador, o funciona com boto voltar.

Cenrio 1

Clique Cm Find Fr92 ByTag. (_) resultado e que o fragment no existe.


(Llique ein Add Frag2 para adicionar o lragment2 no layout.
cap|I.I|0 8 n Fragments 277
Ao clicar em Find Frag2 ByTag, o fragment enggmradg

Clique em Remover Frag2 para remover o fragment pela API.


Cenrio 2
Clique em Add Frag2.

Ao clicar em Find Frag2 ByTag, o fragment encontrado.


Clique no boto Voltar. Isso vai desfazer a transao do FragmentTransacton, e vai
remover o fragment do layout. Isso acontece porque no cdigo foi utilizado o
mtodo addToBackStack(tag), que insere a transao na back stack dos fragments.
Clique no boto Voltar novamente. Dessa vez, a activity ser encerrada e o
aplicativo ser fechado. '
Cenrio 3
Clique em Add Frag2.

Clique em Add Frag2 novamente. O frag2 foi adicionado novamente por


cima do primeiro.
Clique no boto Voltar. Isso removeu o segundo frag2 que estava no topo
da pilha.
Clique no boto Voltar novamente. Isso removeu o primeiro frag2 que foi
adicionado.
Esse exemplo demonstra que devemos ter cuidado ao adicionar os
fragments, pois eles cam por cima dos outros. Como um FrameLayout foi
utilizado, os fragments adicionados ficam por cima.
Cenrio 4
Clique em Replace Frag3. Caso no exista nenhum fragment no layout, o
replace funciona como o add.
Clique no boto Voltar para desfazer a back stack. O resultado ser o layout
vazio.

Cenrio 5
Clique em Add Frag2.

Clique em ReplaceFrag3. O frag2 substitudo pelo frag3.


Clique no boto Voltar. A operao desfeita e o frag2 volta a ocupar O
layout.
278 Google Android - 4 edio

Clique no boto Voltar. O fragment2 removido do laYUt


Esse exemplo demonstra como funciona a back stack dos fragments.
Lembre-se de que isso acontece porque o mtodo addToBackStack(tag) fm
chamado no cdigo.
Cenrio 6
Clique em Add Frag4 Args.

Esse exemplo demonstra de forma simples como passar argumentos para


o fragment pelo mtodo setArguments(bund1e).
Clique no boto Voltar para remover o fragment.
Cenrio 7
Clique em Start Activity.

Esse exemplo demonstra de forma simples como iniciar uma activity que tem
um fragment no seu layout. E ainda como passar parmetros para o fragment.
Clique no boto Voltar para voltar tela anterior.
Cenrio 8
Clique no boto ReplaceFrag3.
Note que apareceu uma ao na action bar, com a gura de uma carinha/smile
Isso signica que fragments podem adicionar aes na action bar.
Ao clicar na ao, o prprio fragment faz o tratamento do evento.
Nos prximos tpicos, vamos revisar alguns conceitos importantes que vimos
nos exemplos desse projeto, como a back stack e como os fragments adicionam
aes na action bar.

8.11 Back stack

O mtodo addToBackStack(tag) da classe Fragmenransaction adiciona um fragml


na baCl< Sf2Cl<, O que significa que, ao clicar no boto voltar, a operao desfeita.
O parmetro do mtodo addToBackStack(tag) a tag que identica esta transao.
mas (5350 f55 nao Sela 1mP0ffHt para a lgica do seu aplicativo possvel paS5f
Wu- Q metodo PPBC|<5Cl<() da Classe Fragmentanager utilizado para desfazer a l'
uma Operao ou 5eJa desempilhar a back stack dos fragments, e funciona COITW
o boto Voltar. O mtodo popBackStack(string tag, int ags) da classe Fragrientllanager
Captulo 8 1 Fragments 279
recebe a tag que identifica a operao e um flag, que pode ser O ou a constante
Fragnentanager.POP_BACK_STACK_INCLUSIVE. Isso faz com que a back stack volte at
a tag desejada, por isso voc pode marcar a back stack ao chamar o mtodo
addToBackStack(tag). Se passar o nmero O (zero) no parmetro flags, a operao des
feita at a tag informada, mas, se passar POP_BACK_STACK_INCLUSIVE, a prpria operao da
tag informada desfeita, ou seja, todas as opes so desempilhadas da back stack.
Para concluir a ideia, cada vez que utilizamos a FragnentTransaction e fazemos
commit(), podemos dar um nome para essa transao. exatamente isso o que
faz o mtodo addToBackStack(tag). Ao adicionar esta transao na back stack, tudo
pode ser desfeito para voltar ao estado anterior. O mtodo popBackStack(string tag,
int ags) fora o estado a voltar exatamente para o nome da tag que voc salvou.

Nota: da mesma forma que o boto voltar utilizado para desempilhar as


activities da pilha de atividades (activity stack), o comportamento o mesmo
caso os fragments sejam adicionados na back stack. Nem sempre voc vai utilizar
esse recurso, vai depender do que sua aplicao est fazendo, mas importante
conhecer seu funcionamento.

8.12 Adicionando botes na action bar pelo fragment


Um fragment pode adicionar botes na action bar normalmente, mas para isso
obrigatrio que ele informe ao sistema por meio da chamada do metodo
setHas0ptionsMenu(true).

Se o fragment informar ao sistema que ele deseja adicionar aes na action bar, o
mtodo onCreateOptionsMenu(menu,inater) ser chamado. Basta inflar o XML de menu.
A seguir podemos visualizar o cdigo-fonte da classe Fragnent3 do exemplo anterior.
3

Fragment3.java

@0verride _ _
public class Fragnent3 extends android.support.v4.app.Fragnent {

public View onCreateView(Layoutlnater inater, Viewroup container,


Bundle savedInstanceState) { .
View View = inater.inate(R.layout.fragnent_3, container, false);
setHas0ptionsMenu(true);
return view;
}

@0verride
P ublc void 0nCreate0ptionsHenu(Menu menu, Henulnater inflater) {
280 Google Android - 4a Edio
inater.inate(R.nenu.nenu_frag3, menu);
}

@0verride
public boolean onOptionsItemSe1ected(HenuItem item) {
int id = item.getItemId();
if (id == R.id.action_he11o_frag3) {
Toast.nakeText(getActivity(),"Hello ActionBar Frag 3",Toast.LENGTH_SHORT).Show(M
return true;
}

return super.on0ptionsItenSe1ected(item);
}

public void he11o() {


Toast.makeTet(getActivity(),"Hello Frag 3",Toast.LENGTH_SHORT).show();
}

) /res/menu/menu_fragment3.java
<menu xn1ns:android="http://schenas.android.con/apk/res/android"
m1ns:too1s="http://schenas.android.com/tools"
xm1ns:app="http://schenas.android.con/apk/res-auto" >
<item android:id:"@+d/action_he11o_frag3"
android:title:"@string/he1io_frag3" android:icon="@drawab1e/smilel"
app:showAsAction="a1ways" />

A gura 8.18 mostra o boto com o cone de smile, o qual foi adicionado pelo fragmem.

_.,1
l7f'~`u l .)i]i

OK

Heilo Activity

F Q e- rw

FEWG 8.18 - Fragment com ao na action ban


Captulo 8 1 Fragments 231
8.13 Salvando o estado de um fragment
A maioria dos aplicativos consulta dados na internet por meio de um web service
e essa tarefa pode demorar
g os alguns
epen endose
da und d d `da' co
velocidade
nexo. Caso o usurio gire a tela, por padro o Android vai destruir a activity e
f
seus ragments, para depois recri~los. Nesse momento podemos salvar o estado
da tela, para depois recuperar os dados do web service e evitar a necessidade de
buscar os dados novamente da internet.
No exemplo anterior, o fragment 1 que foi inserido de forma esttica no layout da
activity demonstra como salvar o estado. Se voc clicar no boto OK da tela, ver
que uma varivel int count incrementada e impressa na tela com um toast. A se

}.
guir, podemos visualizar o cdigo simplicado desse fragment, no qual podemos
Ver como salvar 0 estado da varivel count, a fim de preservar o estado durante a
troca de orientao da tela.

Fragmentl .java
public class Fragmentl extends android.support.v4.app.Fragment {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_1, container, false);
// Recupera o estado da varivel
if(savedInstanceState != null) {
count = savedInstanceState.getInt("count");

count++; _
view.ndViewById(R.id.bt0k).set0nClickListener(new 0nClickListener() {
@0verride
public void onClick(View v) {

Toast makeTet(getActivity(), "Countz " + Count, Toast LENGTH_L0NG)-Sh0W(),


}

}) ;
return view;
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outSta'C2); d
outState.putInt("count",c0Unt)5 / Salva esta
}

}
, . ndle no '
282 Google Android - 4 edio
(,omo vimos, para salvar o estado, basta preencher o Buf metodo
onSaveInstanceState(bundle), que chamado antes de destruir O fgmem- Dpoig,
ao recriar a view do fragment, basta ler os dados desse Bundle: Dependendo dos
objetos e variveis do fragmer1t,pOdmOS decidir 55 C necessario mlclallzaf 8 tela
ou no. Por exemplo, podemos decidir se nece ssrio buscar os dados do web
service ou no.

Outra maneira de preservar o estado do fragment, que muito utilizada devido


sua facilidade, o mtodo setRetainInstance(true). Caso esse mtodo seja chamado, 3
instncia do fragment ser preservada durante a rotao. Assim, o objetivo inteiro
do fragment ser mantido em memria, e apenas o mtodo onCreateView(inater,
container,bundle) ser chamado para recriar e retornar a *view Todos os atribu
tos, objetos e variveis do fragment sero mantidos vivos, pois a instncia do
objeto do fragment preservada. Ento faa o teste! Basta chamar o mtodo
setRetainInstance(true) durante os mtodos onCreate() ou onCreateView() do fragment
e pronto. No necessrio implementar o mtodo onSaveInstanceState(bundle).

Fragment1.java
public class Fragnentl extends android.support.v4.app.Fragment {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_1, container, false);
// Indica que este fragnent deve preservar seu estado
setRetainInstance(true);
view.ndViewById(R.id.bt0k).set0nClickListener(new 0nClickListener() {
@0verride
public void onClick(View v) {
count++;

Toast.makeTet(getActivity(), "Countz " + count, Toast.LENGTH_LONG).show();


}
});
return view;
}

|'+ . [estar 3 fmca de Orientao da tela no emulador, pressione as teclas


gvfafa
(apitulo 8 n Fragments 283
8.14 Vantagens de utilizar os fragments
Depois de ler este capitulo, vamos revisar alguns conceitos importantes e as van
tagens de utilizar fragments.
1. Deixa o cdigo da activity simples, pois a lgica ca nos fragments.
2. Um fragment tem seu prprio ciclo de vida, o qual associado ao ciclo de
vida da activity Por exemplo, se a activity chamar os mtodos onPause () ou
onResume(), eles tambm sero chamados em todos os fragments da tela para
permitir que cada classe gerencie o seu estado.
3. Permite criar componentes reutilizveis.
4. Permite reutilizar o cdigo entre as verses smartphone e tablet, ou at
mesmo reutilizar cdigo dentro do smartphone.
5. Fragments so como miniactivities, pois tm um ciclo de vida bem denido.
6. Fragments so como views, e basta inclu-los no layout.
7. Fragments so utilizados em aplicativos que utilizam action bar com Tabs
e ViewPager, para gerenciar o contedo de cada tab ou pgina.
8. Com o uso da API, os fragments podem ser adicionados, substitudos e
removidos facilmente de qualquer posio do layout.

8.15 Links teis


Neste captulo, estudamos uma das principais APIs de desenvolvimento de UI no
Android, utilizada principalmente para criar componentes reutilizveis de cdigo,
facilitando a manuteno do projeto.
No projeto dos carros que vamos desenvolver nos prximos captulos, usaremos
fragments em conjunto com a navegao por tabs da action bar. Fique tranquilo,
pois ainda vamos estudar muito esse assunto, e o melhor: voc vai aprender de
senvolvendo o aplicativo dos carros passo a passo.
cparei alguns dos principais links cuja leitura recomendo:
Android API Guides - Fragments

http://developer android.com/guide/componemts/fragmcnts.html
Android API Guides - Supporting Tablets and Handsets

http //developer android. com/guide/practices/tablcts-and-humiscts. html


. Android Training - Building a Dynamic UI with Fragments

http '//developer android. com/training/basics/fragmcr1ts/i ndex.lmn1


~` `e CAPTULO
/z' .J
1

Animaes
H
.

Animaes do um acabamento prossional ao aplicativo, e costumam enriquecer


a experincia do usurio.
Neste captulo, vamos estudar diversos recursos de animaes, como movimentar
a view pela tela, criar efeitos de transparncia e muitos mais.
Vamos estudar a API de Animations, utilizada desde as primeiras verses do
Android, assim como a nova API Animator, criada no Android 3.0 Honeycomb.
Alm disso, veremos diversas dicas sobre animaes em geral.

9.1 Drawable Animation

Para comear a brincadeira, vamos estudar a classe androd.graphcs.drawab1e.


AnmatonDrawab1e, utilizada para criar uma animao de guras como se fosse um
GIF animado.

Esse tipo de animao feito com um arquivo XML na pasta /res/drawable, basta
congurar a lista de guras.

/res/drawable/Iist_Ioading.xmI
<?m1 verson="1.0" encoding="utf-8"?
<anmation-list xmlns : android="http: / / schemas . and rod . com/ apk/ res / androtd
android:oneshot="fa1se" >

<iten androdzdrawable @drawab1e/1oadng_01 androidzduratgn 299


<i.tem androdzdrawable @drawab1e/1oading_02 androidzduration 200
<ten androidzdrawable @drawab1e/1oading_03 androdzduraton 266
<tem androd:drawab1e @drawable/loadng_G4" androdzduratign 299
</animation-1ist

O arqulve representa uma gufa que vai animar as imagens 1oadng_61,1oad9-92


1oadng_03, 1oadng_04 sequencialmente, com um intervalo de 200 milissegund05
284
Captulo 9 n Animaes 285
Para ter essa animao em um Imagevew basta utilizar a propriedade androtdzsrc
normalmente, informando a imagem que contm a lista
<ImageView android:id="@+id/img"
3"dfd1lY0U_Wdth="wrap_content" android:1ayout_height="wrap_content"
android:src="@drawable/1ist_1oading" />

No cdigo, podemos usar o mtodo getDrawab1e() para obter o objeto AnimationDrawab1e


da animaao e chamar o mtodo start().
I

Imagevew img = (lmageview) ndVewById(R.d.mg);


AnimationDrawab1e anim = (AnimationDrawab1e) img.getDrawable();
anm.start();

Tambm possvel utilizar a propriedade androdzbackground em vez de androidzsrc.


<ImageView androd:d="@+id/img"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:background="@drawab1e/1st_1oadng" />

Neste caso, usaremos o mtodo getBackground() para recuperar o Anmatonmawable.

Imageview img = (lmageview) ndViewById(R.id.mg);


Animatable anm = (AnimationDrawable) img.getBackground();
anim.start();

9.2 Classe Animation


At antes do Android 3.0, a classe android.view. anmation.Anmati.on junto com a classe
android.view.anmaton.AnmatonUtils formaram a dupla principal para trabalhar com
animaes no Android. A classe Animation tem cinco subclasses:
AlphaAnmaton - Animao de transparncia.
RotateAnmation - Animao de rotao.
Sca1eAnmaton - Animao de escala.

. . ` 'I , ' ~ L 'L _ 3 T1


Trans1ateAnmaton - Animao de mOVimI1f0
AnimationSet - Classe utilizada para criar um grupo de 11IT1210@5 Pq* 1 SCM
executadas ao mesmo tempo.
Essas classes de animaes podem ser encapsuladas inclusive em arquivos XML,
para evitar que o desenvolvedor escreva muito cdigo. Por exempl0, SC P1`CC51`m5
criar uma animao qu e vai deixar uma view transparente gradatvamllf, WSY11
este simples trecho de codigo:
286 Google Android - 41 Mm
View view = ?
Animation a = AnimationUti1s.IoadAnimation(this,and 'd.R.anim.fade_in);
FOI
a . setDuration(2000);
view.startAnimation(a);

O mtodo utiliza a classe AnimationUti1s para carregar uma animao de transparn_


cia que vai deixar a view invisvel. Note que o parmetro passado e um recurso dg
animao que pode ser acessado pela famosa classe R. Nesse caso, usamos a classe
android.R nativa do Android para acessarmos um recurso de animao j existente
na plataforma. Tambm possvel criar animaes personalizadas e inseri-las na
pasta /res/anim. Observe que a classe Animationtiis serve para inflar 0 arquivo de
animao em um objeto do tipo Animation. Nesse exemplo estamos carregando 3
animao android.R.anim.fade_in, e isso retorna um objeto do tipo A1phaAnimation.
Depois que uma animao criada, podemos customizar a forma como ela vai
ser executada. Nesse exemplo, foi chamado o mtodo setDuration(mi11is) para con
gurar o tempo em milissegundos que o efeito da animao deve durar. A seguir

Mtodo Descrio _g
temos alguns dos principais mtodos da classe Animation.

setDuration(mi11isj- Congura o tempo em milissegundos que o efeito da


animao deve durar.
setFi11After(boo1ean) Este ag indica que o efeito da animao, conhecido como
transformao, deve ser mantido ao terminar a execuo
O valor default false. Por exemplo, se criarmos uma
animao de fade_out em uma vievig a mesma ir desa
parecer aos poucos. Mas ao nal da animao a view vai
voltar ao normal, aparecendo novamente. Para que a view
permanea invisvel ao trmino da animao, esse mtodo
deve ser chamado.
S@Uf@fD0t0f() Informa qual a implementao da interface Interpoiator
que ser utilizada. O padro Linearlnterpolator.
setRepeatCount(int) Quantidade de repeties que a animao deve efetuar.
O padro O (zero).
5@tRP@tN0d(t DO) Conguraseaanimaodeverepetirouno.chamadop1I`1
deixar uma animao em loop: setRepeatMode(Animati0
INFINITE).

Por ultimo, depois que a animao foi criada, basta chamar o mtod0
view.startAnimation(animation) para iniciar a animao

N C fC0mIidVl Crlar as animaes utilizando arquivos de XML dentro


da pasta /res/amm. Podemos e
ncontrar vrias animaes j disponveis de
forma nativa no Android, dispensando a criao de arquivos de animao
Captulo 9 n Animaes 287
customizados. Por exemplo, as animaes fade in e fade out j esto presentes
no Android; basta selecionar a classe android.R para utilizar os recursos nativos.
Dentro da pasta /android-sdk/platorms/$(plataformaj/data/res/anim voc pode
encontrar as animaes padres da plataforma.

9.3 View Animation

View Animation 0 ,framework de animaes at o Android 2.x, e com ele podemos


criar efeitos de transparncia, movimento, escala e rotao de forma simples.
Nos prximos tpicos, vou explicar como criar cada um desses tipos de animao,
e recomendo que voc abra 0 projeto ViewAnmaton de exemplo deste capitulo para
acompanhar as explicaes. importante que voc execute o cdigo no emulador
para visualizar as animaes acontecendo. A gura 9.1 mostra o projeto de exemplo
executando no emulador, com um exemplo da animao de transparncia, que
a classe AlphaAnmaton, a qual vamos estudar no prximo tpico.

liiimf AnImarcomAPI
Rotate

Scale
i

"Translate
i.
AnimationSet l

i; _d
, -_ i ~ ,_ _ ,
__A__
Animationstener 1
i. .. _. -~~f~~~---f-*~-^
E

Figura 91 -Imagem quase desaptzmceizco durante a animao de transparncia.

9.4 AIphaAnimation
. . ' ' ~~ >scondei' ii view
A classe A1phaAnmation perm ite criar os famosos efeitos de fade_in e fade_out. 1721111
^ ' da viewcom
alterar a transparencld 0 obietivo de mostrar ou ea
288 Google Android - 4 edio
Na prtica, a classe AlphaAnination altera a propriedade alpha da view FSPOSvcI
pela transparncia. Sempre que o alpha for 0.0 (zero), Slgmca que '3 VICW est
invisvel, e sempre que o alpha for 1.0 ela est 100% visvel. A propriedade alpha
um oat que varia entre 0.0 e 1.0. A tabela a seguir resume os dois parametros
utilizados pela classe AlphaAnination.

Pp'dq,___.__.____ -_ .-.- - -_ azaa A A- -~


oat fronAlpha Valor inicial da propriedad lph
oat toAlpha Valor nal da propriedade alpha.
Para usar a classe AlphaAnination, podemos utilizar um trecho de cdigo como este;
View view = /* qualquer view */
boolean show = false; // Mostrar ou no a view?
AlphaAnination fade_out = new AlphaAnination(1.0f, 0.0f); // Apaga a view (alpha 1 parae)
AlphaAnination fade_in = new AlphaAnination(0.@f, 1.Gf); // Mostra a view (alpha G para1)
AlphaAnination a = show ? fade_in : fade_out;
a.setDuration(2000); // 2 segundos
a.setFillAfter(true); // Manter o efeito no nal da animao
view.startAnimation(a);

Outra forma de fazer a animao criar um arquivo XML com a denio


da animao e inseri-lo na pasta /res/anim. Nos exemplos a seguir, a animao
/res/anim/fade_in.xmI faz a propriedade alpha ir do 0.0 invisvel para 1.0 visvel. A ani
mao /res/anim/fade_out.xmI faz a propriedade alpha ir do 1.0 visvel para 0.0 invisvel.

/res/anim/fade_in.xmI
<?xml version="1.0" encoding="utf-8"?>
<alpha xnlns:android="http://schenas.android.con/apk/res/android"
android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="2000" /

/res/anim/fade_out.xmI
<?xnl version="1.0" encoding="utf-8"?>
<alpha xnlns:android="http://schenas.android.con/apk/res/android"
android:fromAlpha="1.0" android:toAlpha="0.9" andrQid;durati0n="z9en" /

Uma vez que os recursos de animao foram criados, podemos utilizar a classe
Aninationutils para carregar e iniciar a animao. Nesse momento podemos utilizar
3 Classe R do 110550 pI`OpI'lO pI'Oj[O para 3CSS8I` SSS I'CLlI'SOS.
View view = /* qualquer view */
boolean show = false; // Mostrar ou no a viewv
int anin = show ? R.anin.fade_in : R.anin.fade_out'
.O
Captulo 9 1 Animaes 289

Animation a = Animationutils.1oadAnimation(this, anim);


view.startAnimation(a);

Nota: estou mostrando apenas o trecho de cdigo necessrio para criar as


animaoes, por isso recomendo que execute no emulador o projeto de exemplo
deste capitulo.

9.5 RotateAnimation

A classe RotateAnimation serve para para rotacionar uma vievig na qual so utilizadas
as coordenadas x e y e um ngulo em graus para a rotao. Para essa animao;
c possivel configurar um ponto que ser o eixo da rotao. O padro da rotao
a partir do canto superior esquerdo da vievig que tem os pontos x = O e y = O.
A tabela a seguir resume os dois parmetros utilizados pela classe RotateAnimation:
Propriedade Descrio

oat fromDegrees Valor inicial do ngulo para rotacionar.


oat toDegrees Valor nal do ngulo para rotacionar.
int pivotXType Tipo da rotao aplicada baseada no eixo x, podendo assumir uma
das trs constantes: Animation . ABSOLUTE,Animation . RELATIVE_T0_SELF
011Animation.RELATIVE_T0_PARENT.

oat pivotXVa1ue Valor no eixo x para servir de eixo da rotao. Pode receber um
valor absoluto em pixels, em que O (zero) representa o canto
esquerdo superior ou valores relativos coordenada da view ou
de seu layout pai.
int pivotYType Idem a propriedade pivotXType mas para o eixo y
oat pivotYVa1ue Idem a propriedade pivotXVa1ue mas para o eixo ya

O cdigo a seguir demonstra como rotacionar uma view em 1800 graus com a
classe RotateAnimation. A primeira animao gira a vievig e a segunda gira novamente
para a posio de origem.
view view = /* qualquer view */
boolean primeiravez = true;
int angulo = 189; // Girar 180 QFHUS
Animation giralda = new RotateAnimation(0,angu1o,
Animation.RELATIVE_T0_SELF, 0.5F,
Animation.RELATIVE_T0_SELF, 0.5F)5
Animation giraRetorno = new RotateAnimation(an9Ul0,0,
Animation.RELATIVE_T0_SELF, 9.5F,
290 Google Android - 4= edio
Animation.RELATIVE_T0_SELF. 0.SF);
Animation a = prmeiravez ? giralda : gtraRetorno;
a.setDuration(2000); // 2 segundos .
a.setF11After(true); // Manter o efeito no nal da antma
view.startAnination(a);

Note que foram passados no construtor da classe RotatonAni.maton os graus inicial e


final da rotao (O e 180), e as coordenadas X e Y que sao o eixo da rotaao. Para 3
animao de retorno, foram denidos os graus inicial e nal da rotaao (l80 e 0)_
Foi informado Animation.RELATIVE_TO_SELF para indicar que a coordenada relativa
ao prprio objeto, sendo que o valor 0.5F correspondente a 50%. Nesse caso,
o valor 0.0F igual a 0%, O.5F igual a 50% e 1.0F igual a 100%. A constante
Animation.RELATIVE_T0_SELF utilizada para indicar que a rotao relativa ao objeto
que est sendo animado. Dessa forma, sabemos que o ponto X e Y que indicamos
com o valor 0.5F (50%) o centro da imagem. Se alterarmos esta constante para
Animation.RELATIVE_T0_PARENT este ponto de eixo passaria a ser o centro do layout.
A diferena na animao seria gritante. Ao rotacionar no eixo de si mesma, a
imagem nem sequer se move de lugar, ela apenas gira. Mas, se rotacionarmos a
view utilizando outro ponto como o centro do layout, ela iria se movimentar ao
redor desse ponto.
A seguir podemos ver os dois arquivos XML que fazem esta mesma rotao de
girar a view em 180. Observe que no arquivo o ngulo de rotao vai de 0 a
18O para dar um giro completo, e o eixo da rotao foi definido no centro da
imagem, conforme as propriedades androd:pi.votX e andro'td:pvotY, que no XML
podem receber o valor de 50%.

/res/anim/rotate_gira_ponta_cabeca.xmI
<?n1 verson="1.0" encoding="utf-8"?
!-- Rotao do ngulo 0 para 180 graus. -->
<rotate xmlns:androd="http://schenas.androd.com/apk/res/androd"
androd:fronDegrees="0" androd:toDegrees="189"
androd:pvotX="S0%" androd:pvotY="S0%
androd:duraton="500@ androd:11Aftef="true" /,

/res/anim/rotate_gira_ponta_tabeta_retorno.xmI
<?n1 version="1.0" encoding="utf-8"?>
<rotate xm1ns:androd="http://schenas,andr0 d.com/apk/res/android"
androtd:fronDegrees="180" android:toDegrees="0"
android:pivotX="5G%" android:pivotY=59%"
android:duraton="500B" androd:I1After="trUe" /,
(aptulo 9 z Animaes

Utilizando esses XMLs, podemos inflar o objeto da animao:


Animation giralda = Aninationutiis.ioadAnination(this, R.anin.rotate_gira ponta cabeca)
Animation giraRetorno = Aninationtils.loadAnination(this,

ifa1 ' ~ ii
R. anin. rotate_gira_ponta_cabeca_retor`no);

A gura 9.2 exibe o resultado desse exemplo, e mostra o bonequinho do Andmld


de ponta cabea depois da rotao.

1 al
' An " ' . t l . -- a , l
q Imar com XML q Animal com API T .ggfnzy mm zm, Amma, com Am
*^~ f'~--~~~ ~ f~-~~ ~-~~~ -- --_-~. __;~z z.. ..._ , ....,.. _ 1 '_.H_,,,M___;(_,_%~J_(__M_ _ 'A:_=`M___~._M

il

Figura 9.2 - Exemplo da imagem rotacionada em 180.

Nota: a notao de percentual (exemplo android:pivotX="100%") no XML de


animao vai por padro considerar a congurao relativa view, como se
tivesse utilizado Animation.RELATIVE_T0_SELF. Para fazer com que esse parmetro
seja considerado relativo ao layout pai, deve-se inserir um "p" depois do sinal
de percentual, por exemplo, android:pivotX="100%p". Dessa forma seria como se
Animation.RELATIVE_T0_PARENT fosse usado pela API. interessante que voc brinque
bastante alterando os parmetros do eixo da rotao, para que entenda bem o
funcionamento das animaes.

9 6 ScaIeAnimation
\ classe Sca1eAnimation permite aplicar animaes de escala, para diminuir ou
1ilI11CI1t8I` uma vievi: O exemplo de cdigo a seguir faz a view diminuir graditi
\ imente at ela desaparecer. Na prxima vez, vamos fazer com que ela aumente
dt novo, gradativamente, at o seu tamanho normal.
292 Google Android - 4 edio
View view = /* qualquer view */
boolean primeiravez = true; // 59 ff 3 Primeira vez, vai diminuir...
int angulo = 180;
ScaleAnimation encolher = new SClAt0(
1.0f, G.0f, // X inicial e nal
1.0f, 0.0f, // Y inicial e nal
Animation.RELATIVE_T0_SELF, 0.5f, // Eixo X
Animation.RELATIVE_TO_SELF, 0.5f // Eixo Y
);
ScaleAnimation aumentar = new ScaleAnimation(
0.0f, 1.0f, // X inicial e nal
0.0f, 1.0f, // Y inicial e nal
Animation.RELATIVE_T0_SELF, 0.Sf, // Eixo X
Animation.RELATIVE_T0_SELF, 0.5f I/ Eixo Y
);
Animation a = primeiravez ? encolher : aumentar;
a.setDuration(2000); // 2 segundos
a.setFillAfter(true); // Manter o efeito no nal da animao
view.startAnimation(a);

Assim como as outras animaes, podemos fazer o mesmo efeito com recurso
XML, o que em minha opinio bem mais simples.
O exemplo a seguir dene que as coordenadas iniciais da animao so 1.0 (100%
tanto de X quanto de Y, informando o tamanho total da imagem. E as coordena
das nais informadas so 0.0 (0%), informando que o tamanho nal da imagem
deve ser reduzido em nada. Como 0 eixo da animao foi denido como 5090.1
imagem vai diminuir aos poucos at desaparecer no seu centro.

i /res/anim/scaIe_dimnuir.xmI

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


<scale xmlns:android="http://schemas_android.com/apk/res/android"
android:fromXScale="1.0" android:toXScale="0.0"
android:fromYScale="1.0" android:toYScale="0.0"
android:pivotX="50%" android:pivotY="50%"
android:llAfter="true" android:duration="2000"
/

A prxima animao o contrrio da outra. As coordenadas iniciais so 0.0, ndl


Cd qm a wam P150 5f, pois est com seu tamanho zerado. Dessa 'l
as coordenadas nais foram
_ _ _ denidas como 1.0 (1O0/Q), para que a imagem
ao seu tamanho inicial.
Captulo 9 I Animaes 293
%i /res/anim/sca|e_aumentar.xm|
<?mi verson="1.0" encodng="utf-8"?>
<sca1e m1ns:androd="http://schemas.androd.com/apk/res/androd"
android:fromXSca1e="0.@" androd:toXScale="1.0"
androd:fromYScale="0.G" androd:toYSca1e="1.0"
androd:pivotX="5@%" androd:pvotY="50%"

Ii
android:l1After="true" androd:duraton="20G0" />

/\ figura 9.3 mostra o resultado desse exemplo. Na primeira animao, a imagem


vai diminuir at desaparecer, e depois na prxima vez vai aumentar gradativa
mente at ficar com o tamanho original. Na parte direita da gura, podemos ver
a imagem diminuindo, quase antes de desaparecer.

, Animar com XML , 1 Animar com API i L Anlmar com XML /mimar esta wi
LNB* __ vi ,`_ _ ____W_W*__M_4 I l _,____,_ _, _,_ K __W

^aasaa
' ..
tgl l~ z *
l

L >~;.`5*;;,'; z.Ea.{
. . .
._ ;

i i
~
l

l
E

1E
il

Figura 9.3 - Exemplo da imagem diminuindo de tamanho.

9.7 TransIateAnimation
z\ classe TransiateAnmation serve para mover urna view pelo layOL1f ffspeclcanfio
35 goordenadas X e Y iniciais e nais da animao. Essas coordenadas podem ser
passadas como um valor absoluto ABSOLUTE em pixels de tela, ou relativo a propi ia
view RELAT1vE_To_sELF ou ao seu layoutpa1RELATIVE_T0_P^RENT
. . " . ~ z ~- ~> ateAnimation.
A mbkh il Scguif fgmn os parametros utilizados pela classe Transi
I GOOQIQHIIOU-QIQQM
Propriedade DdCfI
non fmnnym Tipo da coordenada x inicial.
float fromtvaloe Valor da coordenada x inicial.
'L-nt toXTwe Tipo da coordenada x nal.
float t~oVa1ue Valor da eoordenacln x nal.
int front/Type Idem explicado no eixo x.
float front/Value Idem eqIiendo no -eixo x.
int toi/Type Idem explieado no eixo x.
float toYValue Idem explicado no eixo X.
Biisicainente. n elnsse recebe as coordenadas X e Y iniciais c nais. 'lemos assim
quatro pontos. Para cndn ponto, e especificado qual o tipo do valor, que podf
ser Anlnatton.ABSOLUTE. Animation.RELATIVE_T0_SELF ou Animation.RELATIVE_T0_PARENT_
Sempre que o tipo de um valor 'for Antnatton.ABSOLUTE, aquele parmetro poder
recelwer valores alwso-luros. que so valores numricos que especieam a real
posio ou y em pixels. Se o tipo do parmetro for Antnatlon.RELATIVE_T0_SELF ou
Antnatton.RELATIvE_T0_PARENT, os valores informados sero em percentual. No XMI
so utilizados os valores 0% e 100%. e nojava so utilizados os valores ODF e IDF.
O trecho de codigo n seguir mostra como mover a .imagem para baixo e depois
para eimn. O valor 2 neste exemplo 6 para a imagem se mover em duas veres n
seu tamanho
View vtew - I* qualquer view */
boolean prtnetravez = true; /I Se for a prtnetra vez, nover para baixo.
Antnatton-noverParaBato = new TranslateAnnatton(
Animation.RELATIvE_T0_SELF, 8.0F. Antnatton.RELATIVE_T0_SELF, 0.0F,
Anlnatlon.RELATIVE_T0_SELF, 0.8F, Animation.RELATIVE_T0_SELF, 2.0F
):
Animation noverParaCtna = new Trans1ateAntnation(
Antnatton.RELATIV_T0_SELF, 0.0F, Antnatton.RELATIVE_T0_SELF, B.6F,
Antnatlon.RELATIVE_T0SELF, 2.0F, Antnation.RELATIVE_T0_SELF, 9.6F
):

nntnatton a = prtnetravez ? noverearahaixoz noverarattna;


a.setDuratton(200B); I I 2 Segundos
4-$\Flll~^f10f('U0) /I Manter o efeito no nal da antnaao
vtew.startAntnation(a);

-lbm Pdl`"*"5 1`*~" *HITUQCS de movimento utilizando XML. O CJIIW 'l


. ` ~ 3 1 0 '
seguir mostra uma animao-que move a imagem pu- baixo (mm. quc sis mile
nd " fim E l~ O WIN -00 ki 6 para a imagem se mover duas vezes o seu tam1lI^`
Captulo 9 n Animaes 295
ffi /res/anim/transIate_mover_para_baixo.xm|
<?m1 verson="1.0" encodng="utf-8"?>
<translate m1ns:androd="http://schemas.android.com/apk/res/android"
androd:fromDe1ta="0.0" android:toXDe1ta="0.0"
androd:fromYDe1ta="0.0" android:toYDe1ta="2@G%"
androd:1IAfter="true" android:duraton="2@00" />

E a animao a seguir o contrrio, faz com que a imagem volte para cima.

() /res/anim/transIate_mover_para_cima.xml
<?m1 verson="1.0" encodng="utf-8"?>
<trans1ate xmlns:androd="http://schemas.androd.com/apk/res/androd"
android:fromXDe1ta="@.0" androd:toXDe1ta="@.0"

l
android:fromYDe1ta="200%" androd:toYDe1ta="@.0"
android:11After="true" androd:duraton="2000" />

Nota: 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 ao layout pai, utilize o valor com um "p" no final, como por exemplo
androd:toYDe1ta="50%p".

Ao executar esse exemplo, a imagem vai mover para baixo numa distncia de duas

i. I1g 11 _
vezes o seu tamanho, e depois na prxima vez a imagem vai se mover para cima
at voltar sua posio original. A gura 9.4 mostra o resultado dessa animao.

(___ _

*E . *llf com XML


_ | pV. s Animar com ,Wi
l

ML
i AnimarAlmf
com(Om
X _P
1 Wgmwg _``, __,\__,A_,___= W; ~_,_.\__,,.--_-._a._._...~... ; ~ ~ -fr f ^" 's

^ - e _9.4
ZIHZLI ~ -dti ozs
em '- t - ~ ' /mito
Figura Extmplo d 3 P dt st mova pam . .
296 Google Android ~ 46 adm
9.8 AnimationSet
'ma es para serem executad
A classe AnimationSet permite agrupar vrias am . b_ HS ao
mesmo tempo. A maneira de unliz-la simples: basta criar um o jeto Antmattonsez
e adicionar as animaes que devem ser executadas.
A classe AnimationSet contm ainda os mtodos para configurar as propriedades das
animaes, como o mtodo setDuration(1ong),o qual altera 0 tempo de execuo Se
alguma propriedade for alterada nessa classe, ela vai sobrescrever as propriedades
que foram antes conguradas em cada animao que foi adicionada na lista. Para
facilitar o entendimento, o cdigo-fonte a seguir mostra como mover uma imagem
ao mesmo tempo em que ela vai cando transparente, com a unio das classeg
Trans1ateAnimationt:A1phaAnimation.

AnimationSet lista = new AnimationSet(true);


Animation a1 = getAnimacaoMoverParaBaioCima();
Animation a2 = getAnimacaoAparecerDesaparecer();
1ista.addAnimation(a1);
1ista.addAnimation(a2);
// Pronto, basta utilizar a animao agora.

Se voc preferir, tambm possvel criar esta lista de animaes no arquivo


XML, no qual as tags de cada animao so agrupadas dentro de uma tag set>.
O exemplo a seguir mostra como criar a animao que move a view para baixo
ao mesmo tempo que faz ela desaparecer.

/res/anim/set_mover_para_baixo_desapareter.xmI
<?m1 version="1.0" encoding="utf-8"?>
<set xmins:android="http://schemas.android.com/apk/res/android">
<trans1ate
android:fromXDe1ta="0.0" android:toXDe1ta="0.0"
android:fromYDeita="0.0" android:toYDe1ta="200%"
android:l1After="true" android:duration="2000" />
<a1pha

3dFd=fF0f'1^1Dh6="1~0" dF0d2t0A1pha="0.0" android:duration="2000" />

No proximo arquivo e o contrrio, a view move para cima e volta a aparecer.


Captulo 9 n Animaes 297
fwl /res/an|m/set_mover_para_t|ma_apareter.mI
<?m1 version="1.0" encoding="utf-8"?>
<set xmins:android="http://schemas.android.com/apk/res/android">
<transiate
android:fromXDe1ta="G.0" android:toXDe1ta="0.0"
android:fromYDeita="200%" android:toYDeita="0.0"
android:iiAfter="true" android:duration="2000" />
<a1pha

android:fromAipha="0.0" android:toA1pha="1.0" android:duration="2000" />

Agora podemos carregar o AnimationSet utilizando estes recursos de animao:


AnimationSet setl = (AnimationSet) AnimationUtiis.1oadAnimation(this,R.anim.set_mover_
para_baixo_desaparecer);
AnimationSet set2 = (AnimationSet) Animationtiis.ioadAnimation(this, R.anim.set_mover_
para_cima_aparecer);
AnimationSet set = ag ? setl : set2;
// Pronto, basta utilizar a animao agora.

Lembre-se de que as animaes adicionadas no AnimationSet sero agrupadas em


uma nica animao. Os efeitos e transformaes de cada animao vo acontecer
tudo ao mesmo tempo, o que pode resultar em diferentes animaes. interes
sante que voc crie alguns exemplos para se acostumar com o comportamento
das animaes. A gura 9.5 exibe o resultado da animao. Na parte direita da
gura, podemos Ver a imagem se movendo para baixo e quase invisvel.

J za `*; . 1 "L;*r'
ia*
l . .>_zze_.=._x>._,,__ ..\_: _:.~..

Wi : 1. - ;~'* 1 i
, ,_
l

1 z A i_i. iriir.. .i .ir i ......~a---~ -s ~ ~~ W


Animar com XML l i Animar com API ; I p Animar com XML Ammar com API

rm
u. 5

Figura 9.5 - Exemplo de AnirmztionSct.


298 Google Android - 4= ed
9.9 Animationlistener
Se quiser monitorar o inciod enima o basta
trmino usaraa
e uma interf
, ace
android . view . animation _ Animation .AnimationListener.

Isso feito chamando o mtodo setAnimationListener(AnimationListener) da Classe


Animation, conforme demonstrado a seguir:
Animation anim = N;
anim.setAnimationListener(new AnimationListener() {
@0verride
public void onAnimationStart(Animation animation) {
// A animao foi iniciada
}

@0verride
public void onAnimationEnd(Animation animation) {
// A animao terminou
}

@0verride
public void onAnimationRepeat(Animation animation) {
// A animao est repetindo
}

});

9.10 Interpolator
Imagine que voc possui uma animao que vai mover o objeto da esquerda para
direita com o tempo de durao de dez segundos. De certa forma, podemos dizer
que, se dividirmos a distncia que ser percorrida pelo objeto por 10, o objeto vai
a cada segundo mover 10% da distncia. Se a distncia total a ser percorrida lior
100px, o objeto ser movido 1Opx por segundo. Esse o esperado e o compor
tamento padro, mas possvel alterar a acelerao dessa animao e customizar
seu comportamento.

d. . z . ` _ '
Quem dee esta taxa "rate" da animao a interface Interpolator. Como padr0~

Se nnhum interpolator for especificado, ser utilizada a classe LinearInterpolf~


qfle 22 fom q_ O efelw 5313 JUStamente o comentado anteriormente, e a anima'

em um carro l ' - . , .
ao sem COUSISWUYC C ter O mesmo efeito durante todo o tempo Agora vws
12f que 0 0bJet1v0 Seja acelerar o objeto que est se movendo Se pensarm05
E C Cma dfagaf ate ganhar aceleraao, e depois que embala \~
embora. Mas lembre-se: muita calma nessa hora Podemos criar o mesmo efew
CaptuIo9 n Animaes 299

com a animao de um objeto e aceler-la aos poucos. Usamos para isso a classe
Acceieratelnterpoiator. Para informar qual interpolator deve ser selecionado, chame
o mtodo setInterpol.ator(nterpolator) da classe Animation;
Anzimation anim = ;
ani.m.setInterpo1ator(new Acce1erateInterpolator());

E para denir o interpoiator utilizando o XML, utilize o atributo androidlrinterpoia-tor:


<?m1 verson="1.0" encoding:"utf-8"?>
<transIate , . and1rroti:inzte,rpo,1_ator="@android:anim/acceIerate_int_erpolator" />.

Existem diversas classes que implementam a interface Interpolator. Fica de exer


ccio para voc brincar com elas para testar a diferena que cada uma faz nos
efeitos de cada animao Na listaa seguir, podemos verificar a explicao das
principais classes..
Mt0d0 Descrio
AccelerateDe.cel;eratelnterpoliator A animao comea rpido. e termina devagar, mas
bem no meio d uma acelerada.
Accelerastelnterpolator A animao. comea devagar e vai acelerando.
An.tic~ipat;eIinter polator A animao- comea para trs e depois vai animando
para frente. Esse inzterpoiator d a impresso de que
oz objetovai para trs para pegar um embalo antes
de acelerar;
Antiicipateve rsh.ootl'nte rpolator Idem animao Anticipatelnterpolator, mas depois
de pegar oembalo ela se empolga e acaba passando
do alvo, de forma que tem de voltar um POUCO
BounceI'nterpoia.tor E1sse.interpo`i.a;to.r faz. com que o objeto d uns puli
nhos ao atingir o nal d_a animao, como se uma
bo-la estivesse quicando no cho antes de parar.
Cyclelnterpoiator Faz: com que a_ animao seja repetida no final por
um nmero "n" de vezes.
Deceleratelnterpolator A animao comea rpido e vai desacelerando.
Linearlnterpolator o nterpolator padro e faz com que O efeito da
animao seja constante.
Overshootlnterpolator A animao se empolga e acaba passando do alvo,
de forma que tem de voltar um p0uC0

9.11 0 problema com a API de animaes no Android 2.x


No Android 2.x a API de animao conhecida como View Animation, C Him QI"
P ossvel criar vrios efeitos especiais conforme acabamos de estudar.
300 Google Android - 41 emo
Mas esse framework de animao tem um problema classico QUC vou explicar
agora. O framework da lew Animation altera apenas a aparncia de uma vievt; mas
no o seu contedo interno, ou seja, o estado do objeto. Por exemplo, se ZrmO
uma animao para mover um boto para baixo, o esperado que o evento seja
disparado ao clicar na nova posio do boto. Mas no isso o que acontece, pgjs
o boto apenas foi desenhado em outro local, mas como se ele ainda estivegsc
l no local original. O usurio teria de continuar clicando no local de origem do
boto para disparar o evento.
No projeto de exemplo deste captulo, voc vai encontrar um exemplo que dg.
monstra essa situao, conforme a gura 9.6. A primeira parte da gura mostra 0
boto antes da animao e a segunda parte mostra o boto l em baixo, pois ele
se moveu. Conforme j expliquei, voc ter de clicar na rea denida pelo crculo,
mesmo depois de o boto estar em outra posio.
vn

6- Anim

lmf i
.` I
\
\
i/
j nlmf

aNota:nV'' ' '' ' ,- -,. ,


Figura 9.6 - Animao de movimento.

IGN Animation e o framework de animaao do Android 2.x, o qual altera


pe as a maneira como a view e desenhada, mas mantem as propriedades da
view inalteradas, o que pode causar alguns problemas

9.12 Property Animations

robusto framework de a ` ' - .


No Android 3.0 (Honeycomb) foi criada a API p
roperty Animation, que consiste em Um
mmae5~ que POCC animar e alterar qualquer propriedadf
Captulo 9 I Animaes 301
de um objeto, sobre uma linha de tempo. Essa nova API de animao vem comple
mentar a API de animao do Android 2.x, conhecida como iew Animation.
Um dos objetivos desse novo framework solucionar o problema que vimos an
terirrnente, em que a animao no alterava as propriedades internas do objeto,
apenas mudava a forma como o mesmo era desenhado. Com a Property Animation,
o conceito de animao baseado nas propriedades do objeto que podem ser
alteradas, como por exemplo a propriedade alpha e as posies x e y. Em vez de
criar classes de.objetos como A1phaAnimaton e TransIateAnmaton, podemos executar
um cdigo que vai alterar determinadas propriedades sobre uma linha do tempo.
Para criar o A1phaAnmaton, basta alterar a propriedade alpha que controla a trans
parncia, e para mover a view basta alterar as propriedades x e y.
O interessante desse framework que voc pode animar uma srie de proprie
dades, conforme vamos aprender agora.

9.13 Classe VaIueAnimator

A primeira classe que vamos estudar no novo framework a Va1ueAnmator, que


consiste em criar uma animao genrica e utilizar um istener para ouvir os
resultados durante a animao.
Nos prximos exemplos, usaremos o mesmo layout com a gura do Android dos
tpicos anteriores. Esse layout dene a imagem que ser animada, e dois botes
para criar a animao utilizando um arquivo em XML ou a API.
Para comear a brincadeira, veja o seguinte exemplo genrico de anmator:

/res/anim/animator_1_para_0.xmI
<aninator xmlns : androd="http : //schemas . android . com/apk/res/android"
android:duration="1000"
android:va1ueFrom="1" android:va1ueTo="0"
android:va1ueType="oatType" android:repeatCount="1" android:repeatMode="reverse" />

_ . . . ` ` ` ilar
Verificamos que essa animao dene que o valor inicial 1 e o nal O, mas no
dene qual a propriedade ou tipo da animaao que sera real1zado.|Isso acontece
porque o conceito de animaes nesse novo framework e generico. Podemos
informar o valor inicial e nal de forma abstrata, para posteriormente vinci
esses valores com alguma propriedd f211~
Uma caracterstica importante que podemos vericar nesse recurso de animao

esse 1interva
l lhido assar deo,
1 a Oo(note
vaqueor esco P'
que o tempo da animao foi denido como um segundo, e, portanto, durante
os nmeros so float)
302 AIIO - 43
~ ,- -. -. mos recu erar es - t
. _ . - ~ ~ 5 umte forma:
Uma vez que o recurso de animaao esteja CI'l1Cl0 Pode _ P Sa &m~
mao e instanciar um objeto do tipo ValueAmnator da seg

. _ _ _ - irias ri* .
V1u@AninatQr 3 z (va1ueAninator) Aninatorlnater.loadAninator(\h15
R.anim. aninator_1_paf3_9);

Essa configuraao de animaao informa o tempo e os valores, ao dent


quais as propriedades que precisam ser alteradas. Portant0 Para aPlCar a anima
o devemos criar um listener para car ouvindo o seu andament0,ue~ duramf
determinado tempo vai nos enviar numeros de 1 ate Tal listener po e receber os
valores e aplica-los em algum objeto durante aquele intervalo de tempot Conforme
demonstrado a seguir:
nal Inageview img = ...
V1ueAn1nr 3 z (ValueAninator) Aninatorlnater.loadAninator(th1s,
R.anin.aninator_1_para_9);
a.addUpdateListener(new ValueAninator.AnimatorUpdateListener() {
public void onAninationUtdate(ValueAninator animation) {
// Fica ouvindo os valores durante a animao
Float valor = (Float) anination.9etAniI'IatedV1U@();
// Altera o alpha
ing.setAlpha(valor);
}

});

Note que no cdigo o mtodo onAninationUpdate(anination) ser chamado vrias


vezes durante o tempo que a animao estiver executando. Chamando o mtodo
getAninatedValue(), recuperamos os valores que vo variar de 1.0 a O_O. Feito issu
podemos fazer qualquer ooisa, neste caso, estamos manualmente alterando
propriedade alpha da view o que vai criar uma animao do tipo fade_out, ou seja.
a view vai ficar transparente at desaparecer.
Como PUCCIUOS verificar, 0 novo framework de animao consiste em definir
um conjunto de valores que sero aplicados durante determinado intervalo dt
tempo, mas cabe ao desenvolvedor utilizar um listener para receber esses valore
e fazer a atualizao neceria em seu objeto
A ideia legal, mas na pratica acaba sendo trabalhosa demais Mas espere: 655
conceito que est por de baixo dos panos do framework, mas no dia a dia vamu
trabalhar com a classe 0bjectAninator, que vzii facilita; nossa vda_
Captulo 9 u Animaes 303
9.14 Classe 0bjectAnimator
Conforme vimos no exemplo anterior, tivemos um pouco de trabalho, pois foi
necessrio alterar a propriedade alpha da view manualmente.
Para facilitar a vida do desenvolvedor, foi criada a classe ObjectAnmator, que uma
subclasse de ValueAnmator, portanto faz tudo que ela faz, mas altera automatica
mente determinada propriedade. Com a classe 0bjectAnmator, criar uma animao
do tipo fade__out, variando o alpha de 1 para O, simples assim:
Imagevew img = m
ObjectAimator a = 0bjeCtAnimat0r.0fFl0at(img, "alpha", 1f, Gf);
anim.setDuraton(2G00); i
a.start();
O mtodo est criando uma congurao de animao que vai iniciar em 1 e
terminar em O, durante dois segundos. Desta vez, porm, no precisamos criar
nenhum listener, pois a classe 0bjectAnmator recebe como parmetro a propriedade
que deve ser alterada durante a animao, que nesse caso a alpha. Internamente
essa classe faz o mesmo processo manual que zemos no exemplo anterior.
Existem vrias propriedades que podemos alterar, outro exemplo seria mover a
view 100 pixels para a direita, conforme demonstrado a seguir:
Imageview img = N
0bjectAnmator a = 0bjectAnimat0r.0fFloat(img, "x", img.getX(), 100);
anm. setDuration(1l)G0);
a.start();
Note que anteriormente tnhamos as classes AlphaAnmati.on e TranslateAnmaton, para
implementar as mesmas coisas. Mas agora uma nica classe chamada 0bjectAnmator
capaz de alterar qualquer propriedade do objeto, e ainda manter o estado do
objeto, o que era um bug no framework antigo.
Para a classe ObjectAnimator conseguir alterar 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:
0bjectAnimator a = ObjectAnimator.ofFloat(mg, "alpha", lf, GF);

Significa que os mtodos getAlpha() e setAlpha(oat) existem na classe View. Note que
o mtodo offFloat indica que o tipo do parmetro oat. Seguindo o mesmo prin
cpio, sabemos que podemos alterar a propriedade , porque existem os mtodo
304 Google Android - 4 @d
.~'"md'z
getX() e set(oat). dessa maneira que a classe 0bCt^"3tf funciona, podendo
alterar qualquer propriedade de um objeto durante a HDIITIHGO 6 Ctrminzdo
intervalo de tempo. um conceito bem flexvel.

Nota: para criar as anima _ _ d _


cs estamos usando o mtodo 0bjectAnmator.ofFloat,
porque os parmetros que estamos tentando manipular sao o tipo float, mas
tambm existem os mtodos 0bjectAnmator.ofInt e 0bjectAnu'iator.of0bJect, qug
permitem manipular outros tipos.

A lista a seguir exibe as propriedades mais comuns que podemos alterar com
essa classe, para obter as tradicionais animaes de alpha, rotate, scale, translate_

alpha

Transparncia da view que pode variar entre O e 1.

xey
Coordenadas com a posio da view

translatonX e translatonY

Essas propriedades representam um deslocamento segundo as coordena


das x e y Por exemplo, se uma view tiver a propriedade translation=-50, ela
ser deslocada 50 pixels para a esquerda. Isso til para colocar layouts
fora da tela, e depois anim-los para dentro.

rotaton, rotatlonX e rotatonY

Propriedades sobre a rotao da view em graus.


scaleX e scaleY

Propriedades para controlar a escala para redimensionar uma view

pvotX e plvotv

Propriedades que definem 0 eixo utilizado nas animaes de rota0 C


escala, em que o padro 0 centro da view
Agora que C0nhC<'3m05 3 Cl3SS 0bjectAnmator, vamos refazer a maioria dos exm'
Pl05 que @5U1dafl105 are 0 m0mf0 Para voc verificar a simplicidade dC**
framework de animao.
Captulo 9 I Animaes 305
Nota: o framework Property Animations funciona somente no Android 3.0 (I-Ioneycomb)
ou superior. Se voc deseja utilizar as facilidades desse framework em verses antigas
do Android, procure pela biblioteca Nine0ldAndroids (http://nineoldandroids.com/). Eu
particularmente utilizo essa biblioteca em meus aplicativos.

9.15 0bjectAnimator - animao fade_in/fade_out


Neste prximo exemplo, vamos implementar os famosos efeitos de fade_in e fade_out,
com o objetivo de alterar a transparncia da view

AIphaAnim.java
public class AlphaAnim extends Activity {
private boolean visivel = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.eemplo_animacao);
}

public void onClickAnimarXML(View view) {


Imageview img = (ImageView) ndViewById(R.id.img);
0bjectAnimator a = (0bjectAnimator) Animatorlnater.loadAnimator(this, R.anim.fade_out);
a.setTarget(img);
animar(a);
}

public void onClickAnimarAPI(View view) {


Imageview img = (ImageView) ndViewById(R.id.im9); .
0bjectAnimator a = 0bjectAnimator.ofFloat(img, "alpha", 1f, Gf);
animar(a);
}

private void animar(0bjectAnimator anim) {


anim.setDuration(2000);
if(visivel) {
anim.start();
} else {
// Apenas reverte a animao
anim.reverse();

// Inverte o ag para na prxima vez utilizar a animao inversa


visivel = lvisivel;
}

}
306 Google Android -vmjm
criar a animao
Esse exemplo utiliza a classe 0bjectAntnator parapela
' . API r
v 'Dor
XML. Para criar o mesmo cfeito em XML, l121SlH Cflf Um arquivo QIUC Cmec
com a tag c configurar as prpriCd8d5
i /res/anim/fade_out.mI
<objectAninator xmlns:androld="http://schenas.androd.com/apk/FGS/BHTO"
android:propertyName="alpha"
android:valueFrom="1" android:valueTo="G"
android:duration="1000" android:valueType="oatType" />

Ao executar esse exemplo, o resultado ser a animao de fade_ln e fade_out, como


o esperado. A classe 0bjectAninator facilita bastante a criao da animao. Uma
das funcionalidades mais interessantes 0 mtodo reverse() disponivel na classe
Animator, que faz com que uma animao execute no sentido inverso, desfazendo
-se. 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 reverse() e a animao executada no sentido contrrio. Compare essa
implementao com a anterior e veja a diferena.

Nota: o mtodo reverse() reverte a animao tornando o cdigo bem mais simples.
Nos casos em que voc precise programar vrias animaes em conjunto (ex:
alpha + translate + rotate) o mtodo reverse() realmente vale a pena, pois economiza
vrias linhas de cdigo.

9.16 Objectlinimator - animao de movimento


Para mostrar como o framework Property Animation simples, vamos refazer os
outros exemplos de animaes que j demonstramos neste captulo.
Q seguinte codigo mostra como mover a view para baixo, algo que no framework
View Animation seria feito com a classe TranslateAnnatton
Inageview img = (lnageview) ndVewById(R.d.lng);
// Pela API
0bjectAninator a = 0bjectAninator.ofFloat(tng, "y", 50 200) I/ De y 56 at 190
// Por XML

g::f"""atr 3 = (0bCt^""3tf) Anlnatorlnater.loadAnlnator(thls,R.an'tn.nover_

Neste exemplo, estamos movendo a view para baixo, alterando a propriedade jz


Captu|o9 :Animaes 307
/res/anim/mover_baxo.x'm|
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android
andr0id:propertyName=y"
android:va1ueFrom="50" android:va1ueTo="200"
android:duration="1000" android:vaiueType="oatType" />

NQR: execute o projeto. PropertyAnimat_ion disponvel nos exemplos deste captulo


para conferir o resultado das animaes.

9.17 0biectAnimartor - animao de rotao


O cdigo a seguir mostra como girar a view em um ngulo de 36O, algo que no
framework Wew Animation ser~ia feito com a classe RotateAnimation.

Imagevieu img = (lmageview), ndViewById(R.id.img);


// Pelil API
ObjectAni_mator a =- Objectmimator.QfF1oat(img, "rotation", 0, 360);
/'/' Por XML
Obje-tAninat_ot a = (0bj_ectA_nimat_or) Animatorlnflater._ioadAnimator(this, R.anim.rotate_360);

No caso do arquivo XML, ele pode s_er criado desta forma:

/res/anim/rotate_360.xmi
<objectAnimator xmlns :-android= "httpz / / schemas ,android . com/ pk/ res / android"
android:propertyNamQ="FOQOD"
android:-vaiueFrom="Q" andr0id:_\a1_ueTo="360"
android:dura_tion="1000" android:_va1ueType="oatType" />

Nota: no framework View Animation, vimos que o cdigo necessrio para animar
uma view muitas vezes era extenso e justamente por isso era recomendado
criar a animao em XML para facilitar a leitura do cdigo. Com o framework
Property Animation o cdigo se tornou extremamente amigvel, e podemos criar
animaes com apenas. uma linha de cdigo., Portanto, ca a seu critrio utilizar
API ou XML.
308 Google Android - 4 um
9.18 Objectllnimator - animao de estala
Ate o momento. tralialliamos corn a classe Objectlminator para alterar uma nica
propriedade de um olijcto para criar a a niniaczio No entanto, as vezes, dependencia
do caso. precisamos alterar mais de uma propricdlslt
Neste pixiino exemplo precisamos alterar duas propriedades do objeto. scalex Q
scaleY Pois P\`cisamos criar o efeito de diminuir o Olllfm "l CIC d*`5ll@f0Cer. e
depois aumenta-lo novamente ate o tamanho normal. Para alterar mais de uma
pnopriedade. e necessario criar um PropertyValuesHolder com as informaes de que
precisamos Essa classe apenas tem o objetivo de armazenar a informaao Salim
uma propriedade para ser animada.
PropertyValuesHolder aninl = Propertyvaluesiiolder.ofFloat("scalex", 1, G);
PropertyvaluesHolder anin2 = PropertyValuesHolder.ofFloat("scaleY, 1, B);

Depois de criar essas duas configuraes, podemos agrupa-las c criar um


0bjectAninator utilizando o metodo ofPropertyValuesHolder(objeto, valores. . .). no qual
podemos informar varias instncias de PropertyValuesHolder.
0bjectAninator a = 0bjectAninator.ofPropertyValuesHolder(ing, antnl, ani.n2);

Feito isso, o objeto 0bjectAninator vai executar as duas animaes simultneas para
criar o efeito de escala tanto no eixo x quanto y do objeto.

9.19 AnimatorSet - criando um conjunto de animaes


A classe AnnatorSet pode ser til para criar um conjunto de animaes que podem
ser executadas em sequncia ou ao mesmo tempo. A seguir, podemos visualizar
um trecho de cdigo que mostra como utilizar a classe AninatorSet:
Inagevlew img = (lnageview) ndViewById(R.id.ing);
I/ Cria as animaes
0bjectAnlnator alphalinin = 0bjeetAnlnator.ofFloat(ing, 1ph', lf, af).
0bjectAninator translateAnin = objetgnator`f1t(n9. y-, y' g_9etHeght()p
/I Insere na lista
AnlnatorSet lista = new AnlnatorSet();
lista.playTogether(translateAnin, alphaAnln);
I/ Dispara a animao
lista.setDuratlon(200G);
llsta.start();

A tnat rSet ll t A ' _


Caso voce prefira utilizar o XM L. basta inar a lista de aniinaes desta tornia:
Inageview lag = (lnageview) ndvt@w3yd(R_d_ng)
n 0 s a = ( ntnatorSet) Aninatorlnater.loadAninator(this,R.antn.antnator,Sfl~
Captulo 9 n Animaes 309
/res/anim/animator_set.xmI
<?ml version="1.G" encoding="utf-8"?>

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueTo="200" android:valueType="oatType"
android:propertyName="x" android:repeatCount="1" /
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:valueTo=400" android:valueType="oatType"
android:propertyName="y" android:repeatCount="1" />

Nota: recomendo executar o projeto de exemplo deste captulo para voc


visualizar as animaes. Os exemplos do projeto so coerentes com os ttulos
dostpkosdolhwo.

9.20 Animatorlistener
Com o framework Property Animation tambm podemos monitorar a execuo das
animaes, basta implementar a interface and roid . animation .Animator.AnimatorListener:
Animator anim = m;
anim.addListener(new Animator.AnimatorListener() {
@0verride
public void onAnimationStart(Animator animation) { }
@0verride
public void onAnimationEnd(Animator animation) { }
@0verride
public void onAnimationCancel(Animator animation) { }
@0verride
public void onAnimationRepeat(Animator animation) { }
});

Acredito que os mtodos dessa interface sejam autoexplicativos. Uma vantagem


dessa API que, se voc quiser, podemos usar a classe AnimatorListenerAdapter que
j implementa a interface AnimatorListener e deixa os mtodos vazios, de modo que
precisaremos apenas sobrescrever o mtodo desejado.
Animator anim = N;
anim.addListener(new AnimatorListenerAdapter() {
31 O Google Android-4*q1
@0verride
public void onAnimationEnd(Animator animation) {
// m da animao
}

}>;

Nota: a interface Animatoriistener do novo framework de animao do Android 3.x


e muita parecida com a interface AnimationListener que estudamos anteriormente
para o Android 2.x. Nessa nova interface, porm, os metodos recebem como
parmetro um objeto do tipo Animator e no um Animation.

9.21 ViewPropertyAnimator - animao do jeito fcil


Para o final do captulo deixei a cereja do bolo, pois a classe VieuPtooertyAninator
permite criar animaes com uma linguagem simples e alterando vrias proprie
dades ao mesmo tempo.
Se quisermos mover as propriedades x e y de uma view ao mesmo tempo, pode
ramos utilizar um cdigo assim:
0bjectAnimator animX = 0bjectAnimator.ofF1oat(myView, ", 56f);
0bjectAnimator animY = 0bjectAnimator.ofF1oat(myView, "y", 1G6f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.piayTogether(animX, animY);
animSetXY.start();

Com a nova sintaxe, basta uma linha de cdigo e a animao est feita:
myView.animate() .x(S9f) .y(166f) .a1pha(0);

O mtodo animate() da classe View retorna um objeto do tipo Viewropertyhninator


que contem vrios mtodos de convenincia para con figurar as propriedades dd
animao. Esse tipo de sintaxe de programao chamada de interface fluente
(fluent interface). pois em uma nica linha de cdigo e com uma linguagfm
natural possivel configurar varias propriedades do objeto de forma aninhada.

ma* 3 Cl55 V"P"P@f2y^imator somente est disponvel no Android 3.1 (AW


Level L), mas caso voc precise utilizar o framework Property Animation
em versoes anteriores do And 'd . procure pela biblioteca NineOldAndroid5
YUI
(hrtp://riinmldundroids.rm/).
Captulo 9 I Animaes 311
9.22 Classe ValueAnimator - outro exemplo
Como j expliquei anteriormente, a classe ValueAnimator a base do novo framework,
mas com ela temos o trabalho manual de obter os dados e atualizar as propriedades
na view A classe 0bjectAninator foi criada para facilitar esse trabalho. Se precisar,
releia as explicaes anteriores sobre essas classes.
s vezes, no entanto, o que voc quer justamente obter todos os valores num
ricos durantel um intervalo de tempo. Para exemplicar, o Google Fit utiliza o
pedmetro (sensor de passos) e mostra uma animao assim que a tela aberta.
Nessa animao a quantidade de passos atualizada de forma sequencial e ani
mada. Ento se voc deu 1.000 passos e fechou o aplicativo, e depois deu mais 500
passos e abriu o aplicativo, o Google Fit vai mostrar uma animao para atualizar
o texto do Textview de 1.000 at 1.500.

Como podemos fazer esse tipo de animao? A classe ValueAnimator pode ajudar,
basta denir o intervalo numrico que voc deseja e que o framework envie as
atualizaes e o tempo da animao. O seguinte trecho de cdigo mostra como atu
alizar o texto de um Textview do valor 0 para 100 de forma incremental e animada,
durante o intervalo de um segundo. Voc poder ver os nmeros incrementando
at chegar no valor nal.
nal TetView textview = ?
// Animaco genrica de 1 at 100
ValueAnimator a = ValueAnimator.ofFloat(1, 100);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAninationUpdate(ValueAnimator animation) {
// Recebe o valor e atualiza o texto.
Float valor = (Float) animation.getAninatedValue();
tetView.setText(String.value0f(valor.intValue()));
}
});
a.setDuration(1000);
a.setTarget(tetView);
a.start();

9.23 Aplicando animaes no layout


Outro recurso interessante aplicar uma animao que foi definida na pasta
/res/anim no layout da tela. A animao precisa ser do tipo LayoutAnimationController
e pode ser definida em XML na pasta /f6S/d11im
312 Google Android - 4 edio
.. ~~ --~.~' ` lo
CSSIVQYH
a se uir
A classe LayoutAnmatonController serve para animar um gerenciador de layout V1ewGr0u
durante a sua criaao, de forma que a animaao seja aplicada suc 1 enr gm
todas as suas views filhas, durante a criaao do layout. O exemp 8 Tl0Stra
como animar o layout da tela. Note que 0 arquivo comea com a tag <1BY0U'C^ination>
e utiliza uma animao j existente para ser aplicada. Escolhemos a animao
/res/anim/fade_in.xml para que cada view aparea na tela aos poucos, Uma apos a outra,

/res/anim/animatao_Iayout_fade_n.xml
<?m1 verson="1.0" encoding="utf-8"?>
<layoutAnnaton xnlns:androd="http://schenas.androd.com/apk/res/androtd
android:annaton="@anm/fade_n"
android:duraton="50" />

Depois de criar a animao de layout, basta utiliz-la em qualquer layout criado


na pasta /res/layout, conforme demonstrado a seguir:
<LnearLayout . . . android:1ayoutAnmaton="@anm/annacao_1ayout_fade_in" >

Outro recurso interessante de animao suportado pelo Android so as animaes


que executam quando o layout de algum gerenciador de container alterado,
como, por exemplo, uma view ser adicionada ou removida dinamicamente.
Por padro, o Android no faz esse tipo de animao, mas para habilit-la basta confi
gurar o atributo androd : annateLayoutChanges para true, conforme demonstrado a seguir:
<LnearLayout androd:anmateLayoutChanges="true" />

Pronto, agora como um passe de mgica sempre que voc adicionar ou remover
uma view desse layout o Android vai animar essa transio.

Nota: para conferir os exemplos de animao de layout, execute 0 projeto


LayoutAnnatons disponvel nos exemplos deste captulo no Android Studio.

9.24 Aplicando animaes nos fragments


DeP0'5 de estudar 35 animaes, ea fcil aplic-las em qualquer lugar do sistem
Como fragments sao muito utilizados para adicionar, substituir e remover compo
nentes do laY0Ut POCCITIOS aplicar uma animao ao executar a FragmentTransact~
Codlgo-fonte a seguir mostra como substituir um fragment no layout de form
animada, utilizando as animaes de f ade_n e fade_out que controlam a tran5P*"
rncia das views.
Captulo 9 I Animaes 313
android.support.v4.app.FragnentManager fm = getSupportFragnentManager();
FragmentTransaction ft = fn.beginTransaction();
ft.setCustonAninations(android.R.anin.fade_in,android.R.anin.fade_out);
Fragmentl fragl = new Fragment1();
ft.repiace(R.id.1ayoutFrag, frag1,"Fragment1");
ft.commit();

O mtodo setCustomAnimations(int animEnter,int animEit) utilizado para especicar


a animao de entrada do novo fragment, e a animao de sada do fragment
atual, em caso de substituio. Neste exemplo utilizei a classe android.R nativa, mas
se voc quiser possvel criar suas prprias animaes.
Para testar esse cdigo, abra o projeto Fragments-ActionBarTabs no Android Studio.
Esse o mesmo exemplo que zemos com fragments + tabs no captulo 8, sobre
fragments. Porm, ao clicar em uma tab, estou substituindo o fragment utilizando
uma animao, conra!

9.25 Aplicando animaes ao navegar entre activities


A partir do Android 4.1 Gelly Bean), podemos customizar a animao de navegao
entre as activities. Para isso foi criada a classe Activityptions com trs mtodos:

makeCustomAnimation(Context context, int enterResId, int exitResId)

Congura uma animao customizada para a activity que est entrando


e saindo.

makeScaieUpAnimation(View source, int startX, int startY, int width, int height)
Congura uma animao customizada de escala para a activity que vai
ser chamada. As coordenadas startX e startY so as posies para iniciar a
animao, referentes view Os parmetros width e heigth denem o tama
nho inicial da nova activity

makeThumbnaiiSca1eUpAnimation(View source, Bitnap thumbnail, int startX, int startY)

Congura uma animao customizada de escala para a activity mas o ob


jeto que d origem animao uma imagem de Bitnap, e as coordenadas
startX e startY so as posies para iniciar a animao, referentes ao bitmap.
Para brincarmos com as animaes de transio entre telas, crie um novo projeto
chamado HeIIoActivityTransition. No layout da activity; vamos adicionar a imagem do
planeta Terra posicionada no centro. Deixei o tamanho da gura com 1@0dp.
314 Google Android - 4 edio
:Zn /res/layout/activity_main.xmI
<FrameLayout xmlns:android="http://schemas.android.com/apk/FGS/3"dfd"
android:layout_width="match_parent" android:layout_height="tCh_DF">
<InageView android:id="@+d/WQ" "dfd=1Y0U_9fVtY="C@tf"
android:src="@drawable/planeta_03_terra"
android:layout_width="100dp" android:layout_height="10Gdp"
android:onClick="onClickPlaneta" />

A ideia que, ao clicar na gura do planeta Terra, a activity PlanetaActivity seja


chamada com uma animao de transio. A segunda activity contm 0 mesmo
layout, porm a foto do planeta est maior. Para fazer a transio, em vez de cha
mar o mtodo startActivity(intent) como de costume, podemos utilizar a classe
ActivityOptionsConpat ea 1I(()NkCUSt0At0(COHX,D8C3OEtF8dId,BDNBCB
oSaidaId) conforme demonstrado a seguir.

) MainActivity.java
public class MainActivity extends AppConpatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onClickPlaneta(View view) {


Intent intent = new Intent(getBaseContet(), PlanetaActivity.class);
Activity0ptionsConpat opts =
Activity0ptionsConpat.nakeCustonAnination(this, R.anin.fade_in, R.anin.fade_outM
ActivityConpat.startActivity(this, intent, opts.toBundle());
l
}

No cdigo foi denida a anima fade_in (aparecer) para a activity que vai abrir
e fade_out (desaparecer) para a activity que vai fechar, portanto crie os seguintes
arquivos de animao na pasta /res/anim.

i /res/anim/fade_in.xmI
<?m1 verSi0n="1.0" encoding="utf-8"?
<alpha xmlns:android="http://schenas.android.com/apk/res/androdn
android:fronAlpha="0.0" android:toAlpha="1_@" ndr0dduratn:190e />
Captulo 9 I Animaes 315
/res/anim/fade_out.xmI
<?xml version="1.0" encoding="utf-8"?>
<alpha mlns:android="http://schemas.android.con/apk/res/android"
android:fronAlpha="1.0" android:toAlpha="0.0" android:duration="1000" />

Na segunda activity vamos mostrar a mesma gura do planeta, sendo assim, voc
pode at copiar o layout anterior. A diferena que na primeira activity deixei a
guzra pequena com 100dp e na segunda deixei a figura grande ocupando o tamanho
que ela tem com a notao wrap_content. Na segunda activity o detalhe importante
que sobrescrevemos o mtodo finish(), pois preciso customizar a animao de
sada chamando o mtodo overridePendingTransition(animEntrada, animSaida).

PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}

@0verride
public void nish() {
super.nish();
// Customiza a animao ao fechar a activity
overridePendingTransition(R.anim.fade_in, R.anin.fade_out);
}

A gura 9.7 mostra o resultado ao executar esse exemplo. Ao clicar na foto do


planeta Terra, a gura vai sumir e aparecer devido s animaes de transparncia:
fade_in e fade_out. Esse efeito muito interessante e amigvel aos olhos do usurio.
Se voc quiser, possvel criar outros tipos de animaes. E bem comum encontrar
aplicativos que utilizam o efeito que desliza a tela da esquerda para a direita. Isso
pode ser feito com os seguintes arquivos de animao.

/res/anim/sIide_in_Ie1't.xmI
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="50@" android:fromXDelta="1@0%p" android:toXDelta="0" />
316 9
0 't O . O oca: 9 4 ' 0 On'
` " ' aaa in t nouonfi* N A N N ,____
HIoAcuvityTnnaition
Google Android - 4 @d

Figura 9.7 - Animao de transio com transparncia.

/res/anim/slide_out_Ieft.mI
<trans1ate n1ns:android="http://schenas.android.com/apk/res/android"
android:duration="500" android:fromXDe1ta="0" android:toXDe1ta="-5G%p" />

No cdigo da MainActivity, basta utilizar esses arquivos de animao para fazer a


transio de telas com um efeito deslizante.
public void onCiickP1aneta(View view) {
Intent intent = new Intent(getBaseContet(), P1anetaActivity.c1ass);
ActivityOptionsCompat opts =
Activity0ptionsCompat.nakeCustomAnimation(this, R.anim.s1ide_in_1eft,
R.anim.s1ide_out_1eft);
ActivityCompat.startActivity(this, intent, opts.toBund1e());
i

S tenha ateno, pois se estamos fazendo a animao entrar com o efeito df


deslizar da direita para a esquerda, temos de fazer o contrrio ao pressionar O
boto voltar, ou seja, temos de sair da tela deslizando da esquerda para a direita.
portanto crie os seguintes arquivos de animaes:

ifo /res/anim/slide_in_right.xmI

<trans1ate xnins:android="http://schenas.android.com/apk/res/android"
android:duration="S90" android:fr
WD@lta="-50%p" android:toXDelta="0" /
Captulo 9 I Animaes 317

. I '
/res/anim/sIide_out_rght.xm|
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="S00" android:fromXDelta="0" android:toXDelta="100%p" />

Feito isso, utilize estas animaes na classe PlanetaActivity:


@0verride
public void nish() {
super.nish();
// Para voltar utiliza a animao da esquerda para a direita
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right);
}

A gura 9.8 mostra o resultado da animao. Desta vez, ao clicar na imagem do


planeta Terra, a segunda activity entrou na tela da direita para a esquerda. Ao
clicar no boto voltar, a animao o inverso.

r'~.`,.J

Figura 9.8 -Animao de transio com deslize.

Caso queira conferir outras animaes, criadas com os mtodos


makeScaleUpAnimation(...) e makeThumbnailScaleUpAnir|ation(...) abra o projeto
ActivityAnimation-GoogIeDemo disponvel nos exemplos do livro.

Nota: a classe Activityptions foi criada no Android 4.1, mas para facilitar nossa vida
o Google disponibilizou as classes android.support.v4.app.ActivityCompat e android.support.
v4.app.Activity0ptionsCompat para termos a compatibilidade com verses antigas. Veja
que estamos utilizando as classes de compatibilidade no cdigo. Caso a animao
no possa ser feita nas verses antigas do Android, a activity vai abrir normalmente.
318 Google Android -4Q|||
f - > ` ' 'V ~ ` ` .` fr; I
9.26 Nine0IdAndroids - animaes com compotlbllldidf
l,a vai a dica hnal sobre anunaoes! Neste captulo. estudamos o "l11CW()I'l(
Pf0P0fYAnlmatlons que funciona somente no /\ndroid 3.0 (l'l0f`|CYUml7) U SUPCWUY
e facilita bastante a criao de animaes. li o melhor exemPl0 de md fm 0 que
mostramos no topico VIewPtopertyAnImator, pois com urna umca linha de codigo
possvel animar diversas propriedades de uma view.

- - * ` ~" 1 -' f
myView.anmate().(50f).y(100f).a1pha(0);

Mas como usamos essa sintaxe snnplihcada em vc rsocs amigas do Android.


A resposta e a bilvlioteca Nlne0IdAndrolds (hup://nineldandrids.com/`). Para utiliz-la,
adicione esta dependncia no arquivo app/huildgradlc.

ii /app/buiId.gradIe
dependencies {

Compile 'com.ntneo1dandrods:library:2.4.0'
}

Feito isso, podemos usar as classes de compatibilidade que tm os mesmos no


mes e mtodos das classes nativas, como por exemplo: com.ntneo1dandrotds.veu.
V'tewPropertyAnmator.

Mas o que eu realmente gosto de fazer usar a sintaxe simplificada da


VlewPropertyAnimator. Felizmente, isso muito simples; basta declarar um import
esttico no cdigo da classe da seguinte forma:
import static com.nneoldandroids.view.viewPropertyAnmator.antmate;

O segredo e que esse import esttico importou a funo antmate(vew), e com


ela podemos animar qualquer view utilizando a sintaxe resumida. O seguinte
exemplo mostra como mover uma imagem n eixo x e ) ao mesmo tempo em
que rotacionada ein I8O; no final, ficar com 50% de transparncia, tudo em
uma nica linha de cdigo.

anmate(mg) .By(200) . yBy(200) . rotat1.on(180) .a1pha(0.SF) .setDuration(2000);

A figura 9.9 mostra o resultad do projeto de exemplo NIne0ldAndr0lds disponvel nos


exemplos do livro, qual executa exatamente esse cdigo mostrado anteriormente.
Captulo 9 n Animaes 319

'A
ANIMAR X, Y E ALPHA ANIMR X, Y E ALPHA

QIIII. _ . ._.
i:ri_;____;}..
,- ai

Figura 9.9 - Animao.

9.27 Links teis


Neste captulo, estudamos como criar animaes para turbinar seu aplicativo e
melhorar a experincia do usurio,
Como o assunto de extrema importncia para criar aplicativos fantsticos que
encantem os olhos do Llsuri, separei vrios links da documentao ocial para
complementar seus estudos,
View Animation

http://developer android, com/guide/topics/graphic5/view-animation. html

Property Anlmatlon

http://dei/elopenmdroid,com/guide/topics/graphics/propanimation. html

Animating Layout Chnges

http://developer android.com/tmining/animation/layout. html


Drawable Animation

http;//dgvglopgzgndrgid,mm/gaide/topics/graphics/drawable-animation. html

Android Training - Animations

https://developer ndroid. com/tming/ar2imati0n/indx. html


Google Android - 4 edio

Android Developers Blog - lntroducing ViewPropertyAnimator

maior html
http://android-developers.blogspot.com.br/201 1/05/introducing-viewpropertyang

Nine0IdAndroids - Biblioteca de compatibilidade para animaes

http://nineoldandroids.com/
Vdeo no YouTube - Android DevBytes: Window Animations

https://www youtube. com/watch ?v=H 081/l' 61 l VI U

Vdeo no YouTube - Android DevBytes: Activity Animations

https://wwuzyoutube. com/watch P1/= CPxkoe2MraA


Vdeo no YouTube - Google Developers: Activity Transitions

https://www. youtube. com/watch?v=RhiP] By] M rM


r cAPTuLo 10
Threads, Handler e
*TW AsyncTask

Neste captulo, vamos estudar o conceito de threads e a classe Handler, a qual


utilizada para enviar ou agendar mensagens para serem executadas na thread
principal da aplicao, conhecida como Main Thread ou UI Thread.
A classe Handler tem um papel importante na arquitetura do Android, porque
somente por meio dela possvel atualizar a interface grfica a partir de uma
thread diferente da thread principal.

10.1 Introduo
Quando um aplicativo aberto no Android, um processo dedicado no sistema ope
racional criado para execut~lo. Cada processo tem uma nica thread, conhecida
como Main Thread ou UI Thread, responsvel por gerenciar todos os eventos da
tela, assim como atualizar a interface grca. O fato que muitas vezes a activity
pode realizar uma tarefa um pouco demorada, e para no travar a interface de
usurio recomendado que esse tipo de cdigo seja executado em outra thread.
Podemos dizer que, sempre que uma consulta realizada em um web service,
banco de dados, agenda de contatos ou leitura de um arquivo, obrigatrio criar
uma thread para desvincular esse processamento da thread principal. Antigamente
isso era apenas uma recomendao, mas nas verses mais novas do Android, se
o cdigo zer uma operao de I/O na thread principal, o sistema vai lanar a
exceo Network0nMainThreadEcepton.

Portanto, como regra, toda operao de I/O, seja consultar um web service, ler
um arquivo ou acessar do banco de dados, deve executar em uma thread separada.

321
322 Google I1d|`0(| - 4' QO
Outro motivo importante para utilizar threads porque a thread principal da
aplicao deve responder aos eventos do usurio, em no mximo em cinco segun
dos. Se esse tempo for ultrapassado, o erro AINR (Application Not Responding)
ser lanado. Esse erro a clssica mensagem com um Forteose que aparece em
muitas aplicaes, porque, nesse caso, o Android entende que a aplicao no est
respondendo e exibe esse alerta para o usurio fecha-la ou aguardar. Para evitar
esse tipo de erro, necessrio utilizar threads.
Uma vez que j justicamos a necessidade de utilizar threads, vamos ver um trecho
de cdigo em Java que executa um cdigo em uma nova thread.
new Thread() {
public void run() {
// Cdigo que deve executar en segundo plano aqui
};
}.start();
Uma thread deve ser lha da classe Thread e deve implementar 0 mtodo run(). Ao
chamar o mtodo start(), a thread iniciada, ou seja, 0 mtodo run() vai executar
em segundo plano. Para mais detalhes sobre threads no java, recomendo uma
leitura adicional em livros sobre essa linguagem.
No caso do Android, sempre que uma thread iniciada, temos um problema,
pois por questes de segurana e concorrncia o Android no permite que uma
thread diferente da principal atualize a interface grca da tela. Por isso, a classe
android.os.Handler foi criada com o objetivo de enviar uma mensagem para a thread
principal, para que, em algum momento apropriado, essa mensagem possa ser
processada de forma segura e consequentemente atualizar a interface grca da
tela (view). Um exemplo clssico de utilizao de threads e atualizao de interface
grca com um Handler pode ser visto a seguir:
nal Handler handler = new Handler;
new Thread() {
public void run() {
// Cdigo que deve executar en segundo plano aqui
handler.post(new Runnable() {
public void run() {
// Cdigo que atualiza a interface aqui
}

});
};
}.start();
Captulo 10 1 Threads, Handler e AsyncTask 323
Ou podemos utilizar o mtodo run0nUIThread(runnable), que um atalho para utilizar
um handler que est dentro da activity
new Thread() {
public void run() {
// Cdigo que deve executar em segundo plano aqui
run0nUiThread(new Runnab1e() {
public void run() {
// Cdigo que atualiza a interface/view aqui
}
});
};
}.start();

Essa a utilizao clssica de um handler, que tem como objetivo sincronizar o


cdigo de uma thread para atualizar a interface. No entanto, o handler tambm
muito utilizado para agendar tarefas e enviar mensagens dentro da activity:
Concluindo, podemos dizer que existem dois bons motivos para usar a classe
android.os.Hand1er:

1. Atualizar a interface (view) sempre que uma thread for utilizada para fazer
algum processamento em segundo plano.
2. Agendar uma mensagem android.os.Message ou um java.lang.Runnab1e para
executar em determinado momento. Essa mensagem pode ser enviada
instantaneamente ou com um intervalo de tempo (delay). Cada mensagem
enviada processada em uma la de mensagens nica para cada handler,
que est vinculada thread principal da aplicao.

Resumo: no Android, cada aplicao executada em um nico processo, e


cada processo por sua vez tem uma thread dedicada. Essa thread tambm
responsvel por desenhar e tratar todos os eventos da interface grca, e
conhecida como Main Thread ou UI Thread. Existe uma regra no Android que
diz que somente a Ul Thread pode atualizar a interface, ou seja, somente ela
pode chamar qualquer mtodo que vai atualizar uma view. A classe Handler
utilizada para enviar uma mensagem para ser processada pela Ul Thread, que
geralmente um cdigo que vai atualizar a view.
324 Google Android - 4' edio
10.2 Mtodo sendMessage(msg)
Para enviar uma mensagem com a classe Handler, pdCmS Utllllf 0 mtodo
sendHessage(msg) e suas variantes, conforme demonstrado a seguir:
Mtodo Descrio
sendMessage(msg) Envia a mensagem informada para a la de mensagens para
ser prcessada assim que possvel.
sendEmptyMessage(i.nt) Envia a mensagem contendo apenas o atributo what informa
d como parmetro. a mesma coisa que criar um objeto
androld.os.Hessage e congurar apenas o\atributo what.
sendMessageDelayed(msg, long)
lnvia a mensagem para a la de mensagens, mas ela pr
cessada somente depois de determinado tempo informado.
O segundo argumento do tip long, que representa o tempo
em milissegundos que a mensagem deve aguardar antes de
ser enviada.
sendMessageAtTlme(msg, long)
linvia a mensagem para a la de mensagens, mas ela pro
cessada somente na data informada. O segundo argumento
do tipo long, que representa uma data em milissegundos
para executar a mensagem.
Para brincar com a classe Handler, vamos criar um exemplo para enviar uma men
sagem com o mtodo sendMessageDelayed(msg, delay), o qual vai enviar a mensagem
com atraso (delay) de trs segundos. Para continuar, crie o projeto HelloHandler, ou
se preferir abra o projet de exemplo que acompanha o livro.
A seguir, podems visualizar o cdigo-fonte da activity e seu layout.

fi /res/layout/activity_demo_handIer_message.xmI
<?xml verslon="1.0 encodlng="utf-8"?
<LlnearLayout mlns:androd="http://schemas.androd.com/apk/res/androld"
androld:layout_wldth="match_parent" androld:layout_helght="match_parent"
androld:orlentaton="vertcal" androd:paddlng="20dp"
<TetVew

androd:layout_width=match_parent" androld:layout_helght="wrap_content"
androd:tet="Dlsparar uma mensagem com atraso de 3 segundos" /
<Button
androld:ld="@+ld/btEnvlar"
BHFO2lY0Ut_width="wrap_content" androld:layout_helght=wrap_content"
androld:text="Envlar mensagem" /
</LlnearLayout
Captulo 10 I Threads, Handler e AsyncTask 325
DemoHandlerMessageActivity.java

public class DemoHandlerMessageActivity extends AppCompatActivity {


protected static nal int MENSAGEM_TESTE = 1;
private Handler handler = new TesteHandler();
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_demo_handler_message);
ndViewById(R.id.btEnviar).set0nClickListener(new Button.0nClickListener() {
@0verride
public void onClick(View v) {
// Cria a mensagem com delay de trs segundos
Message msg = new Message();
msg.what = MENSAGEM_TESTE;
// Envia a mensagem
handler.sendMessageDelayed(msg, 3006);
}

});
}

// Handler utilizado para receber a mensagem (classe interna)


private class TesteHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// O atributo msg.what permite identicar a mensagem
switch (msg.what) {
case MENSAGEM_TESTE:

Toast.makeTet(getBaseContet(), "A mensagem chegou!",Toast.LENGTH_SHORT).show();


break;
}

Note que, para utilizar o mtodo sendMessage(msg) ou uma de suas variaes, pre
ciso criar uma subclasse da classe android.os.Handler; justamente por isso, criamos
a classe TesteHandler. Feito isso, para enviar uma mensagem ao handler, foi criado
um objeto do tipo android.os.Message e no atributo what foi informado um valor, que
contm uma constante para identicar a mensagem.
Message msg = new Message();
msg.what = MENSAGEM_TESTE;
handler.sendMessageDelayed(msg, 3000);
326 Google Android - 4 edio
O mtodo sendMessageDe1ayed(msg,de1ayHi11is) recebe a mensagem e o tempo em
milissegundos (delay) para atras-la. Nesse exemplo, depois de trs segundos, 0
mtodo hand1eMessage(message) da classe interna TesteHandler foi chamado. Nesse mo
mento, o valor informado no atributo what utilizado para identicar a mensagem
(isso til caso exista mais de uma), conforme demonstrado a seguir:
public void handleHessage(Message msg) {
// 0 atributo msg.what permite identicar a mensagem
switch (msg.what) {
case MENSAGEM_TESTE: \
Toast.makeTet(Exemp1oHandler.this, "A mensagem chegoul", Toast.LENGTH_SHORT).show();
break;
}

A gura 10.1 mostra o resultado desse cdigo executando no emulador. Ao clicar


no boto Enviar Mensagem, a mensagem disparada, e depois de trs segundos o
alerta exibido.
| 7:51

CJ. l-ltmtlier l~.T*L.s<1

|'"l`iLlf) '
DlSp3I' Uma mf`lSBgl' (Om atraso E 3 V

Em/lar mensagem l
1

Amensagem chegou!

Figura 10.1 - Exemplo inicial da classe Handlei:

Esse exemplo extremamente simples, e no momento voc pode no entender


quais os reais benefcios de utilizar esse tipo de mensagem. Observe que nesse
simples exemplo j podemos vericar que possvel agendar mensagens para
execuo posterior. Isso por si s j parece bastante til. O fato e que nosso exem
plo muito simples para demonstrar o que a classe android.os.Handier pode fazer.
Lembre-se de que as mensagens enviadas para um Handler so processadas pela
UI Fhread, e esse e o verdadeiro sentido de utilizar um handler. No momento,
Captulo 10 I Threads, Handler e AsyncTask 327
vamos continuar com esses exemplos simples para voc entender a sintaxe e como
utilizar um Handler e mais para frente vamos analisar casos em que utilizar um
Handler obrigatrio.

10.3 Mtodo post(runnabIe)


Outra forma de enviar uma mensagem com o mtodo postHessage(runnable), que
funciona de forma similar ao mtodo sendMessage(msg), mas recebe uma implemen
tao da interface java.lang.Runnable. A interface Runnable bem conhecida pelos
programadores Java e utilizada para auxiliar na programao multithreading.
No caso do Android e a classe Handler, a interface Runnable tambm pode ser utilizada
para enviar uma mensagem para a thread principal ou executar determinada tarefa
com um tempo de atraso. A vantagem de utilizar um java.lang.Runnable em vez de
enviar uma mensagem com a classe android.os .Message que no necessrio criar
uma subclasse de androtd.os.Handler e implementar o mtodo handleMessage(nsg).
Utilizando um java.lang.Runnable, naturalmente o mtodo run() implementado por
ela chamado na thread principal.
Para executar ou agendar um java.lang.Runnable, so usados os mesmos mtodos
que para enviar uma mensagem, com os mesmos argumentos, mas agora os no
mes dos mtodos comeam com a palavra post(. . .). A seguir, veja a lista com a
descrio de cada mtodo:
Mtodo Descrio
post(Runnable) Adiciona o Runnable na la de mensagens.
postDelayed(Runnable, long) Adiciona o Runnable na la de mensagens, mas somente
executa o processo depoisdo tempo especicado em
milissegundos.
postAtTine(Runnable, long) Adiciona 0 Runnable na fila de mensagens, mas somente
executa o processo na data especi cada em milissegundos.

Para praticar como utilizar o mtodo post( . . .) e suas variaes, vamos criar um
exemplo para enviar uma mensagem com atraso (delay).

DemoHandlerMessageActivty.java

public class DenoHandlerRunnableActvty extends AppConpatActvity {


private Handler handler = new Handler();
@0verrde
public void onCreate(Bundle icicle) {
super.onCreate(iccle);
328 Google Android - 4 edio
setContentView(R.layout.activity_demo_handler_message);
ndViewById(R.id.btEnviar).set0nClickListener(new Button.0nClickLtstener() {
@0verride
public void onClick(View v) {
// Cria a mensagem com delay de trs segundos
handler.postDelayed(new Runnable() {
@0verride
public void run() {
Toast.makeText(getBaseContet(), "A mensagem Ch90U CON RUHHBUE",
Toast.LENGTH_SHORT).show();
}

}, 3000);
}

});
}

Esse cdigo tem o mesmo objetivo do exemplo anterior, mas note que a sin
taxe utilizando um Runnable mais simples do que enviar uma mensagem pelo
sendMessage(msg). Ao executar 0 cdigo, 0 resultado ser o mesmo da gura 10.1.
Vale lembrar que a interface Runnable uma gurinha bem conhecida no mundo
java e podemos dizer que ela representa algum cdigo que deve ser executado.
Se necessrio, procure mais detalhes sobre essa interface.

10.4 Atualizando a view dentro de uma thread


Depois dessa introduo sobre como utilizar a classe Handler, vamos entender
quando ela realmente necessria, que quando as threads entram na brincadeira.
No captulo 7 sobre a classe View, no exemplo sobre Progressialog, mostramos como
fazer 0 download de uma imagem e mostrar uma mensagem de por favor, aguarde
para o usurio. Se voc voltar l e ver o cdigo desse exemplo, ver que ele utiliza
threads e o mtodo run0nUiThread(runnble) foi utilizado para enviar a mensagem
UI Thread. Antes de tudo, vale explicar que 0 mtodo run0nUiThread(runnble) um
atalho ao mtodo post(runnable) da classe Handler. Agora vamos estudar em mais
detalhes porque isso foi necessrio.

Vamos utilizar este cdigo que vai funcionar como exemplo porque apresenta
um bug.
Captulo 10 I Threads, Handler e AsyncTask 329
/res/layout/activity_downIoad_imagem.xm|
<?ml version="1.G" encoding="utf-8"?>
<FrameLayout xmlnszandroid="http://schenas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<InageView android:id="@+id/img" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" android:scaleType="tCenter" /
<ProgressBar android:id="@+id/progress"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/

No layout XML existe um Imageview e por cima foi inserido um ProgressBar para
mostrar a animao durante o download da imagem. O cdigo da activity sim
plesmente faz o download de uma imagem e atualiza o contedo no Imageview.

DownloadlmagemActivty.java
public class DownloadImagemActivity extends AppCompatActivity {
private static nal String URL = "http://livroandroid.com.br/imagens/livro01.png";
private ProgressBar progress;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_download_imagem);
progress = (ProgressBar) ndViewById(R.id.progress);
progress.setVisibility(View.VISIBLE);
downloadImagem(URL);
}

// Faz o download da imagem em uma nova thread


private void downloadImagem(nal String urlImg) {
new Thread() {
@Override
public void run() {
try {
// Faz o download da imagem
nal Bitmap imagem = Download.downloadBitnap(urlImg);
// Atualiza a tela
atualizaImagen(inagem);
} catch (IOException e) {
// Uma aplicao real deveria tratar este erro
Log.e("Erro ao fazer o download: ", e.getMessage(), e);
}
33 Google Android - 4 edio
}
}.start();
}

// Vai dar Erro neste mtodo pois somente a UI Thread pode atualizar a view
private void atualizaImagem(nal Bitmap imagem) {
// Esconde o progress
progress.setVisibility(View.INVISIBLE);
// Atualiza a imagem
Imageview imgview = (ImageView) ndViewById(R.id.img);
imgview.setImageBitmap(imagem);
}

O download feito na classe Download, conforme demonstrado a seguir:

DownIoad.java
public class Download {
public static Bitmap downloadBitmap(String url) throws IOException {
// Faz o download da imagem
Bitmap bitmap = null;
InputStream in = new URL(url).openStream();
// Converte a InputStream do Java para Bitmap
bitmap = BitmapFactory.decodeStream(in);
in.close();
return bitmap;
}

Para o exemplo funcionar, declare a permisso INTERNET no arquivo AndroidManiest.xm!.

AndroidManifest.xmI
<manifest . . . />
uses-permission android:name="android.permission.INTERNET" /
<application ... /

Para fazer o download, estamos iniciando corretamente uma thread, pois isso
e obrigatorio. Contudo, no nal do download, ao tentar atualizar o Imageview, o
Android vai lanar uma exceo, conforme mostra a gura 10.2.
5mPf que Um UFO HCOIWCC, voc deve olhar os logs detalhados na janela LogCat,
Conforme 21 figura 103, que mostra a stack trace do erro. Podemos ver que o pr0
blema est na linha 46 da classe que criamos. Para mais detalhes do I.o9(at, reviS
Captulo 10 I Threads, Handler e AsyncTask 331
os captulos 2 e 3. Note que a mensagem de erro : Only the original thread
that created a view hierarchy can touch its views. Traduzindo a mensagem, isso
signica que somente a thread principal (UI Thread) pode atualizar as views!
Portanto, aqui que a classe Handler entra em ao.

Figura 10.2 - Erro ao executar o aplicativo.

ins uma
i ,_ '09-20 10:30:08.124 62-92/2 E/Iuputsparcher channel '40'7a4f8 br.livroand:oid.ca:ro:s/br.1iv2oan:iro1d.ca:zo:..
'09-20 18:30:08.124 G2-92/2 E/Inpucllispezcber channel 'l0'a4f8 br.livxoandzroidazros/br.livrc:and:oid.carros..
* O9-20 54.706 62-92/2 E/Inputzbispaccher channel '4080ledO br.livroandroid.livroandzuidcapi.l_hnndler/br.
z :09-20 54.706 62~92/? E/Input:Diapatcher channel 40801ed0 br.livroandtoid.1ivmandroid:apl1_handler/br.
":-1
._ OQ-20 07.115 62-92/?
09-20 07.115 62-92/? E/Inputbiapatcher
E/Inputbiapatcherchannel
channel. -061ebd0bnlivroandxoid.livrcandxeidcapl1__handle:/br.
406lu!.~d0 br.livmandzoid.livrcandroidcapllghandlerbr.
l' 509-20 48.405 62-92/7 E/Inputbispatcher channel 40762508 br.livraandroid.livtoendrcidcapl1__handler/br.
` 092O 48.405 62-92/? E/InputDispa\:che: channel '407{508 br.livrodndroid.l1vroand.roidcap1l_han:1lerfhr.
09-20 48.425 62-92/7 E/Inpucbispacchez: Received spurious receive callback ter unknown inpuc channel.

i.
l G 09-20 26.435
09-20 62-92/?
26.435 E/Inpucbispetcher
62-92/7 channel
E/Inpucbiapaccher '40769af0
channel bnlivxoandrnid.liv:oand.roldcap1l__hand1er-'br.
'|0'i69a0 bnlivroandroid.livroand.roidcap11_hundle:/br.
09-20 54.337 B82-890/? E/Androidkuntlnel FATAL EXCEYIION: Thread-10
android.v1ew.ViewRoat:$Celledfroimnqlhreedxcepxzianz Only the oriqinul thread that created A view hierucny can
ac andrid.view.VievRrot . chechread (viewkooc . j av: : 2932)
at nnd.roid.viev.Vie'uP.oox: . invalidecethild (Vie\P.cm: . java : 642)
ac andmid. v1e\:.V1evRom: . invalldatethildlnarent (viewkooz _ java : 665)
1 ac andreid.v:\ew.VievGroup. inva1ida\:eCh11d (yzgvrnw. i gva :
. at andreia . view . View . invalidez: (fg ei . >> avg ; 2 3)
^ ac and:oid.viea.Yie'r. aetflaga (View. vga- ; 556)
l na uiazmi-1.v1ew.v1ew. ze:v1z1b111:y3g;z,;,_az;5
* al: androidfidqec.P:oqressBar.aet:V1s1.bili\:yr.~,resg
3 lb b: . livmandroid. livroandro1dcap11_hundler . Downloadlmnqemhccivltyil . run (

__ _

Figura 10.3 - Detalhes do erro no LogCat.


Como uma thread foi utilizada para fazer o download, preciso utilizar um
Handler para enviar a mensagem para a thread principal (UI Thread), a m de
atualizar a view O cdigo-fonte a seguir est atualizado para utilizar o mto~
do run0nUThread(runnab1e), que nada mais do que um atalho para o mtodo
Hand1er.post(runnab1e) que estudamos anteriormente.
332 Google Android - 4 edio

r; DownloadlmagemActivity.java

public class DownloadImagemActivity extends AppCompatActivity {


private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
private ProgressBar progress;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_download_imagem);
progress = (ProgressBar) ndViewById(R.id.progress);
progress.setVisibility(View.VISIBLE);
downloadImagem(URL);
}

// Faz o download da imagem em uma nova thread


private void downloadImagem(nal String urlImg) {
new Thread() {
@Override
public void run() {
try {
// Faz o download da imagen
nal Bitmap bitmap = Download.downloadBitmap(URL);
// Atualiza a tela
atualizaInagem(inagen);
} catch (IOEception e) {
// Uma aplicao real deveria tratar este erro
Log.e("Erro ao fazer o download: ", e.getMessage(), e);
}

}.start();
}

private void atualizaImagem(nal Bitmap imagem) {


run0nUiThread(new Runnable() { // Atualiza a view na UI Thread
@0verride
public void run() {
// Esconde o progress
progress.setVisibility(View.INVISIBLE);
// Atualiza a imagem
Imageview ingview = (ImageView) ndViewById(R.id.img)
ingview.setImageBitmap(imagem);
}});
}

}
Captulo 10 I Threads, Handler e AsyncTask 333
Com essa alterao, o exemplo vai executar perfeitamente, pois o download
feito em uma thread separada, mas a view atualizada na thread principal com
a ajuda do nosso amigo Hand1er.A gura 10.4 mostra o resultado.
Q :I ' 3
Download llillflgfflll

I.

Google
.
1

HIIDROII) 1
Apnndaacarnpllcacsparadisposmvosmvels
oomoAndrodSDK `

novatec nrlw-on.|.cmu g 1

Figura 10.4 - Download da imagem realizado com sucesso.

Lembre-se de que estas duas tarefas so obrigatrias ( uma regra):


1. Toda operao de I/O, seja consultar um web service ou acessar o banco de
dados, deve executar em uma thread separada. Qualquer processamento demo
rado tambm deve executar em sua prpria thread para no travar a interface.
2. Somente a thread principal pode atualizar as views. Quando as threads
secundrias terminam o seu trabalho, necessrio utilizar um handler ou
o atalho runOnUThread(runnab1e) para atualizar asviews.

Importante: se a aplicao fechar por causa de um erro, olhe a exceo (stack


trace) nos logs do LogCat. Mostrei o erro de propsito aqui apenas para voc
se acostumar a olhar os logs de erro.

10.5 Agendando tarefas contnuas na activity


A classe Handler muito utilizada para executar tarefas de modo contnuo na
activity Por exemplo, se voc precisar executar um cdigo na activity a cada 30
segundos, isso pode ser feito com um handler. Isso muito comum para criar
telas que precisam car atualizando o seu contedo.
334 Google Android - 4' edio
Para demonstrar, vamos alterar o exemplo de download da imagem para atualizar
a imagem a cada dez segundos, ou seja, refazer o download continuamente. No
cdigo a seguir estou demonstrando apenas as partes importantes.

DownloadlmagemActivity.java
public class DownloadInagenActivity extends AppConpatActivity {

private Handler handler = new Handler();

// Faz o download da imagen em uma nova thread


private void downloadInagen(nal String urllmg) {
new Thread() {
@Override
public void run() {
try {
// Faz o download da imagen...
nal Bitnap bitmap = Download.downloadBitnap(URL);
// Atualiza a tela
atualizaInagen(inagen);
// Agenda o download novamente (daqui a dez segundos)
handler.postDelayed(new Runnable() {
public void run() {
downloadInagen();
}
},16606); // dez segundos
} catch (IOException e) {

}.start();
}

@0verride
protected void onDestroy() {
super.onDestroy();
// Cancela o runnable ao sair da activity
handler.renoveCallbacksAndHessages(null);
}

Ao executar esse cdigo, ser feito o download da imagem normalmente. Depois


de dez segundos, o download ser feito novamente. Observe que no mtodo
onDestroy() da activity todas as mensagens enviadas ao handler foram canceladas
Captulo 10 I Threads, Handler e Asynclask 335
com o mtodo renoveCallbaclsAndMessages(null). Isso necessrio para garantir que
nenhuma mensagem seja entregue com a activity fechada. Caso contrrio, o
handler continuaria executando continuamente sem a necessidade de fazer isso.

10.6 Implementao de um tela Splash Screen para sua aplicao


Provavelmente voc j viu algum aplicativo que exibe uma tela inicial com a
mensagem Por favor, aguarde, carregando a aplicao...' ou talvez exibindo uma
imagem antes de carregar o aplicativo.
Essas telas de inicializao so chamadas de splash screen e para implement-las
no Android podemos utilizar umhandler. Uma splash screen deve permanecer
aberta por determinado tempo para que a aplicao consiga realizar algum pro
cessamento inicial. Enquanto isso, o usurio pode car observando uma imagem
ou mensagem na tela.
Vamos criar. uma splash screen na qual o layout da activity vai mostrar uma
simples gura.

/res/layout/activity_spIash_screen.xml
<FraneLayout nlns:androd="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent">
<InageView androd:src="@drawable/livro_android"
androd:layout_width="wrap_content" android:layout_heght="wrap_content"
android:layout_gravity="center"/>

O cdigo da activity vai mostrar a tela por um segundo e depois vai prosseguir para
a primeira activity do projeto. Para isso, uma mensagem enviada ao handler com
um atraso de um segundo. No momento em que o handler receber a mensagem,
a aplicao j foi carregada e a prxima activity j pode ser exibida.

SpIashScreenActivity.java
public class SplashScreenActvity extends Activity {
@Overrde
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Exibe o layout com a imagen...
setContentView(R.layout.activity_splash_screen);
// delay de 1 segundo
new Handler().postDelayed(new Runnable() {
336 GO0Ql|! hdlld - 4' edlh
@0verride
public void run() {
// Inicia a HainActivity ,
startActivity(new Intent(getBaseContet(), HainActivity.c1ass));
// Fecha a activity da splash
nish();
}

},1600); /I Un segundo de atraso


1

Urna splash screen geralmente a primeira activity do projeto que aquela ao


MAIN e categoria LAUNCHER configurada no AndroidHani.fest.m1. No projeto de exemplo
deste captulo. eu deixei a Sp1ashScreenActivity como a primeira activity e cadastrei
a MainActivity que mostra a lista com os exemplos como uma activity normal,
conforme demonstrado a seguir.

J AndroidManifest.m|
<nanifest . . .>
<uses-permission android:nane=android.permission.INTERNET" /
<app1ication . . . >
activity androtd:nane=".SpIashScreenActivity"
intent-Iter
<action android:nane="android.intent.action.HAIN" /
category android:nane=android.intent.category.LAUNCHER /
/intent-lter
/acttvity
<activity android:name=".MainActivity" android:1abeI="@string/app_nane" /

por isso que, ao executar o projeto de exemplo deste captulo, voc ver uma
splash screen antes de visualizar a lista com os exemplos. Lembre-se de que a tela
de splash screen deve chamar o mtodo nish() para encerrar (3553 activity para
destruir a splash ao entrar no aplicativo.

|mP|'| Saiba WC Segundo as boas praticas de interface para Android.


no e recomendado criar uma splash screen. Uma tela de splash screen deve
ser utilizada somente se voc obrigatoriamente precisa ganhar tempo antes de
abrir *1 1*Pl11 POP Cxcmplo, para carregar informaoes necessarias para o
seu aplicativo funcionar. O fato e que muitos aplicativos comearam a abusar da
5Pla5h SCYCCH c mostram mclusive banners e anuncios antes de abrir o aplicativo.
Captulo 10 I Threads, Handler e AsyncTask 337
Isso para os usurios ruim, pois eles querem utilizar o aplicativo o mais
rpido possvel, e no car olhando guras. Portanto, utilize uma splash screen
somente se for realmente necessrio. Esse assunto foi mostrado aqui apenas para
voc saber como implementar tal tipo de funcionalidade, mesmo porque est
relacionado com o disparo de mensagens com atraso (delay) e a classe Handler.

10.7 AsyncTask
Criar uma thread simples, mas vimos que necessrio utilizar um Handler ou o
mtodo de atalho run0nUIiThread(runnable) para atualizar a interface.
Porm ao desenvolver aplicativos para Android no utilizamos threads diretamen
te, pois se recomenda utilizar a classe AsyncTask, a qual representa uma pequena
biblioteca de threads. A seguir podemos visualizar as suas principais caractersticas.
1. A classe AsyncTask gerencia internamente as threads e handlers necessrios
para atualizar a interface.
2. Uma tarefa pode ser cancelada se chamar o mtodo cancell(boolean).
3. Contm mtodos para atualizar o andamento (progresso) de uma tarefa,
por exemplo, o progresso de um download.
4. Contm um pool de threads que pode executar as tarefas de modo serial
ou em paralelo.
Para explicar o que uma AsyncTask, vamos ver um exemplo de cdigo. Basicamente
para criar uma tarefa preciso criar uma subclasse de AsyncTask e informar os trs
argumentos <Parans,Progress,Result>, conforme demonstrado a seguir. A sintaxe
<Parans,Progress,Result> das classes genricas (Generics) do Java.
private class DownloadFilesTask extends AsyncTaskURL, Integer, Long> {
@0verride
protected void onPreExecute() {
// Executa na thread principal
// til para mostrar um ProgressDialog ou ProgressBar
}

protected Long doInBackground(URL... urls) {


// Executa em segundo plano (background)
// 0 retorno do tipo "Long" passado ao mtodo onPostExecute()
return 1L;
}

protected void onProgressUpdate(Integer... progress) {


// Pode atualizar o progresso da tarefa
338 Google IO - 4 Ed0
// 0 valor do progresso deve ser enviado via o mtodo publishProgress(int)
// durante o doInBackground()
}

protected void onPostExecute(Long result) {


// Recebe o resultado do mtodo doInBackground()
// Executa na UI Thread e pode atualizar a view
}

O cdigo anterior est comentado, ento leia-o com ateno. Depois de criar essa
classe, para execut-la basta chamar o mtodo execute() e informar os parmetros,
que nesse caso pela Genetics foi denido como um objeto do tipo URL.
new DownloadFilesTask().execute(url);

Ou voc pode at passar mais de um parmetro:


new DownloadFilesTask().eecute(url1, url2, url3);

Para vermos um exemplo mais detalhado, o cdigo a seguir mostra como


fazer o download de um arquivo, e ainda mostra como utilizar o mtodo
onProgressUpdate(progress).

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {


protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
// Reporta o progresso
publishProgress((int) ((i / (oat) count) * 196));
// Se o mtodo cancel() foi chamado, termina.
if (isCancelled())
break;
}

return totalSize;
}

protected void onProgressUpdate(Integer... progress) {


// Atualiza a interface com o progresso dd dgwnload,
setProgressPercent(progress[0]);
1

protected void onPostEecute(Long result) {


showDialog("Downloaded " + result + " byteS")
}

}
Captulo 10 I Threads, Handler e AsyncTask 339
Na prtica, eu nunca precisei utilizar os mtodos pub1shProgress(progress) e
onProgressUpdate(progress), pois geralmente em aplicativos a nica coisa de que
precisamos consultar um web service, e durante isso podemos mostrar uma
animao com um ProgressBar ou Progressalog. Portanto, costumo utilizar um
template apenas com os mtodos onPreExecute(), doInBackground() e onPostExecute().
A lista a seguir explica o signicado de cada mtodo da AsyncTask.
Mtodo Descrio
onPreExecute() Mtodo executado antes de a thread iniciar, sendo uma boa
oportunidade para exibir uma janela de progresso ao usurio
ou uma mensagem de por favor, aguarde? Esse mtodo executa
na UI Thread."
doInBackground() Mtodo executado em background por uma thread, que deve
conter todo _o processamento pesado. Ele pode retornar um
objeto qualquer, o qual ser passado como parmetro para o
mtodo onPostEecute(). aqui que a thread executa, mas isso
feito automaticamente para voc.
onProgressUpdate() Mtodo chamado na UI thread e recebe geralmente um inteiro
para informara quantidade do progresso. O progresso deve serin
formado em background dentro do mtodo doInBackg round( ). Para
reportar o progresso, utilizado o mtodo pub1shProgress(int).
onPostEecute() Mtodo executado na UI Thread, em que podemos atualizar
a view com o resultado. Ele chamado utilizando um Handler
internamente.

Note que esses mtodos no devem ser invocados manualmente, pois so chama
dos automaticamente pela classe AsyncTask. Portanto, para iniciar o processamento,
necessrio apenas chamar o mtodo AsyncTask.execute(params. . .), informando os
parmetros se necessrio, como por exemplo: `
new Down'LoadF1esTask().eecute(ur11, ur12, ur13);

Nota: resumindo, voc deve criar um AsyncTask e utilizar o mtodo doInBackground()


para fazer o processamento, o qual executado automaticamente em uma thread.
Quando terminar, utilize o mtodo onPostEecute() para atualizar as views. A
classe AsyncTask elimina a necessidade de criar uma thread e utilizar um handler,
pois ela j faz esse trabalho para voc.

Talvez a parte mais difcil de entender na classe AsyncTask sejam os seus trs tipos
genricos, que tm a seguinte denio: AsyncTask<Params, Progress, Resu1t>.
340 Google Android - 4 edio
Parmetro Descriao g d_ _ __ ____ _ _`_ W
Pa rams O primeiro tipo genrico chamado de Parans, que so os argumentos
que podemos passar ao mtodo eecute(params. . .) para executar 0
AsyncTask. No exemplo da classe DownloadFilesTask, o parmetro foi
denido como URL, e portanto a tarefa pode ser executada passando
URLs como parmetro ao mtodo eecute( ) , ex: new DownloadFilesTask(),
eecute(url1, url2, url3).
Progress O segundo tipo genrico chamado de Progress, e pode ser utilizado
para receber um valor inteiro, que representa o progresso da exe
cuo, e em conjunto com uma barra de progresso, para noticar
o usurio. No exemplo que criamos, esse parmetro foi denido
como Integer, e o mtodo onProgressUpdate(Integer. .. progress) cou
com essa assinatura.
Result O terceiro tipo genrico chamado de Result, e o mesmo objeto
que retorna do mtodo doInBackground() passado como parmetro
para o mtodo onPostEecute(). O mtodo onPostEecute() executa na
UI Thread e pode atualizar a interface. No exemplo que criamos,
o retorno do mtodo doInBackground() era do tipo Long, e o mtodo
onPostEecute(Long result) recebe um Long como argumento.

Eu sei, parece complicado, mas logo voc se acostuma com a ideia. Mas isso nada
mais do que o conceito de Genetics da linguagem Java. Qualquer dvida, con
tinue lendo e depois volte aqui para revisar os conceitos, no se preocupe se no
entender exatamente o que significa cada parmetro agora.
Outra informao importante que, ao criar a AsyncTask, voc pode denir os ti
pos genricos que quiser e deixar alguns como Void. O prximo exemplo mostra
como deixar todos os argumentos como Void e somente o argumento Result como
Boolean. Veja como ca a sintaxe:

private class DownloadBitnapTask extends AsyncTask<Void, Void, Boolean {


Bitnap bitmap;
@Override
protected Boolean doInBackground(Void... parans) {
bitmap = downloadBitnap();
return true;
}

protected void onPostEecute(Boolean ok) {


if(ok) {
// atualizar a view aqui :-)
}

}
Captulo 10 I Threads, Handler e AsyncTask 341
s vezes, um cdigo e um exemplo falam mais que mil palavras. Vamos criar nova
mente o exemplo do download da imagem, mas desta vez utilizando a classe AsyncTask

MainActivity.java
public class MainActivity extends AppCompatActivity {
private static nal String URL = "http://livroandroid.com.br/imgs/livro_android.png";
private ProgressBar progress;
private Imageview imgview;
private Bitmap bitmap;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity;download_imagem);
imgView = (ImageView) ndViewById(R.id.img);
progress = (ProgressBar) ndViewById(R.id.progress);
// Faz o download
downloadImagem();
}

// Faz o download da imagem em uma nova thread


private void downloadImagem() {
// Cria uma AsyncTask
DownloadTask task = new DownloadTask();
// Executa a task/thread
task.execute();
}

private class DownloadTask extends AsyncTaskVoid,Void,Bitmap {


@Override
protected void onPreExecute() {
super.onPreExecute();
// Executa antes do download. Mostra o ProgressBar para fazer a animao.
progress.setVisibility(View.VISIBLE);
}

@0verride
protected Bitmap doInBackground(Void... params) {
// Faz o download em uma thread e retorna o bitmap.
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e("livroandroid",e.getHessage(), e);
}

return bitmap;
}
342 Google Android - 4' edio
protected void onPostExecute(Bitnap bltnap) {
if(bitmap != null) {
// Atualiza a imagen na UI Thread
ingvew.setImageBtmap(btnap);
// Esconde o progress
progress.setVisiblity(View.INVISIBLE);
}

public Context getContet() { return this; }


}

O layout da activity o mesmo do exemplo anterior do doumload da imagem. E,


ao executar o cdigo, o resultado tambm ser o mesmo.
O importante voc perceber que a AsyncTask encapsulou a necessidade de mani
pular as threads e handlers, alm de trazer outros benefcios os quais no utilizei
neste exemplo, como cancelar as tarefas ou utilizar o pool de threads para executar
o processo de forma serial ou paralela.

Nota: uma das vantagens de utilizar a classe AsyncTask que possvel parar a
execuo da tarefa com o mtodo cancell(boolean). Quando uma tarefa cancelada,
o mtodo onCancelled() chamado. Para descobrir se uma tarefa j foi cancelada,
basta chamar o mtodo isCancelled(). comum nos mtodos onPause() da activity
e fragment chamar o mtodo cancell(boolean) da AsyncTask; assim, quando a activity
vai entrar em estado de pausa, essas tarefas so interrompidas para economizar
recursos, uma vez que a tela ser fechada. Tudo depende do caso e se voc deseja
ou no parar a execuo da tarefa.

10.8 Download de imagens tom a biblioteca Picasso


O principal objetivo deste captulo foi lhe explicar a importncia do uso de
threads no aplicativo e consequentemente explicamos as classes Handler e AsyncTask.
Como o exemplo de threads que criamos fez o download de uma imagem, vamos
aproveitar e mostrar uma biblioteca que facilita este download, chamada de Picasso
(http://squaregithub.io/picasso/).

Essa biblioteca utilizada por muitos desenvolvedores, pois extremamente


simples e inclusive faz cache das imagens. Para utiliza-la, basta declarar a seguinte
dependncia no arquivo app/bui1d.gradle.
Captulo 10 n Threads, Handler e AsyncTask 343
/app/buiId.grad|e
dependencies {

compile 'com.squareup.picasso:picasso:2.5.2'
}

Depois de declarar adependncia, o download pode ser feito com apenas uma
nica linha de cdigo.
Picasso.with(this).1oad(URL).into(imgView);

Tambm possvel especicar uma imagem que deve ser exibida antes de o
download terminar (placeholder) e uma imagem de erro.
Picasso.with(this).1oad(URL).placeholder(R.drawab1e.android).error(R.drawab1e.android)
.into(imgView);

Caso voc queira animar um ProgressBar como fizemos nos exemplos anteriores,
basta mostrar o progresso antes do download, e escond-lo quando o download
terminar. A biblioteca Picasso oferece a interface de Callback para avisar o aplicativo
sobre o resultado do download, seja sucesso ou falha.
progress.setVisibi1ity(View.VISIBLE);
Picasso.with(this).1oad(URL)
.p1aceho1der(R.drawable.android)
.error(R.drawab1e.android)
.into(imgView, new Ca11back() {
@Override
public void onSuccess() { // OK
progress.setVisibility(View.GONE);
}

@0verride
public void onError() { // ERRO
progress.setVisibi1ity(View.GONE);
}

});

isso: simples e prtico.

Dica: mostrei como animar um ProgressBar para seu aprendizado, e isso muito
utilizado em vrias situaes. Porm, no caso de download de imagens, parece
que est havendo uma tendncia de utilizar apenas uma imagem temporria
durante o download, chamada de placeholder. Um exemplo a navegao no
aplicativo do Google Play que mostra uma imagem vazia enquanto o download
344 Google Android - 4 edio
do cone do aplicativo no termina. Nesse contexto, a biblioteca Picasso
extremamente til, pois faz o download em uma nica linha de codigo.

10.9 Links teis


Neste captulo, estudamos a classe Handler, que tem como principal objetivo
enviar uma mensagem para ser processada na UI Thread. Tambm estudamos a
importncia de criar threads e por ltimo aprendemos a utilizar a classe AsyncTask.
No que se refere principalmente classe AsyncTask, ainda temos rnuito o que estudar.
Como eu expliquei anteriormente, a classe AsyncTask uma biblioteca de threads e
contm vrios recursos interessantes. Acredito que esta introduo sobre threads
e AsyncTask j suciente para avanarmos com nossos estudos.
Eu quero entrar logo nos captulos 11 e 12, sobre Material Design e Toolbar respec
tivamente, para logo depois comear o desenvolvimento do aplicativo dos carros
passo a passo. Nesse aplicativo tambm vamos usar threads, pois os carros sero
buscados de um web service, portanto vamos aprender na prtica tudo o que for
necessrio. No final do livro, deixei um captulo especial mais avanado sobre a
classe AsyncTask (Captulo 31), que acredito que agora no seja o momento certo
para ler. Vamos dar um passo de cada vez.
Para complementar seus estudos, separei alguns links da documentao ocial.
(ommunicating with the UI Thread

https://developer android. com/training/multiple-threads/communicate-ui. html


Processes and Threads

http://developer android.com/guide/components/processes-and-th read.s.html


Picasso

http://squaregithub.io/picasso
.\ !.
CAPTULO 11
Material Design
\-1
.xl p

Com a chegada do Android 5.0 (Lollipop), foi criado o Material Design, um guia
completo sobre como criar aplicaes com um timo design e que leva em consi
derao que atualmente o Android est difundido em diversos dispositivos como
smartphones, tablets, wearables, culos, TVs e carros.

Com esses avanos da plataforma, foi necessrio criar um guia de Design, e princi
palmente uma interface que funcione de forma consistente, independentemente da
plataforma e do tipo do dispositivo, seja um pequeno relgio ou uma grande TV

11.1 Introduo
O Material Design um guia completo sobre como implementar o visual, anima
es e interao entre os componentes de um layout, considerando que o Android
se tornou uma plataforma comum para vrios dispositivos, como smartphones e
tablets (Android), wearables (Android Wear), culos (Google Glass),TVs (Android
TV) e carros (Android Auto). `
Implementar o visual de um aplicativo de forma consistente, simples e intuitiva para
cada tipo de dispositivo um desao, e o Material Design o resultado do esforo
do Google de padronizar um guia completo de design para nos auxiliar nessa tarefa.
No Android 5.0 (Lollipop), foram criadas diversas APIs para auxiliar o desenvol
ver a criar interfaces ricas, uidas e com animaes iguais quelas encontradas
nos aplicativos nativos do Google. O melhor de tudo que podemos utilizar
uma biblioteca de compatibilidade para trazer os benefcios do Material Design,
inclusive para dispositivos com verses antigas do Android.
A gura 11.1 da documentao ocial do Android e mostra a ideia do Material Design:
um aplicativo que tem um design consistente em diversos tipos de dispositivos.

345
346 Google Android - 4' edio

...z .. .. _...--zz
.__,,.,,.`,,,,,
,.. ..-zm
..-..~. zz...
W.
. a.....
.z..........`$--
...uv I

Figura 11.1 - Material Design.

11.2 Tema Material

No captulo 5, sobre action bar, estudamos a importncia dos temas na plata


forma de desenvolvimento do Android. Vimos que o tema Holo revolucionou o
desenvolvimento de aplicativos, pois introduziu a action bar, e o tema Material
foi criado junto com o Android 5.0 (Lollipop).
Nesta altura do livro, voc j sabe congurar a biblioteca de compatibilidade
no projeto e tambm sabe utilizar o tema Material. Conforme estudamos, basta
configurar o tema do aplicativo para herdar de algum destes temas:
android:/Theme.Matera1

android:/Theme.Material.Light
androd:/Theme.Materia1.Lght.DarkActionBar

Caso o aplicativo utilize a action bar de compatibilidade, deve-se utilizar o tema


AppCompat, que consistente com todas as verses do Android.
Theme.AppCompat

Theme.AppCompat.Lght

Theme.AppCompat.Lght.DarkActonBar

Lembre-se de que, para utilizar o tema AppCompat, voc deve declarar no arquivo
app/build.gradle a dependncia para a biblioteca supportzappcompat-v7 e todas as
activities devem herdar de AppCompatActvty.
Captulo 11 I Material Design 347
app/buiId.gradIe
dependencias {

compila 'com.androd.support:appcompat-v7:22.1.9'
}

Nota: para um melhor entendimento dos prximos exemplos, recomendo abrir o


projeto de exemplo deste captulo no Android Studio e acompanhar a explicao.

11.3 Paleta de cores


O Material Design utiliza muito o conceito de ter uma cor padro (primary color)
para o aplicativo e uma cor de acentuao (accent color) para dar destaque a al
gumas views. No captulo 7, sobre a classe View, j expliquei um pouco sobre esse
assunto, e agora vamos estuda-lo um pouco mais a fundo.
O mais legal do tema Material que o Google tornou bem simples customizar
as cores do tema, algo que sempre foi uma dor de cabea nos temas antigos. Para
customizar as cores, basta sobrescrever algumas propriedades do tema, conforme
indicado pela figura 11.2.

_1, l
Figura 11.2 - Cores para customizar 0 tema Material.
343 Google Android - 4 edio
Para brincarmos, crie o projeto Helloaterial, ou abra o projeto de exemplo deste captu
lo. Ao criar o projeto com oAndroid Studio, deike ele compatvel com o Android 23 ou
superior e ative a biblioteca de compatibilidade appconpat-v7 no arquivo app/build.gradle_
Lembre-se tambm de que a MainActivity deve ser lha de AppCompatActivity.

Para comear, vamos customizar as cores do tema. O que eu costumo fazer


customizar a cor primria e a cor de acentuao, conforme demonstrado a seguir.
Note que, como estamos utilizando o tema AppConpat, s precisamos de uma verso
do arquivo /res/values/styles.xml. `
/res/values/styIes.mI

<style nane="AppThene" parent="Theme.AppConpat.Light.DarkActionBar">

<iten name:"colorPrinary"@color/prinary

<iten name:"colorPrinaryDark">@color/prinary_dark

<iten name="colorAccent">@color/accent

Estou customizando as cores no arquivo do tema, e por isso criei estas cores no
arquivo /res/values/colors.xml.

/res/values/coIors.xmI
<resources

<color name="primary">#03A9F4
<color name="primary_dark">#01579B
<color nane="accent">#F44336

Simples assim. Se voc executar o projeto com essas conguraes, a cor da action
bar ser azul, a cor da status bar ser um azul escuro, e a cor de acentuao uti
lizada para dar destaque vermelho.

Nota: a cor primria (primary) deve ser a cor principal do aplicativo. A cor primria
escura (primary escura) uma variao escura da cor primria e utilizada na
status bar. A cor de acentuao (accent color) de extrema importncia para
destacar views importantes do aplicativo e chamar a ateno do usurio.
Captulo 11 I Material Design 349
Para escolher essas cores, o Google recomenda seguir a paleta de cores, que pode
ser encontrada nesta pgina da documentao ocial:
http://www. google. com/design/spec/style/color html
Voc vai encontrar diversas cores nessa ina e todas elas tero vrios nveis
9

de intensidade. Segundo as boas prticas do Material Design, a cor primria


(primary) do aplicativo deve ser alguma cor com intensidade 500 e a cor primria
forte (primary dark) deve ter intensidade 700 ou superior.
Para entender do que estou falando, abra o link mencionado anteriormente e
veja a paleta de cores.

11.4 Elevao de views


No Material Design, as views podem ser elevadas da superfcie, aumentando ou
diminuindo a sombra sobre elas. Para alterar a elevao de uma view no XML,
utilize o atributo androdzelevaton ou utilize o mtodo setE1evaton(z) para alterar
pelo cdigo.
A gura 113 demonstra o conceito de elevao. Na esquerda podemos visualizar
a view normal. Na direita podemos visualizar o resultado se aplicada a elevao
no eixo Z da view

Figura 11.3 - Elevao e sombra da view.

Esse conceito um dos pilares do Material Design, pois os elementos visuais so


construdos e aplicados sobre camadas que se sobrepem. A figura 11.4 demonstra
a ideia. Na esquerda as views esto sobrepostas (correto) e na direita no temos
ideia de profundidade (incorreto).
350 Google Android - 4~ Qdljg

=:\ 1%zu'm| r;, '

Figura 11.4 - Elevao e sombra da view.

Para praticarmos o conceito de elevao, vamos criar um exemplo. O arquivo de


layout contm um boto cuja elevao vamos alterar via programao.

Ci /res/Iayout/activity_exempIo_eIevation.xml

<LnearLayout . . .>
<Button android:d="@+d/button" androd:tet="@strng/he1Io_wor1d"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
androtd:eIevatton="2dp" />
<SeekBar androd:id="@+d/seekBar"
sty1e="?android:attr/progressBarSty1eHorzonta1"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:nax="100" androtd:paddng="2Gdp" androd:progress="0" />
</LnearLayout>

A SeekBar uma view que se parece com um controle para aumentar ou diminuir
o volume do som; neste caso, vai permitir navegar entre O a 100, pois foi definida a
propriedade androd:ma="100". No codigo da activity sempre que o valor da Seekbar
for alterado, vamos congurar esse mesmo valor na elevao do botao.

(5131 ExemploE|evationActivity.java

public class Eemp1oE1evationActvity extends AppCompatActvity {


private SeekBar seekBar;
private Button button;
@0verrde
protected void onCreate(Bund1e savedInstanceState) [
super.onCreate(savedInstanceState);
Captulo 11 I Material Design 351
setContentView(R.layout.activity_eemplo_elevation);
button = (Button) ndViewById(R.id.button);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
seekBar = (SeekBar) ndViewById(R.id.seekBar);
seekBar.set0nSeekBarChangeListener(new SeekBar.0nSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fronUser) {
// Altera a elevao da view ao mexer na SeekBar
button.setElevation(progress);
}
@0verride
public void onStartTrackingTouch(SeekBar seekBar) { }
@0verride
public void onStopTrackingTouch(SeekBar seekBar) { }
});
1

Para entender esse exemplo, voc precisa executar no emulador. Ao mexer na


Seekbar, a elevao da view ser alterada e voc perceber o efeito que acontece na
sombra da view como se a view estivesse levantando e saindo da tela. A gura
115 mostra o valor 0 (zero) congurado como elevao e depois o valor 100. Veja
que no valor 100 a sombra da view grande.
Vale ressaltar que o mtodo setElevation(oat) da classe View est disponvel apenas
para o Android 5.0 ou superior. Em nosso caso, por questes de compatibilidade,
pode ser utilizada a classe Viewtompat, conforme demonstrado a seguir:
ViewConpat.setElevation(view, valor);

A classe ViewConpat aplica a elevao em dispositivos com Android 5.0 ou superior,


ou simula a elevao em verses antigas. O importante que o cdigo ca com
patvel com todas as verses.

. aowm; ueio
. __ u zz z z~ zz z z ,Q ` ,~. uw, .

Figura 11.5 - Exemplo de elevao e sombra da view.


3 52 Google Android - 4 edio
Importante: execute esses exemplos no emulador do Android 5.0 ou superior
para obter os efeitos desejados do tema Material. Por questes de compatibilidade
com verses antigas do Android, recomendo utilizar a classe Vewtompat.

11.5 Ripple - feedback ao toque


Ripple o nome do efeito de toque padro do Android 5.0, e todas as views como
botes, listas e aes da action bar o tm por padro. Esse? efeito de toque no
Android conhecido como Touch Feedback (feedback ao toque), e seu objetivo
informar ao usurio que o sistema recebeu 0 toque de uma forma leve e agradvel.

l
O efeito de ripple alcanado pela classe Rpplerawable, que pode ser criada por
programao, ou at mesmo via XML criando uma tag <rpple>. Neste prximo
exemplo, vamos testar em um boto vrios efeitos diferentes. Para isso abra o
arquivo /res/layout/activity_exemplo_ripple.xmI no editor visual (Figura 11.6).

* bel ii] l
.zirtriry_eempIo_r|ppIe xml \ 1
O ..
, Nexus S' v AprTheme mv -'

1 .,..
i

'W*V'- z'.-
2..=;-ir
rf cr
'-M, mg;

1

l
'NG
l

Figura 11.6 - likitos ao clicar em um boto.

Cada boto est congurado com um efeito diferente, mas para entender a expli
caao, por favor, execute o projeto no emulador para visualizar o efeito. No tem
como demonstrar as animaes no livro.

Agora que voce Ja executou o exemplo, vamos continuar com a explicao.


Captulo 11 I Material Design 353
O primeiro boto no faz `nada, mas caso o aplicativo seja executado em um
Android 5.0 ou superior o efeito de ripple criado automaticamente.
Para dar esse feedback ao toque e adicionar o efeito de ripple e qualquer vievsg
podemos utilizar o item ?attr/se1ectab1eItemBackground, que deve ser aplicado como
fundo (background) de uma view O segundo boto mostrou justamente isso.

<Button android:text="@strng/ok2" . . .
android:background="?attr/se1ectab1eItemBackground" />

A gura 11.7 demonstra a animao criada ao tocar no boto. A primeira parte


mostra o toque, e depois o efeito da onda foi crescendo e se propagando at ocupar
o boto inteiro. O item ?attr/se1ectab1eItemBackground muito utilizado ao construir
uma lista com Lstvew, com o objetivo de congurar o efeito de toque da lista.

()KI2

()K2

()K2

Figura 11.7 - Feedback ao toque.

Nota: a animao de ripple parecida com o efeito de quando voc toca alguma
superfcie com gua, formando aquelas ondas circulares.

O terceiro boto mostrou o efeito criado pelo item ?androd:attr/se1ectab1eItemBa


ckgroundBorder1ess, o qual faz o efeito da onda porm sem margens.

<Button androd:tet="@strng/ok3" . . .
androd:background="?attr/se1ectab1eItemBackgroundBorder1ess" />

como se o efeito continuasse executando, pois no bateu em nenhuma borda


para parar. A gura 11.8 demonstra que o efeito sem borda inclusive invadiu o
espao dos outros botes.
Os prximos botes, 4, 5, 6 e 7, mostram como criar um efeito de ripple customi
zado com um arquivo XML; assim podemos denir inclusive as cores do boto
e do efeito da onda.
354 Google Android - 4= edio
OK2

OK3

OK4

Figura 11.8 - Feedback ao toque com o efeito da onda sem a borda.

Um ripple criado com a tag <rpp1e> e o atributo androdzcolor dene a cor do


efeito das ondas. A tag dene o formato da borda, que pode ser retangular
(shape="rectang1e") ou oval (shape="ova1"). A seguir, temos um exemplo de arquivo
que cria um efeito de ripple retangular.

/res/drawable/rppIe_rect.xmI
<rpp1e xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:co1or="@co1or/accent">
<tem androd:d="@androd:id/mask">
<shape androd:shape="rectang1e">
<so1d androd:co1or="@co1or/primary" />

</rpp1e>

Esse arquivo customizado pode ser denido como o fundo de qualquer view
conforme demonstrado a seguir.

<Button androd:text="@strng/ok4" . . .
android:background="@drawable/rpple_rect" />

Para conferir os resultados, execute o projeto de exemplo deste captulo.

Nota: segundo as boas prticas do Material Design, importante fornecer


o feedback ao toque (Touch Feedback) para interagir com o usurio. Esses
conceitos podem ser aplicados em qualquer tipo de view.
Captulo 11 n Material Design 355
11.6 Floating Action Button (FAB)
Um dos novos padres mais utilizados do Material Design o Floating Action Button,
que consiste em um boto utuante que deve ser utilizado para conter a ao
mais importante da tela. Esse boto deve ter uma cor chamativa e por isso re
comendado utilizar a cor de acentuao denida no tema.
Vale ressaltar que o Floating Action Button frequentemente chamado apenas de FAB,
portanto acostume-se com essa sigla. Felizmente, criar um FAB bem simples e voc
pode utilizar um InageButton com um fundo especial, conforme o cdigo a seguir:

/res/Iayout/activity_exempIo_oating_button.xmI
<FrameLayout n1ns:androd="http://schens.androd.con/apk/res/androd"
android:1ayout_wdth="natch_parent" androd:1ayout_heght="natch_parent"
androd:paddng="16dp">
<TextVew android:tet="@strng/he11o_wor1d"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content" />
<ImageButton`androd:d="@+d/bt0k"
androd:1ayout_wdth="56dp"androd:1ayout_heght="56dp"
androd:1ayout_gravity="center"
android:src="@androdzdrawable/c_nput_add"
android:tint="#fffB00"
android:background="@drawable/fab_ova1"
androd:e1evation="8dp" />
</FraneLayout

Esse boto foi congurado com uma imagem nativa do Android, sendo que a
notao @androd:drawab1e/ nonejnagem utilizada para acessar um recurso nativo do
Android. Em nosso exemplo, estamos utilizando a imagem c_nput_add. Se voc
abrir essa figura com a tecla CtrI+cIique no editor, ver que ela um sinal de mais
(+) transparente, conforme a figura 11.9.
1 -. : c \' _
' 'z^'. '. U-'zz '~ r, *.

.z;_,.. _.(4~.zh z \_._


W ''=='f'.

..-.
-,..;, ' .-uy...
ni 1 rf x'f':' .z \ viu \` w ~:
..".`=..'"," ' -`."-X
-1' '1'z``.'--'- _ v'` _
..,.zzz.z,.-,r;.

Figura 11.9 - Imagem transparente.

O segredo dessa imagem que a rea do sinal de mais (+) pode ser preenchida
com qualquer cor, e por isso definido o atributo androd:tnt="#fff000" que est
pintando a cruz de amarelo. j o fundo do boto feito com um efeito de ripple,
sendo assim basta criar um XML customizado conforme demonstrado a seguir:
356 Google IIO - 4 edio
/res/drawable/fab_ovaI.xmI
<?m1 verson="1.0" encoding="utf-8"?>
<rpp1e m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:co1or="?androd:coIorPrimary">

</tem> `
<tem>
<shape androd:shape="ova1">
z<so1d androd:co1or="?android:co1orAccent" />
</shape

E isso tudo! Neste exemplo, a gura c_nput_add mostra o sinal de mais (+), qug
padro do Android, e o fundo do ripple criou um shape=ova1, por isso a imagem ficou
como um crculo. O fundo do ripple foi denido como vermelho, que a cor de
acentuao congurada no projeto. A gura 11.10 mostra o resultado, e quando voc
executar no emulador ver que o boto tem o Touch Feedback com o efeito do ripple.
4 u 2 33
-' . ', -1 z2}`-1" -=\' f' ' QC. .. ""!K"i " 1'-*"'*'
-,ty-~
gw* .Lg *f
-,r _.:f'. , .. law;-'52
f z.1..1~^. ~*,_;.z.V`
1.- ,f,._-_~..._Wz.
~ _=;1 ..:%zf4_ zm _-vil.
.t da w~~
Q

lif:(_.'~z`V,..V.\,.
, z'_ V ~ fist.. .. 'Ja
,z -' .'l.;"V. _2`.. Ez
..' -1^*-'
; ._
Q A :'.:'u; z z' ,4;^-gi. _$'z., . - N-.i.{v.,_-Qz, g {--rw ;_'.'_, _ * 2

,_<.,,;'-';._
"' *
'ijg'
-fr?;&:#z

i;_z=\_,
.

Figura 11.10 - Floating Action Button.

Nota: a imagem do sinal de mais (+) transparente e conhecida como Tint Drawable
Resources. No Android 5.0 (API Level 21) ou superior possvel criar as figuras
como nine-patches e demr uma msca ra transparente (alpha masks). Essa rea
transparente pode ser pintada (tint) com a cor desejada

ltsse exemplo que fizemos sobre o boto FAB interessante porque voc apren
deu conccitos importantes como Tin! Drawablc Resource; Q ngvameme utilizamos
Captulo 11 I Material Design 357
o efeito de ripple. Porm, logo depois do Google I/O 2015, o Google anunciou a
biblioteca Android Design Support Library que contm classes e componentes para
auxiliar na construo de aplicativos com Material Design. Dentre esses com
ponentes, foi criada a classe F1oatngActonButton, que uma subclasse de Inageview
e torna simples a tarefa de criar um Floating Action Button. Para utilizar a biblioteca
Android Design Support Library, declare a dependncia no app/buildgradle.

app/buiId.gradIe

conpe 'con.android.support:desgn:22.2.0'

Utilizando esta biblioteca, criar um Floating Action Button um passe de mgica,


conforme mostra o cdigo a seguir. Vale ressaltar, que por padro a cor do fundo
do boto utiliza a cor de acentuao denida no tema material.
<android.support.desgn.widget.F1oatingActonButton
androd;d="@+d/fab"
androd:1ayout_width="wrap_content"
androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center"
androd:src="@androd:drawable/c_nput_add"
androd:tint="#fff0O0"
/>

11.7 CardView

O Material Design recomenda a utilizao de cartes (cards) sempre que for preciso
separar a visualizao de determinados elementos. O visual parecido com o que
temos na lista do Google Now em que diversos cartes so mostrados em uma
lista. Cartes tambm podem ajudar a organizar o contedo de aplicativos que
mostram informaes em listas ou grids, como feito no Google Play oferecendo
uma melhor visualizao ao usurio.
A gura 11.11 mostra o visual obtido com os cartes no aplicativo do Google Play
A principal ideia por trs da utilizao dos cartes proporcionar uma interface
consistente em todas as plataformas do Android, desde smartphones, tablets e at
relgios. Sendo assim, criar um carto (card) a tarefa mais simples, o importante
voc entender o real significado de utiliz-lo, que seguir as boas prticas de
interface do Material Design e oferecer um design de interface consistente para
vrios tipos de dispositivos.
358 Google Android -4' edio
g 'ij' 1 0 'q5`
:`f.f. PAGINAWCW = f
Apps novos e atualizados W
.{ *i`
JI
I; } ~, u
'._*1@\_ . af
' :`'<\`
/T;_^='i
ta .

L 1.tmam;
M d Anywhere
Aifbb E Send 2

Gostinho Brasileiro
App:+;:i-10<:rna|~: xiriizffts m

L: .. _ , ~'
_ =,i ,
~S''z= *fo
Muda voz 2 STAR UUSWYGSW
com efeitos WARS' 7 g

Figura 11.11 - Google Play.

Agora vamos falar um pouco de cdigo. A classe do Cardvew distribuda por meio
de uma biblioteca de compatibilidade do Google, portanto declare a seguinte
dependncia no projeto:

app/buiId.gradIe
dependences {

conpile 'con. android . support : ca rdview-v7 : 21 . 0 . +'


}

A classe Cardvew lha de FrameLayout e pode ser utilizada como um continer de


outras views. Utilizando o Cardvew podemos criar uma borda quadrada ou circular
ao redor do layout. Neste prximo exemplo eu adicionei o Cardvew no layout e dentro
dele coloquei um simples Textvew, conforme podemos visualizar no cdigo a seguir.

/res/Iayout/activity_eempIo_tard_view.xmI
<LinearLayout m1ns:android="http://schemas.android.com/apk/res/androd"
xm1ns:card_view="http://schemas.androd.com/apk/res-auto"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:paddng="16dp" androd:orentaton="vertica1">
Captulo 11 I Material Design 359
<android.support.v7.wdget.ardVew
androd:id="@+d/cardvew"
android:1ayout_wdth="100dp" androd:1ayout_heght="10Gdp"
card_vew:contentPaddng="6dp"
card_vew:cardBackgroundCo1or="?attr/colorPrmary">
<TextView
android:text:"@string/he11o_wor1d"
android:1ayout_wdth="wrap_content" androd:layout_heght="wrap_content"
android:1ayout_gravity="center"
androd:tetCo1or="?attr/co1orAccent" />
/android.support.v1.widget.CardVew
SeekBar android:id="@+id/seekBar1"
sty1e="?androdzattr/progressBarSty1eHorzonta1"
androd:1ayout_wdth="11_parent" android:1ayout_heght="wrap_content"
androd:max="100" androd:paddng="20dp" android:progress="0" />
SeekBar android:id="@+d/seekBar2"
sty1e="?androd:attr/progressBarSty1eHorzontal"
androd:1ayout_wdth="11_parent" android:1ayout_heght="wrap_content"
android:ma="100 androd:paddng="20dp" android:progress="0" />

A propriedade card_vew: cardBackgroundCo1or dene a cor de fundo do carto, e a pro


priedade card_vew:contentPaddng dene o espaamento do seu contedo. Tambm
pode ser denida a propriedade card_vew:cardCornerRadus para deixar a borda do
carto arredondada, pois o padro retangular. E por ltimo podemos alterar a
elevao do carto com a tag card_vew:cardE1evaton.
Para voc entender como as propriedades cardCornerRadus e cardE1evaton afetam
o visual do carto, eu adicionei duas views do tipo SeekBar no layout. A primeira
seekBar vai alterar a elevao do carto, e a segunda vai aumentar o arredondamento
das bordas, conforme o cdigo-fonte demonstrado a seguir.

ExempIoCardViewActivity.java
public class EemploCardVewActvity extends AppCompatActivity mplements SeekBar.
OnSeekBarChangeListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.layout.actvity_eemp1o_card_vew);
cardvew = (Cardvew) ndViewById(R.id.cardVew);

@Overrde
360 Google Android - 4 edio
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromser) {
if (seekBar == this.seekBar1) {
cardview.setCardE1evation(progress); // Elevao
} else if (seekBar == ths.seekBar2) {
cardvew.setRadius(progress); // Arredondamento
1

A gura 11.12 mostra o resultado. Veja que como o segundo SeekBar est com o valor
cheio, a borda do carto cou arredondada. Sugiro que voc execute o cdigo no
emulador para visualizar o resultado.

Figura 11.12 - Cardlew.

Nota: o Cardvew muito utilizado nos aplicativos nativos do Android. Se voc notar,
o Google Now composto por cards e o Google Play tambm mostra a lista de
aplicativos com cards. Esse efeito de carto ajuda a separar uma view da outra, pois
cria essas bordas. No aplicativo dos carros que logo vamos comear a desenvolver
vamos utilizar o Cardvew para separar as informaes de um carro para outro.

11.8 RecycIerView
Desde a primeira verso do Android, o Listvew sempre foi a view padro para
criar listas, mas sempre que era necessrio criar efeitos customizados ele tornava
a vida do desenvolvedor um pouco difcil. Outra questo importante que se
o desenvolvedor no soubesse otimizar a rolagem da lista, por meio do padro
VewHo1der, o aplicativo acabava cando com a rolagem da lista prejudicada, de
uma forma no fluida e com travamentos.
Captulo 11 I Material Design 361
Com o surgimento do Material Design, tambm foi criado o Recyclervew, que
o novo Lstvew do Android, e a partir de agora a view recomendada para criar
listas segundo as boas prticas de interface.
O Recyclervew apresenta algumas funcionalidades interessantes:
Suporte a animaes ao adicionar ou remover elementos da lista.
Controle automtico da reutilizao das views (padro VewHo1der).
Permite alterar o gerenciador de layout para renderizar as views como listas,
grids, ou outra forma customizada.
Para usar um Recyclervew, basta inclu-lo no layout e informar uma subclasse de
Recyclervew. LayoutManager, conforme demonstrado a seguir.
Recyclerview recyclervew = (Recyclervew) vi.ew.ndViewById(R.i.d.recyclerview);
Recyclerview.LayoutManager mLayoutManager = new LnearLayoutManager(getActvty());
recyc1erVew. setLayoutManager(mLayoutManager);
recyclerview. setItemAnnator(new Defau1tItenAninator( ) );

O Recyc1erVew tambm utiliza o conceito de adapters para preencher o contedo


da lista, sendo que um adapter deve ser uma subclasse de Recyc1erVew.Adapter.
List planetas = P1aneta.getP1anetas();
recyc1erView.setAdapter(new P1anetaAdapter(ths, planetas, onC1ckP1aneta()));

O conceito simples e segue o mesmo princpio de outras views que usam o


adapter. Mas o Recyclervew tem uma congurao diferente, que o gerenciador
de layout, chamado de LayoutManager. Basicamente o Google separou as respon
sabilidades de quem faz o controle do reaproveitamento das views e de quem
organiza o layout. Por exemplo, o Lstvew faz o scroll das suas views e ainda faz
um gerenciamento para reaproveit-las, e para isso usamos o padro ViewHo1der.

Nota: o padro ViewHo1der sempre foi utilizado para fazer a rolagem de um Lstview.
No cheguei a explic-lo aqui no livro porque vamos utilizar o Recyclerview
daqui para frente, e ele j faz a rolagem de forma eciente. Mas vamos explicar
resumidamente o que o VewHo1der. Quando o Android faz a rolagem em uma
lista com uma grande quantidade de elementos, preciso reutilizar as views
para evitar criar uma nova a cada item. Imagine que exista uma lista com 1.000
linhas. Como o Android mostra no mximo umas 10 views por vez, podemos
criar apenas 10 views e reutiliz-las ao fazer a rolagem, em vez de criar 1.000
objetos do tipo view. Isso otimiza a memria e deixa a rolagem da lista uida.
Com a utilizao do Recyclervew, a implementao desse padro feita de forma
transparente ao desenvolvedor.
362 Google Android - 4 edio
O Recylerview na verdade um componente especializado no reaproveitamento das
views, ou seja, ele recicla as views e implementa automaticamente o padro Viewolder,
para garantir um bom desempenho ao fazer rolagem com uma grande quantidade de
itens Mas o Recylervew no sabe desenhar nada na tela, e para isso ele precisa de alguma
subclasse de LayoutManager, a qual responsvel por desenhar e organizar a disposio
das views A lista a seguir mostra algumas das subclasses de Recyc1erView.LayoutManager:

LinearLayoutManager

Organiza as views na vertical ou horizontal. Para ter omesmo comporta


mento do Listvew, basta utilizar este gerenciador de layout.

GridLayoutHanager

Organiza as views em um grid.

StaggeredGridLayoutManager

Organiza as views em um grid que suporta as orientaes vertical e ho


rizontal.

Outra melhoria do Recyclerview no suporte as animaes. Voc pode informar


uma subclasse de Recyc1erView.ItemAnimator que responsvel por animar a lista
quando os dados so alterados, como, por exemplo, quando um item removido
ou adicionado.

recyclervew.setItenAnimator(new Defau1tItemAnimator());

A classe Defau1tItemAnimator lha de Recyc1erView.ItemAnimator e implementa as ani

}.
maes bsicas quando um item da lista adicionado, removido ou movido de
posio. Depois de inserir alguma informao na lista que a fonte do contedo
do adapter voc pode chamar o mtodo notifyItemInserted(idx) para informar que
um item foi adicionado ou o mtodo notifyItemRemoved(id) para informar que um
item foi removido da posio indicada.

P' .
Depois dessa explicao, vamos para a parte prtica. Primeiramente, para utilizar
o Recyclerview preciso declarar a seguinte dependncia:

@ app/bu|Id.gradIe
dependencies {

conpile 'con.android.support:recyclerview-v7:21.6.+'
Captulo 11 1 Material Design 363
A seguir podemos visualizar o arquivo de layout com o Recyclerview.

/res/Iayout/attivity_eempIo_recyc|er_view.xmI
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.Recyclerview
android:id="@+id/recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

O cdigo-fonte a seguir mostra o que a activity precisa fazer, que congurar o


adapter do Recyclerview e o gerenciador de layout. Em nosso exemplo, estou uti
lizando um LinearLayoutManager para criar uma lista. Mas ao executar o exemplo
no emulador, voc ver dois botes na action bar para alternar entre o modo de
visualizaopor lista e grid, assim podemos brincar um pouco com o Recyclerview.

ExempIoRecyclerViewActivity.java

import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Recyclerview;
public class ExemploRecyclerViewActivity extends AppCompatActivity {
private Recyclerview recyclerview;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eemplo_recycler_view);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Recyclerview
recyclerview = (Recyclerview) ndViewById(R.id.recyclerView);
recyclerview.setLayoutManager(new LinearLayoutManager(this));
recyclerview.setItemAnimator(new DefaultItemAnimator());
recyclerview.setHasFixedSize(true);
// Planetas e Adapter
List planetas = Planeta.getPlanetas();
recyclerview.setAdapter(new PlanetaAdapter(this, planetas, onClickPlaneta()))
}

// 0nClick Planeta
private PlanetaAdapter.Planeta0nClickListener onClickPlaneta() {
return new PlanetaAdapter_PlanetaOnClickListener() {
364 ' 4 Ed0
@0verride
public void onClickPlaneta(View view, int idx) {
List planetas = Planeta.getPlanetas();
Planeta p = planetas.get(idx);
Toast.makeTet(getBaseContet(), "Planetaz " + p.n0me, Toast LENGTH_SH0RT) Sh0w();
}

};
}

private Activity getActivity() { return this; }


@0verride
public boolean onCreate0ptionsMenu(Menu menu) { `
getMenuInater().inate(R.menu.menu_exemplo_recycler_view, menu);
return true;
}

@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_linear_layout) [
// Troca o modo de visualizao para lista
recyclerview.setLayoutManager(new LinearLayoutManager(this));
return true;
} else if (id == R.id.action_grid_layout) {
// Troca o modo de visualizao para grid
recyclerview.setLayoutManager(new GridLayoutManager(this, 2));
return true;
}

return super.on0ptionsItemSelected(item);
}

Conforme podemos ver no cdigo da activity utilizar o Recyclerview simples e


no temos muitas novidades. Mas no cdigo do adapter que est todo o se
gredo. Um adapter do Recyclerview utiliza o conceito de Generics do Java (tipos
genricos) portanto os mtodos onCreateViewHolder() e onBindViewHolder() recebem o
tipo genrico declarado na classe, que neste caso a classe interna PlanetaAdapter.
PlanetasViewHolder.

P|anetaAdapter.java

import android.support.v7.widget.RecyclerView;
// Herda de RecyclerView.Adapter e declara o tipo genrico <PlanetaAdapter.
PlanetasViewHolder
Captulo 11 n Material Design 365
public class PlanetaAdapter extends Recyclerview.AdapterPlanetaAdapter.PlanetasViewHolder {
protected static nal String TAG = "livroandroid";
private nal List planetas;
private nal Context context;
private nal Planeta0nClickListener onClickListener;
public interface Planeta0nClickListener {
public void onClickPlaneta(View view, int idx);
}
public PlanetaAdapter(Context context, List planetas, Planeta0nClickListener
onClickListener) {
this.context = context;
this.planetas = planetas; ,
this.onClickListener = onClickListener;
}

@Override
public PlanetasViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Este mtodo cria uma subclasse de RecyclerView.ViewHolder
// Ina`a view do layout
View view = Layoutlnater.from(context).inate(R.layout.adapter_planeta,
viewGroup, false);
// Cria a classe do ViewHolder
PlanetasViewHolder holder = new PlanetasViewHolder(view);
return holder;
}

@Override
public void onBindViewHolder(nal PlanetasViewHolder holder, nal int position) {
// Este mtodo recebe o indice do elemento, e atualiza as views que esto
// dentro do ViewHolder
Planeta c = planetas.get(position);
// Atualiza os valores nas views
holder.tNome.setText(c.nome);
holder.img.setImageResource(c.img);
// Click
if (onClickListener != null) {
holder.itemView.set0nClickListener(new View.0nClickListener() {
@Override
public void onClick(View v) {
// Chama o listener para informar que clicou no Planeta
onClickListener.onClickPlaneta(holder.view, position);
1

});
}

}
366 Google Android - 4 edio
@0verride
public int getItemCount() {
return this.planetas != null ? this.planetas.size() : 6;
}

// Subclasse de RecyclerView.ViewHolder. Contm todas as views.


public static class Planetasviewolder extends RecyclerView.ViewHolder {
public Textview tNome;
Imageview img;
ProgressBar progress;
private View view;
public PlanetasViewHolder(View view) {
super(view);
this.view = view;
// Cria as views para salvar no ViewHolder
tNome = (Textview) view.ndViewById(R.id.tNome);
img = (lmageview) view.ndViewById(R.id.img);
progress = (ProgressBar) view.ndViewById(R.id.progress);
}

O resultado do exemplo pode ser visualizado na gura 11.13, que mostra a visu
alizao no formato de lista e grid.

I Wl
z rtof ef lJ
Mercrio Mercrio 0 Vnus

li
~rm i
T"3 p Jupiter ` Saturno
` I p p,
A' Mam* i 8 Urano O Netun
E

-~@~ ~'UP'1f r Pluto


V Saturno
zu.. `i
FgmnlH3-RydmVkwcmnUaegd
Agora vamos entender um pouco o cdigo-fonte desse exemplo, que pode ser um
POUCO C0mPl1C3d0 3 PYIUCIPIO, 111215 Simples logo que voc se familiarizz1r.O que
Captulo 11 I Material Design 367
muitos desenvolvedores acham complicado no cdigo o fato de utilizar classes
internas e tipos genricos, mas isso Java e no Android.
O Recyclerview basicamente pede que voc crie um ViewHolder para armazenar suas
views. Isso feito no mtodo onCreateViewHolder(), o qual chamado uma nica vez.
Esse ViewHolder utilizado internamente pelo Recyclerview para fazer o controle e
reaproveitamento das views. Para preencher as views com as informaes, processo
conhecido como " bind ", chamado o mtodo onBindViewHolder(), o qual chamado
N vezes conforme a quantidade de elementos da lista.
A classe PlanetaAdapter que criamos herda de RecyclerView.Adapter e informa no tipo
genrico a classe interna PlanetasViewHolder criada dentro do adapter. Basica
mente temos uma classe interna esttica que deve herdar de RecyclerView.Adapter.
public class PlanetaAdapter extends Recyclerview.Adapter<PlanetaAdapter.
PlanetasViewHolder> {

Como foi declarado na classe o tipo genrico , o


mtodo onCreateViewHolder() retorna esse mesmo tipo.
public PlanetasViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Ina a view do layout
View view = Layoutlnater.fron(contet).inate(R.layout.adapter_planeta,
viewGroup, false);
// Cria a classe do ViewHolder
PlanetasViewHolder holder = new PlanetasViewHolder(view);
return holder;
}

neste momento que o layout do adapter inado, o qual contm a foto e o


nome do planeta. Se quiser, abra o arquivo /res/layout(adapter_planeta.xml no edi
tor do Android Studio, pois um layout simples. O mtodo onCreateViewHolder()
deve retornar o ViewHolder, para garantir que o padro ViewHolder utilizado, com o
objetivo de otimizar a rolagem da lista. Com o Listview, esse padro era utilizado
apenas por desenvolvedores que conheciam as boas prticas de desenvolvimento,
mas agora ele automtico, pois o Recyclerview exige que voc faa isso.
O mtodo onBindViewHolder() tambm recebe o tipo genrico no argumento e deve
preencher as views com os valores do objf to.
public void onBindViewHolder(PlanetasViewHolder holder, int position) {
// Este mtodo recebe o indice do elemento e atualiza as views.
Planeta c = planetas.get(position);
// Atualiza os valores nas views
holder.tNome.setText(c.nome);
1c
368 Google Android - 4 edio
holder . img . setImageResource(c . img);
// Dene o evento de clique, e delega para um listener.
if (onClickListener != null) {
holder.itemView.set0nClickListener(new View.0nClickListener() {
@0verride
public void onClick(View v) {
// Chama o listener para informar que clicou no Planeta
onClickListener.onClickPlaneta(holder.view, position);

});
}

O terceiro e ltimo mtodo que precisamos implementar nesse adapter o


getItemCount(), o qual retorna a quantidade de linhas.
public int getItemCount() {
return this.planetas != null ? this.planetas.size() : 0;
}

Outro conceito importante sobre o Recyclerview que ele no trata os eventos de


clique como o Listview fazia, justamente por ser um componente especializado
no reaproveitamento (recycle) de views. Portanto, voc precisa criar sua prpria
interface de listener para tratar esse evento.
Entender isso pura programao, pois utilizada uma interface para fazer a
comunicao entre dois objetos distintos. Por isso, o adapter define a interface
Planeta0nClickListener para a qual ele delega os eventos de clique. A classe da
activity implementa essa interface e avisa ao Recyclerview que ela est interessada
em receber os eventos de clique.
// Congura o adapter com a lista de planetas e o listener de clique
recyclerview.setAdapter(new PlanetaAdapter(this, planetas, onClickPlaneta()));
// Depois implementa o mtodo onClickPlaneta(view,idx)
private PlanetaAdapter.Planeta0nClickListener onClickPlaneta() {
return new PlanetaAdapter.Planeta0nClickListener() {
@0verride
public void onClickPlaneta(View view, int idx) {
// clicou no planeta
}

};
}

Criar esse tipo de interface de retorno frequentemente conhecido por desenvol


vedores de software como interfaces de callback, listener ou delegate.
Captulo 11 1 Material Design 369
Dica: outro padro de design muito conhecido no Material Design o Swipe to
refresh, que permite atualizar os dados da lista quando o usurio faz o gesto de
rolagem para baixo. Quando formos desenvolver o aplicativo dos carros, vamos
implementar isso.

11.9 Efeito de revelao (Reveal Effect)


No Material Design uma animao muito utilizada o efeito de revelao, chama
do de Reveal Effect. Essa animao faz a view aparecer aumentando gradativamente
o seu tamanho ou desaparecer dim_inuindo o seu tamanho.
Para demonstrar como utilizar essa animao, criei a classe RevealEffect com os
mtodos show() e hide().

ReveaIEffect.java
@rrgerAp(Buud.vERs1oN_coDEs.LoLL1PoP)
public class RevealEffect {
public static void show(View view, long animDuration) {
// Centro da view
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
// Dene o arco para a animao
int nalRadius = Math.ma(view.getwidth(), view.getHeight());
// Cria a animao
Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, nalRadius);
// Inicia a animao
view.setVisibility(View.VISIBLE);
anim.setDuration(animDuration);
anim.start();
}

public static void hide(nal View view, long animDuration) {


// Centro da view
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
// Dene o arco para a animao
int initialRadius = view.getHidth();
// Cria a animao
Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, G)
// Quando a animao terminar esconde a view
anim.addListener(new AnimatorListenerAdapter() {
370 Google Android - 4 edio
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.INVISIBLE);
1

});
// Inicia a animao
anim.setDuration(animDuration);
anim.start();
1

Para testar esse efeito de animao, criei um layout simples com dois botes que
chamam os mtodos show() e hide() dessa classe. A imagem do planeta que est no
centro do layout vai receber o resultado da animao. A gura 11.14 mostra a anima
o de revelao executando no emulador, mas o ideal voc conferir no emulador.
No cdigo-fonte da activity a tarefa necessria obter a view que precisa ser

E11snow
. .1.1l1 |Fsnow~uma
animada e chamar os mtodos para mostrar ou esconder a view

ii snow
' I1-nos 'mos

Figura 11.14 - Efeito de revelao.

ExempIoRevea|EffectActivity.java

public class EemploRevealEffectActivity extends AppCompatActivity {


@0verride

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eenplo_reveal_effect);
Captulo 11 I Material Design 371
ndViewById(R.id.btShow).set0nClickListener(onClickShow());
ndViewById(R.id.btHide).set0nClickListener(onClickHide());
1

private View.0nClickListener onClickShow() {


return new View.0nClickListener() {
@0verride
public void onClick(View v) {
View img = ndViewById(R.id.img);
RevealEffect.show(img, ZGGB);
}

};
}

private View.0nClickListener onClickHide() {


return new View.OnClickListener() {
@0verride
public void onClick(View v) {
View img = ndViewById(R.id.img);
RevealEffect.hide(img, 2666);
}

};
}

11.10 Extraindo as cores de uma gura


Como j foi dito, o Material Design muito baseado em cores. Por isso foi criada
a classe Palette com o objetivo de extrair as principais cores de uma gura. A ideia
de extrair as principais cores da imagem para colorir o layout e views com as
cores da imagem.
Imagens no Android so representadas pela classe Bitmap. Assim, para extrair a
paleta de cores de uma gura, utilize o seguinte cdigo:
Bitmap bitmap = . . .;
Palette p = Palette.generate(bitmap);

Preferencialmente devemos utilizar o mtodo generateAsync(bitmap,listener) para


extrair as cores, pois ele assncrono e no vai travar a UI Thread.
Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
public void onGenerated(Palette palette) {
// Fazer algo com as cores aqui
}

});
Google Android "' 45 Edio
Depois que a paleta de cores extrada da gura, podemos tentar ler as tonali
dades de cores que foram extradas da gura, que so: Vibrant, Vibrant Dark, Vibrantligln,
Muted, Muted Dark e Muted Light. Para fazer isso, basta chamar algum mtodo da classe
Paiette, como o mtodo getvibranttoior(defaul.tCo1or).
Se quiser brincar um pouco com a paleta de cores, execute no emulador o exemplo
que est disponvel no projeto deste captulo. Nele, pinto vrios Textviews que esto
na tela com as cores que so extradas de uma gura do planeta Terra.

11.11 Animaes com item compartilhado entre duas activities


No captulo 9, sobre animaes, aprendemos a criar uma animao ao navegar de
uma activity para outra. A boa notcia que a partir do Android 5.0 (Lollipop) foram
criados mais dois mtodos que so muito interessantes, pois permitem compartilhar
elementos entre duas activities e animar a transio desse elemento compartilhado.

makeSceneTransitionAnimation (Activity activity, View sharedE1ement, String


sharedE1ementName)

Congura uma animao customizada compartilhando uma view entre


duas activities. O parmetro sharedE1enentName a chave desta vievxg que deve
existir em ambas as activities. Para utilizar esse mtodo, a activity precisa
habilitar a funcionalidade FEAruRE_Acr1v1rY_rRANs1r1oNs.

makeSceneTransitionAnimation (Activity activity, Pair...<View, String>


sharedEienents)

Idem ao mtodo anterior, mas permite passar uma lista de views a serem
compartilhadas durante a animao de transio.

Nota: o aplicativo do Google Play usa e abusa deste novo recurso. Na pgina
inicial que lista os aplicativos da loja, ao clicar em algum aplicativo, a nova
activity e aberta e o icone do aplicativo move-se at a nova posio.

Para utilizar esse novo recurso de animao, a transio de janelas precisa estar
habilitada, o que pode ser feito congurando 0 AndroidManifest.xml ou dinami
camente no cdigo de cada activity
Basicamente podemos definir a animao de entrada (enter) ao abrir a tela, a
animao de sada (exit) ao sair da tela e ainda compartilhar elementos (shared
elements) entre as telas.
Captulo 11 I Material Design 373
Para habilitar o modo de transio de telas, temos duas formas. A primeira
congurar o atributo windowContentTransitions para true na congurao do tema,
como demonstrado a seguir.
<styie nane="BaseAppThene" parent="android:Theme.Materia1">

<iten nane="android:windowContentTransitions">true
||n

As animaes de transio suportadas no Android 5.0 so:


explode - Move uma view para dentro ou fora da tela.
' slide - Move a view para dentro ou fora da tela, fazendo um movimento
lateral.

fade - Utiliza a propriedade aipha das views para controlar a transparncia


a m de fazer a transio.
No arquivo de manifesto, efeitos de transio podem ser congurados de forma
global para todo o tema, como por exemplo:
<styie nane="BaseAppThene" parent="android:Thene.Materia1">

<iten nane="android:windowContentTransitions">true

<iten name="android:windowEnterTransition">@transition/ep1ode
<item nane="android:windowExitTransition">@transition/exp1ode

Outra opo habilitar o modo de transio entre activities pelo cdigo. Basta
chamar o mtodo getllindow( ) . requestFeature(window. FEATURE_CONTENT_TRANSITIONS). Eu
costumo utilizar essa opo. S tenha ateno, pois para a transio funcionar esta
chamada precisa ser feita na primeira linha de cdigo do mtodo onCreate(bundie) da
activity e deve ser includa em ambas as activities, origem e destino da animao.
Mas chega de teoria, vamos praticar um pouco de cdigo. Copie o projeto
Hellollctivityiransition que zemos no captulo 9, sobre animaes, pois ele ser a base
para continuarmos. No captulo 9 j aprendemos a fazer transies de transparn
cia, chamadas de fade_in e fade_out, assim como transies de movimento, chamadas
de sIide_in e sIide_out.

Antes de continuar, remova todo o cdigo de animao que zemos no projeto


dos planetas. Assim podemos partir de um cdigo limpo para brincar com as
novas animaes do Android Lollipop.
374 Google - 4'
Para comearmos, o cdigo da MainActivity pode car simples assim:

] MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClickPlaneta(View view) { \
Intent intent = new Intent(getBaseContet(), PlanetaActivity.class);
startActivity(intent);
}

E o cdigo da PlanetaActivity pode car assim:

PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

Desta vez, para fazer a navegao de telas, vamos utilizar o mtodo


nakeSceneTransitionAnimation(activity) da classe Activity0ptions, porm escolhemos a
classe Activity0ptionsConpat para manter a compatibilidade.

MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
// Habilita a transio de telas (deve ser a primeira linha de cdigo)
getHindow().requestFeature(Hindow.FEATURE_CONTENT_TRANSITIONS);
super . onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
l
Captulo 11 n Material Design 375
public void onClickPlaneta(View view) {
Intent intent = new Intent(getBaseContet(), PlanetaActivity.class);
// Efeito padro de cross-fading
Activity0ptionsCompat opts = Activity0ptionsCompat.nakeSceneTransitionAnimation(this);
ActivityCompat.startActivity(this, intent, opts.toBundle());
}

1
\

Dica: neste exemplo, estou habilitando a transio de activities dinamicamente


no cdigo. O mtodo requestFeature(FEATURE_CONTENT_TRANSITIONS) deve ser chamado
na primeira linha do mtodo onCreate(bundle) de ambas as activities.

Na activity do planeta, tambm d.evemos habilitar a transio de telas na primeira


linha do mtodo onCreate(bundle).

PIanetaActivity.java
public class PlanetaActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
getNindow().requestFeature(Nindow.FEATURE_CONTENT_TRANSITIONS);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_planeta);
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}

Pronto! Execute o projeto e veja o efeito da animao. Por padro, ao habilitar a


animao de transio de telas, o efeito de sumir e aparecer (cross-fading) criado
automaticamente.
Agora vamos aprimorar o efeito de transio e animar a figura do planeta, con
forme demonstrado na figura 11.15. O objetivo redimensionar a foto do planeta
Terra com uma harmoniosa animao durante a transio de activities, algo
comum em aplicativos que seguem o Material Design.
376 Google Android - 4 edig
0 3 Gi v E 93 Ie 9
HelIoActivityTransIton <'

Figura 11.15 - Animao entre duas activities.

Para criar o efeito de transio, precisamos informar ao Android que existe


um item compartilhado entre as activities. Isso feito adicionando o atributo
androd:transtonName view que voc deseja compartilhar.

/res/layout/activity_man.xmI
<FrameLayout m1ns:android="http://schemas.androd.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent">
<ImageVew androd:d="@+d/img"
androd:transtonName="@string/transtion_key"
android:1ayout_gravty="center"
androd:src="@drawable/p1aneta_03_terra"
androd:1ayout_width="100dp" androd:1ayout_heght="100dp"
androd:onC1ck="onC1ckP1aneta" /

A chave @strng/transton_key pode ter qualquer texto e serve apenas para identi
car esta view:

P /res/values/strings.mI

<strng name="transti.on_key">mg_trn5t0n key</51;rng>


Captulo 11 I Material Design 377
Para o efeito funcionar, no layout da activity do planeta congure o Inageview de
destino com a mesma chave de transio.

/res/layout/activity_pIaneta.xmI
<FraneLayout . . .>
<ImageView android:id:"@+id/img"
android:transitionNane="@string/transition_key" . . ./>

Por ltimo, para habilitar o compartilhamento das views durante a transio de


telas, vamos passar a view e sua chave de compartilhamento como parmetros
para o mtodo makeSceneTransitionAnination(activity,view,shareKey), conforme de
monstrado a seguir.

MainActivty.java
public class MainActivity extends AppConpatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
// Habilita a transio de telas (deve ser a primeira linha de cdigo)
getHindow().requestFeature(Nindow.FEATURE_CONTENT_TRANSITIONS);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}

public void onClickPlaneta(View view) {


Intent intent = new Intent(getBaseContext(), PlanetaActivity.class);
// Conpartilha a gura com o efeito de transio
Imageview ing = (InageView) ndViewById(R.id.ing);
String key = getString(R.string.transition_key);
Activity0ptionsConpat opts = Activity0ptionsConpat.nakeSceneTransitionAnination(this,ing,key);
ActivityConpat.startActivity(this, intent, opts.toBundle());
}

Pronto, isso tudo. Agora execute o cdigo novamente e veja a mgica acontecer.
Caso seja necessrio, fazer a transio de mais uma view ao mesmo tempo tam
bm possvel, pois a assinatura do mtodo makeSceneTransitionAnination(. . .) pode
receber uma lista de android.support.v4.util.Pair<View, String>.
nakeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElenents)

Ento digamos que no layout exista um Textview com o nome do planeta e a gura
Nesse caso, poderamos fazer a transio de telas com este cdigo:
378 Google Android - 4 edig
Intent intent = new Intent(getBaseContet(), PIanetaActivity.c1ass);
Pair p1 = Pair.create(ndViewById(R.id.img), getString(R.string.img_transition_key));
Pair p2 = Pair.create(ndViewById(R.id.img), getString(R.string.tit1e_transition_key));
ActivityOptionsCompat opts = Activity0ptionsCompat.makeSceneTransitionAnimation(this, p1, p2)
Activitytompat.startActivity(this, intent, opts.toBundIe());

Se quiser conferir o resultado, abra o projeto He|loActivityl`ransition-Pair disponvel nos


exemplos do livro.

11.12 Compatibilidade com verses anteriores


Ao desenvolver aplicativos, uma importante deciso qual ser a verso minima
que voc ir suportar. Durante o livro voc vai aprender diversas tcnicas para
manter a compatibilidade, mas voc pode perceber que na prpria API j existem
diversas classes como ViewPage, ActionBar de compatibilidade, Recyclerview, Cardview
etc. que j esto disponveis para as verses mais antigas.
Uma dica sempre procurar pela palavra "compat" quando voc for precisar de
alguma coisa, e provavelmente vai encontrar o que precisa.
Outro recurso muito utilizado criar pastas com os qualicadores por API Level,
como /res/layout e /res/layout-21 (para API Level 21 ou superior).
E se voc no puder customizar os arquivos XML utilizando esses qualicadores,
sempre possvel executar um cdigo customizado, fazendo o teste em tempo
de execuo.
// 0 Android 5.0 ou superior
if (Buil.d.VERSION.SDK_INT >= BuiId.VERSION_CODES.LOLLIPOP) {
// Pode executar uma nova API
} else {
// Implementa determinada funcionalidade de outra maneira.
1

11.13 Links teis

Neste CQPITUIO, estudamos o bsico sobre o que o Material Design. Recomendo


que voce olhe a documentao oficial para complementar seus estudos.
O sitedo Android Design rico em contedo e contm muitas coisas sobre o
Material Design, vale a pena conferir.
Captulo 11 n Material Design 379
Google Design
http://www. google. com/design/
Android Design
https://developer: android.com/design/
Android Design - Material Design
http://wwugooglie.com/design/spec/material-design/
Android Design - Especicao do Material Design
https://developer android.com/design/material/

Blog do Google Developers - Post sobre Material Design

http://android-developers.blogspot.com.br/2014/10/implementing-material-desigm
-in-your: html

Android Training - Creating Apps with Material Design


https://developer android.com/training/material/
Android Training - Dening Custom Animations
https://developer android. com/training/material/animations.html
Android Developers Blog - Android Design Support Library
hp://android-developers. blogspot. com.br/2015/05/android-design-support-library.html
` cAPruLo 12
Toolbar
/`4 'X

,
\z

Com o lanamento do Android 5.0 (Lollipop) foi criada a Toolbar, uma view
especial que pode ser inserida em qualquer lugar do layout e tem as mesmas
funcionalidades da action bar.
A Toolbar est sendo muito utilizada para criar aplicativos seguindo as boas
prticas de interface do Material Design.

12.1 Introduo Toolbar


Segundo a documentao do Android, a Toolbar uma generalizao da action
bar, e sua vantagem que ela uma view que pode ser inserida em qualquer lugar
do layout e tem as mesmas funcionalidades da action bar.
A action bar um elemento xo associado activity e sempre ca no topo do
layout. j a Toolbar uma view sendo assim, ela pode ser inserida onde voc de
sejar, inclusive ela pode aparecer mais de uma vez no layout. A gura 12.1 mostra
um exemplo em que a Toolbar poderia ser utilizada em uma janela de dialog, pois
ao abrir essa janela podemos ter a mesma barra de aes que temos na action bar.

Figura 12.1 - Conceito da Toolbar

380
Captulo 12 I Toolbar 331
Outro exemplo de utilizao da Toolbar pode ser visto na gura 12.2, que mostra
um contato selecionado no aplicativo e a Toolbar mostra as aes que podemos
fazer com aquele contato. Como podemos ver, a vantagem de utilizar a Toolbar
que ela uma view parecida com a action bar, portanto os usurios vo reconhe
cer esse padro e vo se sentir familiarizados com ele. AToolbar pode mostrar as
principais aes com os actions buttons e inclusive ter o menu flutuante action
overflow com as opes menos utilizadas.

Figura 12.2 - Exemplo de Toolbar

Como Toolbar uma view comum os aplicativos criarem animaes para mover
a Toolbar ou redimension-la. Um exemplo disso o aplicativo da agenda e ligao
do Android 5.0. A gura 123 mostra um contato da agenda e ao seleciona-lo (clicar
nos trs pontinhos) uma view com a Toolbar aparece na tela com uma animao
de baixo para cima ( direita da figura).

li
Q, Type a name or phone number

T | _l 5
Ricardo Lecheta ea
fi Mobile, O mins ago

, p ,W (999) 999 9999 E


` 1 Mobile
Ii (888)
Home sas-asas1

Figura 12.3 - Animao da Toolbar ao fazer a rolagem.

Nesse momento, se voc tocar na view da Toolbar e arrasta-la para cima, todo
o contedo aparece por cima da tela original, at a Toolbar se fixar no topo
382 Google Android - 4' edigo
(Figura 114). Caso o contato tenha muitas informaes cadastradas e voce conti
nuar fazendo a rolagem para cima, aToolbar redimensionada com uma animao
e fica pequena igual action bar ( direita da figura).
Egz exemplo mostrou o poder e a flexibilidade da Toolbar se comparada action
bar. e acredito que tenham cado claras as diferenas. O recomendado voc
brincar como esse aplicativo no emulador ou no seu smartphone para visualizar
as animaes de movimento que so muito fluidas.

999\ 999 9999 G


K.. *`\" '.

8:i:i\ 1588 8888 D

5 f E T 777)?77~77?? D
zz wa 9999
-\` \

\885\ 885 8888


D \oob) o boe E3
fl '.~.>eZzi\.;q:iiaz. ri
H ie*he(.\gniau com
i Hi:

Figura 12.4 - Animao da Toolbar ao fazer a rolagem.

Nota: como a Toolbar uma view, podemos executar animaes de transparncia


com o objetivo de faze-la aparecer e desaparecer. Tambm podemos movimentar
a Toolbar conforme desejado. No Material Design, comum aplicativos que
utilizam listas monitorarem a rolagem da lista a m de esconder a Toolbar para
dar tnais espao ao contedo. Os aplicativos do Google l/O 2014 e Google Play
utilizam esses conceitos.

12.2 Utilizando a Toolbar como a action bar

Agora que sabemos o basico sobre a Toolbar, vamos estudar um pouco de codigo.
A maneira mais simples de utilizar a Toolbar desligar a action bar nativa, inserir
a Toolbar no layout e configura-la para tomar o lugar da action bar. lsso e simples
e consiste em trs passos:
Captulo 12 I Toolbar 333
1. Para desligar a action bar, congure o aplicativo ou a activity desejada para
utilizar o tema Theme.AppCompat.NoActionBar ou Theme.AppCompat.Light.NoActionBar.

2. Adicione a Toolbar em algum lugar do layout.


3. Utilize o mtodo setSupportActionBar(toolbar) para transformar a Toolbar na
action bar.

Para aprendermos isso na prtica, crie um projeto chamado HeI|oTooIbar no Android


Studio e configure-o com o tema App(ompat. A classe android .widget.Too1bar foi criada
a partir do Android 5.0, mas felizmente podemos utilizar a biblioteca de compa
tibilidade v7, que contm a classe android.support.v7.widget.Too1bar.

app/buiId.gradIe
dependencies {

compile 'com. android . support: appcompat-v7 : 22 . 1 . 0'


}

Neste prximo exemplo, vamos desabilitar a action bar da MainActivity e substitu


-la pela Toolbar. Para comear, crie o tema AppTheme.NoActionBar.

/res/values/styIes.xmI

<styie name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">


<item name="co1orPrimary">@co1or/primary
<item name="co1orPrimaryDark">@co1or/primary_dark
<item name="coiorAccent">@co1or/accent

<styie name="AppTheme.NoActionBar" parent="Theme.AppConpat.Light.NoActionBar">


<item name="co1orPrimary">@co1or/primary
<item name:"co1orPrimaryDark">@co1or/primary_dark
<item name="co1orAccent">@co1or/accent

No arquivo de manifesto, congure a MainActivity para utilizar o tema AppTheme


NoActionBar.

AndroidManifest.xm|
<manifest . . . />
<app1ication android:theme="@sty1e/AppThene" >
384 Google Android - 4' edio
<acti.vity android:nane= .Ha'tnActivity" androtd:thene="G$tY1/ PPTNHQ-N0Bir'

/activty

</nanifest

Essa con gurao vai remover a action bar da activity; portanto precisamos inserir
a Toolbar no layout. Para isso. eu gosto de criar um arquivo de layout separado
conforme demonstrado a seguir.

/res/layout/intIude_tooIbar.xm| c
android.support.v7.widget.Toolbar n1ns:android="http://schemas.android.com/apk/res/android"
androd:id='@+id/toolbar"
android:1ayout_heght=wrap_content" androd:layout_wdth="match_parent"
androd:nnHeight="?attr/actonBarSize" android:background="?attr/co1orPrinary" /

Nesse arquivo a altura da Toolbar est configurada como a altura da action bar. Isso
feito acessando o recurso nativo ?attr I actionBarSze. A cor de fundo da Toolbar foi
denida pelo recurso ?attr/co1orPrinary, ou seja, a cor primria do tema Material.

Nota: voc deve ter reparado que a notao ?attr utilizada para acessar um
recurso nativo do Android. lsso interessante. pois podemos obter os valores
padres da plataforma.

Depois de criar o arquivo /res/Iyout/include_tooIlanxml, podemos inclu-lo no


layout da activity Isso feito com a tag <nc1ude.

a /res/layout/attivity_main.xml

<LinearLayout xmlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent">

inc1ude layout:"@1ayout/nc1ude_too1bar" /
<TetView

"<l"Cl1lY0Ut_Wdth="wrap_content" android:1ayout_height="wrap_content"
android:tet="@string/he11o_wor1d" /
</LinearLayout

Por Ulm. 110 codigo da activity e preciso chamar o mtodo setSupportActionBar(too1bar)


para transformar a Toolbar na action bar. Coin isso o Android vai mostrar a 'lbolbalf
que 15@flm0S 110 l21)'0UI. porem todos os mtodos da classe Acti.onBar continuam
funcionando como antes. Podemos dizer que esse um artifcio tcnico interessante.
Captulo 12 I Toolbar 335
MainActivity.java

@Override l
public class MainActivity extends AppCompatActivity {

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Aqui a mgica (A Toolbar ser a action bar).
Toolbar toolbar = (Toolbar) ndViewById(R.id. toolbar);
setSupportActionBar(toolbar);
}

@Override
public boolean onCreate0ptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean on0ptionsItemSelected(Menultem item) {
// Trate os eventos da action bar normalmente aqui.
}

l1i Ili|l
Pronto, isso tudo. A gura 12.5 mostra o resultado. Parece que uma action bar,
mas na verdade uma Toolbar. Inclusive o boto do smile funciona normalmente.

Hello world! p
l

Lr__f_____A,___ ___ ____ _ ,_ _ _ __ __ _ _ __ ___;

Figura 12.5 - Toolbar no lugar da action bar


335 Google Android - 4 edio
A vantagem de utilizar a Toolbar dessa forma no lugar da action bar que a mesma
API da classe ActionBar pode ser utilizada, ento os mtodos onCreate0ptionsMenu(menu)
e on0ptionsItemSelected(HenuItem item) continuam funcionando da mesma forma.
Tambm podemos chamar o mtodo getSupportActionBar( ) para recuperar o objeto
da ActionBar e chamar qualquer mtodo que quisermos.

Nota: utilizar a Toolbar como a action bar nos permite continuar utilizando os
mesmos mtodos com que j estamos acostumados da classe ActionBar. Com a
vantagem de que podemos inserir a Toolbar em qualquer lugar do layout e ainda
brincar de anim-la, pois ela uma view como outra qualquer.

12.3 Utilizando a API da Toolbar (modo standalone)


Outra forma de utilizar a Toolbar adicion-la no layout e usar a sua prpria API.
Neste caso no importa se voc desligou ou no a action bar nativa, pois inclusive
ambas podem coexistir.
Para este prximo exemplo, crie o projeto He|IoTooIbarStandaIone ou abra o projeto
pronto disponvel no material de download do livro. Para utilizar a Toolbar basta
adicion-la no layout como j zemos. Mas desta vez, no cdigo da activity vamos
usar a API da prpria classe Toolbar.

MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) ndViewById(R.id.toolbar);
toolbar.setTitle(getString(R.string.app_name));
toolbar.setNavigationIcon(R.drawable.ic_up);
// Trata o evento de clique na Toolbar
toolbar.set0nMenuItemClickListener(new Toolbar.0nMenuItemClickListener() {
@0verride

public boolean onMenuItemClick(MenuItem item) {


// Trate os eventos da action bar normalmente aqui.
} T);
Captulo 12 I Toolbar 387
// Up Navigation
toolbar.setNavigationOnC1ickListener(new View.0nC1ickListener() {
@Override
public void onClick(View v) { nish(); }
});
// Ina os itens de menu na Toolbar
toolbar.inateMenu(R.nenu.menu_nain);
}

private Context getContext() { return this; }


}

No layout da activity vamos adicionar a Toolbar. Para brincar, ela foi inserida na
parte inferior da tela.

/res/layout/activity_main.xml
<FraneLayout . _ .>
<TextView . . ./>

<include layout:"@1ayout/inc1ude_too1bar_1ight"
android:1ayout_width="match_parent" android:1ayout_height="wrap_content"
android:layout_gravity="botton" />

O resultado pode ser visto na gura 12.6. Veja que a activity mostrou a action bar
normalmente l em cima. Na parte inferior podemos ver a Toolbar, que inflou os
itens de menu e tambm ativou o up navigation.

Exemplo de Toolbar l em babo eActlon Bar


normalmente em cima.

Figura 12.6 - Toolbar inserida na parte inferior do layout.


333 Google Android - 4 edio
12.4 Links teis

Neste captulo, estudamos o bsico sobre a classe Toolbar e para continuar seus
estudos selecionei alguns links da documentao ocial.
Toolbar - Documentao da API

https://developer android. com/referencv/android/support/v7/widget/Toolban html


Android Design - Toolbar

http://www. goo gl e. com/desi gn/spec/com ponents/tool bars. html

Blog do Google Developers - Post sobre a Toolbar

http://android-developers. blogspot. com.br/2014/10/appcompab v2l-material-desigm


-for-pre. html

Blog do Google Developers - Post sobre Material Design

hrtp://android-developers.blogspot.com.br/2014/10/implementing-material-desigm
-in-younhtml
cAPruLo 13

Navigation Drawer
\
\

,lp j

Neste captulo, comearemos o desenvolvimento do projeto dos carros, no qual vamos


praticar os conceitos aprendidos at o momento e aprender muito mais. Na sequncia
dos prximos captulos, o aplicativo ser incrementado com novas funcionalidades.
Neste momento, vamos criar o projeto e preparar a estrutura bsica para utilizar
o Navigation Drawer (menu lateral deslizante) como o padro de navegao.

13.1 Criando o projeto


Para comear a brincadeira, crie o projeto conforme a gura 13.1. Preenchi o campo
Company Domain com Iivroandroid.com.br e recomendo que voc faa o mesmo, pois assim
o pacote gerado no seu projeto ficar com o mesmo nome dos meus cdigos,
facilitando qualquer copy-paste que voc venha a fazer ao seguir os exemplos.
Create New Project

Configure your new prtzoject

Application name: {&;c5 l


Qompany Domain: l"livroandmid.com.lar V gl
Pacigt mim!! kr arm i.::.;ur\:"cd ;.3r': Edi!
pfOjCOC0|'Z l R:\Java\workspaces\wurkspace-livro-android-4\Cazqs 7

i_ lr`.`T_} LA

Figura 13.1- Criando 0 projeto dos carros.

389
390 Google Android - 4= edio
No wizard selecione o Android 23 como a verso mnima (Figura ]3.2). Clique
em Next e na prxima pgina do vvizard escreva Carros no campo Title.

re Create New Project j j jp

Seect the form :;tcrt your app wifi run on


., _,.,. . ... 'H'

Phone and Tebiet `


M.,..,,,,.. Sat Avi sz zzama l 2 ' E l M H
Lower API levels target more devices, but have fewer features cvaiabie.
By torgeting AW 9 and later, your app will run ou apprexrmntety 99,3$
of the devices that are actrve on the Googie Play Store. Help me choose.
C] TV
h

.zf._=.r L5e:~.s__,

Figura 13.2 - Criando 0 projeto dos carros.

Depois de criar o projeto, como selecionamos o Android 23 (API Level 9) para


a verso mnima suportada pelo aplicativo, 0 Android Studio j congurou o
projeto para utilizar a biblioteca de compatibilidade v

app/buiId.gradIe

dependencies {
conpile 1eTree(dir: '1ibs', include: ['*.jar'])
// Ativa a dependncia da biblioteca de compatibilidade v7
conpile 'con.android.supportzappconpat-v7:22.1.1'
}

O projeto tambm foi congurado para utilizar o tema AppConpat. Se por acaso o
wizard no zer isso automaticamente, faa estas alteraes:

/res/values/sty|es.m|
<I"ESOUI'CS>

<sty1e nane="AppThene" parent="Thene AppConpat.Light.DarkActionBar"


Captulo 13 I Navigation Drawer 391
Dica: para facilitar a digitao do cdigo-fonte no Android Studio e a insero
dos imports no cdigo, vamos ativar os imports automticos. Abra o menu File
> Settings, entre no menu Editor > Auto Import e deixe ligada a opo Add unambiguos
imports on the y.

13.2 Customizando as cores do tema Material


Conforme j estudamos anteriormente, podemos customizar as cores do tema
Material facilmente, portanto crie o arquivo com as cores.

/res/values/coIors.xmI
<|'SOUICES>

<color name="primary">#03A9F4/color
<color name="primary_dark">#01579B
<color name="accent"#F44336
<color name="control_highlight"#B3E5FC

<color name="transparent">#0000
<color name="white">#fff
<color name="black">#000
<color name="blue">#00f
<color name="red">#f00
<color name="green">#@f0
<color name="gray">#eee

No arquivo de cores j aproveitei e criei algumas cores comuns que podemos utili
zar no aplicativo. Para prosseguir, congure o tema com as cores do tema Material
que so os atributos colorPrimary, colorPrimaryDark, colorAccent e colorControlHighlight

/res/values/styIes.xmI

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<item name="colorPrimary">@color/primary

<item name:"colorPrimaryDark">@ClF/DFWBFy_dFk</W>
392 Google Android - 4 edio

<item nane="co1orAccent"@color/accent</iten

iten nane="colorControlighlght">@co1or/contro1_highIight/iten

Antes de executar, garanta que no arquivo /res/values/strings.xml a chave app_nane


esteja com o texto (arros, assim o ttulo da action bar vai car legal.

] /res/values/strings.ml
<'SOU'CeS>

<string nane="app_nane">Carros/string
<string name:"he11o_wor1d">Hel1o wor1d!
<strng name="action_settings">Settings

A gura 133 mostra o projeto executando no emulador. Este livro no foi impresso
com cores, portanto certique-se de que a action bar cou com as cores que voc
deniu no tema.
_ , ll u 10:43
, Yytjg jjf- ' V:
ts .fra-a ~ '., ';*i,,fz
z~;':,'i'f az
"'.-aaa. - .
' *~ 'j'l"`!
1 ;-..f
f ~.._,:~~ zfxmf, ~
.- :.`.,;\c~>". sui' _ l

Pigu ra 13.3 - (fores da action bar Customizadas.

13.3 Criando a activity e o fragment base para o projeto


/\lg0 QUC Cu Sempre gosto de fazer nos meus projetos criar uma classe de activity
me para todas as activities do
PTOJQO, C o mesmo vale para os fragments.
Captulo 13 I Navigation Drawer 393
Ento vamos l. Crie a classe BaseActivity, que lha de AppCompatActivity. Utilize o
menu New >Java Class. Essa classe deve car no pacote br.livroandroid.carros.activity.

BaseActivity.java
package br.con.livroandroid.carros.activity;
import android.support.v7.app.AppCompatActivity;
public class BaseActivity extends AppCompatActivity {
}

A vantagem de fazer isso podermos colocar mtodos na classe BaseActivity para


reutilizarmos em todas as suas subclasses. Como at o momento somente a
MainActivity existe no projeto, altere o seu cdigo para herdar de BaseActivity.

MainActivity.java
package br.cormlivroandroid.carros.activity;
public class MainActivity extends BaseActivity {
}

Ainda no temos nenhum fragment no projeto, mas j deixe criada a classe


BaseFragnent, conforme demonstrado a seguir. Essa classe deve car no pacote
br.livroandroid.carros.fragments.

BaseFragment.java
package br.com.livroandroid.carros.fragnents;
import android.support.v4.app.Fragment;
public class BaseFragment extends Fragment {
}

Para efeitos de organizao do projeto, eu gosto de utilizar cada classe em seu


respectivo pacote (Figura ]3.4). Veja que deixo as classes de activity no pacote
br.livroandroid.carros.activity e os fragments em br.livroandroid.carros.fragments.

1j java
3PP
n.;zaai;r,A____i___W__g_#_~_w__=&_j

l ? manfests
l

j br.com.lb.froandroid.carros

j*C.CzzMainActivit~y
Basezzctivity L.
'- fragments p
j ': BaseFragment

Figura B4 - Organizao dos pacotes.


394 Google Android - 4' edio
Lembrando que no cdigo o pacote a primeira linha do arquivo. Se voce digitar
um pacote que no existe no projeto, o Android Studio vai perguntar se voce nao
deseja mover o arquivo, portanto ele vai ajud-lo.

Dica: caso o editor mostre uma linha vermelha porque o Android Studio
identicou um problema. Nesses casos, recomendado abrir o assistente com o
atalho Ctrl+Enter pa ra visualizar as opes. Um exemplo disso quando digita mos
no cdigo um pacote que no existe (ex.: br.con.livroandroid.carros.fragments) c o
__. --' -~ ;' '
assistente pode ajudar a mover a classe pa ra o pacote correto.

No se esquea: ao fazer essa con gurao de pacotes, no arquivo AndroidManiest.xm1,


voc precisa congurar o caminho da activity relativa ao pacote principal, con
forme demonstrado a seguir.
<activity android:name=".activity.MainActivity" ... />

Antes de prosseguir com a leitura, execute o projeto novamente e garanta que


est tudo funcionando.

13.4 Classe Application - armazenando informaes globais


comum desenvolvedores utilizarem variveis de classe (estticas) para arma
zenar informaes globais da aplicao, mas no Android no recomendado
fazer isso. O correto criar uma classe de Application customizada que herda de
android.app.Application, a qual faz parte do ciclo de vida da aplicao, e o Android
vai criar essa classe junto com o processo da aplicao.
A classe Application um Singleton que pode ser utilizado para armazenar informa
es de forma global no aplicativo. Caso voc no saiba o que um Singleton, segue
uma breve explicao:
http://pt. wikipedia. org/wilai/Singlcton

O cdigo~fonte a seguir mostra como criar a classe CarrosApplication. Eu gosto de


deixa-la sempre no pacote raiz do projeto, que neste caso br.com. livroandroid.carros.

o (arrosAppIication.java
package br.com.livroandroid.carros;
import android.app.Application;
import android.util.Log;
public class CarrosApplication extends Application {
private static nal String TAG = "CarrosApplication";
Captulo 13 I Navigation Drawer 395
private static CarrosApplication instance = null;
public static CarrosApplication getInstance() {
return instance; // Singleton
}
@Override
public void onCreate() {
Log.d(TAG, "CarrosApplication.onCreate()");
// Salva a instncia para termos acesso como Singleton
instance = this;
1

@0verride
public void onTerminate() {
super.onTerninate();
Log.d(TAG, "CarrosApplication.onTerninate()");
}

Essa classe precisa ser registrada no AndroidManiest.xml, portanto congure a tag


conforme demonstrado a seguir:
AndroidManifest.mI
<nanifest . . .
<application android:nane=".CarrosApplication" .

Dica: sempre depois de colocar qualquer nome de classe em algum arquivo


XML, pressione Ctrl+Clique para abrir o arquivo. Se o arquivo abrir porque a
congurao est correta. Isso pode identicar erros simples de digitao.

Ao fazer essa configurao, a classe CarrosApplication ser instanciada quando o


processo da aplicao for criado. Nesse momento, o mtodo onCreate() chamado
6 St3 mos retendo a instncia desse objeto. Quando o Android terminar o pro
cesso da aplicao, o mtodo onTerminate() ser chamado para limpar os recursos.
Assim, sempre que precisarmos acessar essa classe para salvar ou ler informaes
globais, basta utilizar esta linha de cdigo:
CarrosApplication app = CarrosApplication.getInstance();

Mais para frente, durante o desenvolvimento do projeto dos carros, vamos utilizar
essa classe.
396 Google Android - 4 edio
13.5 Biblioteca android-utils
No projeto dos carros vamos utilizar a biblioteca android-utils que contem algumas
classes utilitrias criadas para facilitar os exemplos e tambm o seu aprendizado.
O cdigo-fonte da biblioteca android-utils est no GitHub para sua consulta:
https://github.com/livr0android/AndroidUtils/
O objetivo dessa biblioteca facilitar seu aprendizado e principalmente deixar
o desenvolvimento do aplicativo dos carros mais fcil. Para utilizar a biblioteca
android-utils, basta adicionar a dependncia no arquivo app/build.gradIe.

app/buiId.gradIe
apply plugin: 'com.android.application'

dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'con.android.support:appcompat-v7:22.1.0'
conpile 'br.con.livroandroid:android-utils:1.9.6'
}

Logo depois de adicionar essa linha no arquivo app/buildgradle, clique no boto


Sync Now que aparece no editor. Isso far com que o Gradle baixe a dependn
cia e deixe-a disponvel no projeto. Para testar se a dependncia da biblioteca
android-utils foi corretamente configurada, altere a classe BaseActivity do projeto
dos carros para herdar de livroandroid.lib.activity.BaseActivity.

BaseActivity.java
package br.com.livroandroid.carros.activity;
public class BaseActivity extends livroandroid.lib.activity.BaseActivity {
}

Repare que as classes tm o mesmo nome, mas esto em pacotes diferentes. Se voc
tiver duvidas sobre os pacotes, procure alguma leitura complementar. Da mesma
forma, lff 21 CQSSC BSFFH9ment que criamos para ser lha de livroandroid.lib.
fragment.BaseFragment da biblioteca android-utils.

J BaseFragment.java

package br.com.livroandroid.carros.fragments;
public class BaseFragment extends livroandroid.lib.fragnent.BaseFragnent {
}
Captulo 13 I Navigation Drawer 397
Feito isso, salve os arquivos e compile 0 cdigo com a opo Build > Rebuild Project. Se
o projeto compilar, tudo est congurado corretamente, e as classes da biblioteca
androd-utls foram encontradas.

Caso voc tenha diculdades em seguir os passos descritos at aqui, recomen


do comparar com o projeto Passo01-(riarProjeto disponvel no site https://github.com/
livroandroid/carros. Sempre que necessrio, conra o seu cdigo com os projetos
de exemplo, os quais apresentam a soluo para os exerccios que vamos fazer.

Nota: a classe BaseActivity da biblioteca android-utls contm alguns mtodos auxiliares


para mostrar alertas e toasts para facilitar a digitao dos exemplos. A classe
BaseFragrnent da biblioteca contm os mesmos mtodos, mas tambm tem o mtodo
startTask(...) que vamos utilizar posteriormente ao trabalhar com web services e
banco de dados. Lembrando que criei essa biblioteca para facilitar os exemplos,
e o cdigo-fonte est disponvel no site https://github.com/livroandroid/AndroidUtils

13.6 Como o Gradle encontrou a biblioteca android-utils


Voc deve estar se perguntando como o Gradle fez para encontrar a biblioteca
android-utiis que declaramos no arquivo app/buildgradle. Isso foi possvel porque a
biblioteca androd-utls est instalada (foi feito o deploy) no Maven Central, que um

1 . 1
repositrio mundial de projetos utilizado pelo Gradle para buscar as dependncias.

_l .**'
Para validar que a biblioteca existe no repositrio do Maven Central, acesse o
endereo http://search.ma1/en. org e faa a busca por br.con.1vroandrod (Figura 135).
Voc tambm pode adicionar repositrios locais na sua mquina, ou at instalar

is
1 l l
1 . E
um servidor de repositrios privado na rede de sua empresa.

the cezzrioo{ C \__ _ p* ___ __A,_:p__:__ ,AM __ ____v __ -__ -K3


C' searcb.rr1averi.or-rg;/*search%`7Cga%?C197KLbr.com.livroandroicl ' J Fa :

...aaa a--- ~~ - i ,z l pl

,Search' . DoResults
{, '1. GDIYQFSY
l SEARCH 1 AWANCEDSEARCH 1 BROWSE i C.UlCl$

I d ' ll .
br com lrvroandroid l SEARCH ll'
l Nr=vrAbutCenlral Ad d5earch | AFI Guide I 529
Gmupm Amfz Latest Version UPUHEG W"
br corn livroandroid andro1d~ulils LM _f13'll9l Odxmnlzms Em El @ .
:Too few results? Try: fc:br.com.l|vroandro|d l
l

Y4 :~'~_j:;*''-*';f"*
_-,_,_.,._.-.--.~--z-;___ ~~--W-e~~~-*~~f~~'f W- `7'";`"`"
___,:.;:.f:-;;z_:z,.;:::..---.:--: --:::.~ "fa':;:i:;.
--;.:---~~z~--~~ ~~-~z~ V

Figura 13.5 - Biblioteca android-utils no Maven Central.


398 Google Android - 4 edig
Como fazer o deploy no Maven Central est fora do escopo do livro, mas no cap
tulo 38, sobre Cradle, vamos aprender a criar e utilizar projetos do tipo biblioteca,
Tambm publiquei no meu site (ricardolecheta.com.br) uma srie de artigos sobre;
como criar bibliotecas no Gradle, desde deixar uma biblioteca de forma local no
seu computador, at fazer o deploy no Maven Central. Leia os artigos quando
achar necessrio, pois um dia voc vai precisar criar suas prprias bibliotecas.

Nota: sempre que adicionar uma nova dependncia no arquivo app/build.gradle,


clique no boto Sync Now no editor. Isso faz com que o Gradle faa 0 download da
dependncia e a deixe ativa no projeto, para que as funcionalidades como o assistente
de cdigo e o compilador encontrem as classes dessa biblioteca. Essa opo tambm
pode ser acessada no menu Tools > Android > Sync Project with Gradle Files.

13.7 (ongurando a Toolbar


No aplicativo dos carros, vamos utilizar a Toolbar para termos maior controle
sobre como ela posicionada e at podemos fazer algumas animaes de que
vamos precisar mais para frente.
Conforme j estudamos no captulo 12, sobre Toolbar, existem duas maneiras de
utiliz-la. Vou optar pela mais simples, que apenas desligar a action bar no tema
e fazer a Toolbar ser reconhecida pelo sistema como a action bar.
Para isso, altere o tema do projeto para utilizar a verso NoActionBar.

/res/values/styIes.mI

<sty1e nane="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">


// 0 resto no muda aqui

</resources

Na sequencia, crie o arquivo de include com a Toolbar. Lembre-se de que vamos


utilizar a biblioteca v7 de compatibilidade, portanto nunca utilize as classes na
tivas para action bar, fragments ou Toolbar.

/res/layout/include_tooIbar.xmI

<android.support.v7.widget.Too1bar xmins:android="http://schemas.android.com/apk/res/android"
xmins : app="http : //schemas . android . com/apk/res - auto"
android:id="@+id/toolbar"
Captulo 13 n Navigation Drawer 399
android:layout_width="match_parent" androidzlayout height="wrap Content"
android:background="?attr/colorPrimary" android:minHeight="zattr/actongargze"
app:theme:"@style/Thene0verlay.AppConpat.Dark.ActionBar"
app:popupTheme="@style/Theme0verlay.AppCompat.Light" />

Feito isso, deixe o arquivo de layout da activity conforme demonstrado a seguir.


Ve]a que o alterei para utilizar um Linearlayout com orientao vertical e tirei todos
os espaamentos (paddings).

/res/layout/activity_main.xml
<LinearLayout xnlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">

<include layout="@layout/include_toolbar" /
<TetView android:tet="@string/hello_world"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

Para finalizar a configurao, precisamos ativar a Toolbar no cdigo. Como essa


tarefa precisa ser feita em todas as activities, vamos criar o mtodo setUpToolbar()
na classe BaseActivity, que me de todas as activities do projeto.

BaseActivity.java
package br.com.livroandroid.carros.activity;
import android.support.v7.widget.Toolbar;
public class BaseActivity extends livroandroid.lib.activity.BaseActivity {
protected void setUpToolbar() {
Toolbar toolbar = (Toolbar) ndViewById(R.id.toolbar);
if (toolbar != null) {
setSupportActionBar(toolbar);
}

Em todas as activities
(1 ' no o prOJf0,
odemos nos esquecer P'
de ativar a Toolbar
portanto adicione a seguinte linha de cdigo na classe MainActivity:

MainActivity.java
package br.com.livroandroid.carros.activity;
public class MainActivity extends BaseAct1vity {
@0verride
400 Google Android - 4 edio
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activty_man);
setUpToo1bar();
1

Pronto. Feito isso, execute o projeto, e tudo deve continuar funcionando. A dife
rena que estamos usando a Toolbar no lugar da action bar. A vantagem que a
Toolbar uma view normal, portanto podemos criar animaes de movimento ou
transparncia quando for necessrio. Para ter ideia dessas aniinaes, brinque um
pouco com o aplicativo do Google Play e veja os efeitos que ele faz com a Toolbar.

13.8 Navigation Drawer


Denir o padro de navegao do aplicativo importante para facilitar a navega
o das telas. Para auxiliar nessa tarefa, o Android apresenta alguns padres bem
conhecidos pelos usurios, como a navegao por tabs e o Navigation Drawer
(menu lateral deslizante).
A navegao por tabs j estudamos, e ela recomendada quando existem poucas
aes, pois o usurio pode ver as tabs disponveis e descobrir rapidamente o que
o aplicativo pode fazer. Caso o aplicativo tenha muitas aes, recomendado
utilizar o Navigation Drawer, pois o menu lateral pode abrir uma listagem de
vrios itens, facilitando a visualizao do usurio.
No caso do aplicativo dos carros, no temos muitas aes, mas vamos utilizar o padro
Navigation Drawer mesmo assim, para pratjcarmos. lnternamente no aplicativo para
navegar entre os carros esportivos, clssicos e luxo, vamos utilizar as tabs. O padro
Navigation Drawer utilizado em vrios aplicativos nativos do Android, como Gmail.
Google Play Maps, Google Drive, YouTube, Play Music, Photos, entre outros, e reco
mendo que voc leia a documentao ocial para aprender mais detalhes.
http://wwuzgooglc.com/design/spec/pattcrns/navigation-drawcn html
A gura 13.6 mostra o que queremos fazer neste captulo.
(.riar um Navigation Drawer no to simples, principalmente se formos seguir
as boas praticas do Material Design. Segundo essas boas prticas, o Navigation
Drawer precisa ser aberto por cima da action bar, ocupando todo o espao dis
ponivel no celular. O menu tem diversas mtricas de espaamento e alinhamento
que voc precisa seguir, portanto recomendo que voc leia a pgina do Android
Design para obter mais informaes.
Captulo 13 I Navigation Drawer 401

Figura 13.6 - Navigation Drawer no projeto dos carros.

Antigamente, o Navigation Drawer abria por baixo da action bar, algo que at
que era simples de fazer. No Material Design, como temos de abrir o menu lateral
por cima de tudo, vamos precisar fazer algumas mgicas no cdigo. Mas que
tranquilo que vou ajudar voc a fazer tudo passo a passo.
Ento, mos obra!
Altere o arquivo de layout da activity principal, conforme demonstrado a seguir:

/res/layout/activity_main.xm|
<androd.support.v4.wdget.DrawerLayout xmlnszandrod="http://schenas_androd.com/apk/res/androd"
android:id="@+d/drawer_1ayout"
androd:1ayout width="match_parent" androd:1ayout_heght="natch_parent" >

<LinearLayout androd:d="@+d/content"
androd1ay0Ut wdth:"mtCh_D8Ft" androd:1ayout_heght="natch_parent"
androd:orentaton="vertica1">
<nclude 1ayout="@1)'U'C/lude-t1bar />

<FraneLayout _ H
androd:d="@+id/nav_drawer_c0nf19F
androd.1ay0ut wdth="match_parent" androd:layout_heght="match_parent"/>
402 Google Android - 4 edio
fragnent
android:id="@+id/nav_drawer_fragnent"
android:nane="livroandroid.lib.fragnent.NavigationDrawerFragnent"
android:layout_width="@dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start" />
</android.support.v4.widget.DrawerLayout

A classe DrawerLayout responsvel por abrir o menu deslizante, por isso ela foi
denida como a raiz deste layout. Essa classe contm os mtodos openDrawer() e
closeDrawer(), que podemos utilizar no cdigo para abrir e fechar o menu.
No layout do arquivo XML, so denidos dois blocos principais. Na parte supe
rior denido o contedo do aplicativo, onde inclu a Toolbar, e na parte inferior
existe um FrameLayout que ficar vazio por enquanto, pois ser utilizado para incluir
os fragments ao selecionar algum item do menu.
Na parte inferior do layout, foi adicionado o fragment NavigationDrawerFragment, que
faz parte da biblioteca android-utils. Vamos utilizar essa classe da biblioteca para
facilitar a criao do Navigation Drawer. Uma vez que alteramos o arquivo de
layout, vamos atualizar o cdigo-fonte da activity conforme demonstrado a seguir.

MainActivity.java
public class MainActivity extends BaseActivity {
private NavigationDrawerFragment nNavDrawerFragnent;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setUpToolbar();
// Nav Drawer
nNavDrawerFragnent = (NavigationDrawerFragnent) getSupportFragnentManager()
.ndFragnentById(R.id.nav_drawer_fragnent);
// Congura o drawer layout
f3WfL3YUf dfW@FLy0ut = (DrawerLayout) ndViewById(R.id.drawer_layout);
nNavDrawerFragnent.setUp(drawerLayout);
}

Esse Codigo simplesmente obtm os objetos NavigationDrawerFragment e DrawerLayout


que esto no layout. O mtodo setup(drawerLayout) utilizado para configurar o
NavigationDrawerFragnent com o DrawerLayout que foi adicionado no arquivo de layout.
Captulo 13 n Navigation Drawer 403
Feito isso, execute o cdigo, e o resultado deve ser como a gura 131 A parte
esquerda da gura mostra o menu fechado e na parte da direita podemos ver
o menu aberto. O menu lateral tem o fundo transparente, por isso precisamos
definir o contedo agora para o visual car mais interessante.

Figura 13.7 - Navigation Drawer

Para criar a lista no menu lateral, vamos fazer a activity implementar a interface
NavigationDrawerFragment.NavigationDrawerCallbacks. Essa interface utilizada na bi
blioteca android-utils para que a activity possa retornar a view do menu lateral.
Portanto, altere o cdigo-fonte da classe MainActivity conforme demonstrado a
seguir. Veja que coloquei comentrios para explicar o signicado de cada mtodo.

MainActivity.java
public class MainActivity extends BaseActivity inplenents
NavigationDrawerFragnent.NavigationbrawerCallbacks {

@Override
public NavigationDrawerFragment.NavDrawerListView
getNavDrawerView(NavigationDrawerFragment navigationDrawerFragnent,
Layoutlnater layoutlnater, ViewGroup container) {
// Deve retornar a view e o identicador do Listview
return null;
}

@0verride
public ListAdapter getNavDrawerListAdapter(NavigationDrawerFragment navigationDrawerFragment) {
// Este mtodo deve retornar o adapter que vai preencher o Listview
return null;
}

@0verride
404 Google Android - 4 edio
int position) { ,
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,

// Mtodo chamado ao selecionar um item do Listview.


}

Dica: nos cdigos do livro, utilizo a notao de trs pontinhos (. . .). O objetivo
mostrar apenas a parte nova do cdigo-fonte. Os trs pontinhos indicam que
o restante do cdigo-fonte no deve ser alterado.

Por enquanto, deixaremos estes trs mtodos vazios e vamos preench-los medida
que criarmos os arquivos. Comeamos criando o arquivo XML de layout da view
que vai aparecer no menu lateral. Essa view deve conter um Listview, conforme
demonstrado a seguir:

/res/layout/nav_drawer_Iistview.xm|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id:"@+id/listViewContainer" android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent" >
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1" android:background="#ffffff" />
<TetView

android:layout_width="match_parent" android:layout_height="50dp"
android:background="@color/gray" android:gravity="center"
android:text="Livro Android - Todos os direitos reservados"
android:tetColor="?attr/colorPrimary" />

Veja que, no cdigo deste arquivo de layout, criei o Listview e um Textview com um
texto sobre direitos reservados. Com esse arquivo pronto, podemos implementar o
mtodo getNavDrawerView( . . .) da interface NavigationDrawerCallbacks, o qual deve inflar
o layout para criar a view, e retornar a view e o identificador do Listview. Dessa
forma, a biblioteca android-utils vai saber como encontrar o Listview no layout.

MainActivity.java
public class MainActivity . . . {

@Override

public NavigationDrawerFragment.NavDrawerListView getNavDrawerView(.. .) {


View view = layoutInater.inate(R.layout.nav_drawer_listview, container, false);
Captulo 13 I Navigation Drawer 405
return new NavigationDrawerFragment.NavDrawerListView(view,R.id.listView);
}

Se voc executar o projeto agora e abrir o menu lateral, ver que esse layout j foi
utilizado, porm o Listview est vazio. Para preencher o Listview, vamos precisar de
um objeto que contenha o ttulo e a gura de cada item do menu, portanto crie
a classe NavDrawerMenuItem no pacote adapter.

NavDrawerMenu|tem.java
package br.com.livroandroid.carros.adapter;
public class NavDrawerMenuItem {
// Titulo: R.string.xx
public int title;
// Figura: R.drawable.x
public int img;
// Para colocar um fundo cinza quando a linha est selecionada
public boolean selected;
public NavDrawerMenuItem(int title, int img) {
this.title = title;
this.img = img;
}
// Cria a lista com os itens de menu
public static List getList() {
List list = new ArrayList();
list.add(new NavDrawerMenuItem(R.string.carros, R.drawable.ic_drawer_carro));
list.add(new NavDrawerMenuItem(R.string.site_livro, R.drawable.ic_drawer_site_livro));
list.add(new NavDrawerMenuItem(R.string.conguracoes, R.drawable.ic_drawer_settings));
return list;
}

Para esse cdigo funcionar, no se esquea de copiar as imagens dos cones do


menu para o projeto. Voc pode encontrar todas as imagens de que vamos precisar
nos projetos de exemplo de cada captulo. Adicione tambm os ttulos de cada
item de menu no arquivo /res/values/stringsxml.

/res/values/strings.xmI
<'eSOU'CeS>

<string name="carros">Carros
<string name="site_livro">Site do Livro</5F9>
<string name:"conguracoes">Conguraes
406 Google Android - 4 edio

O arquivo de layout do adapter pode ser visualizado a seguir. Ele contm apenas
o Imageview do cone e o Textview para o ttulo do item de menu.

/res/layout/adapter_nav_drawer.mI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout nlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="48dp"
android:background="@color/white" android:gravity="center_vertical"
android:orientation="horizontal" \
android:paddingLeft="16dp" android:paddingRight="16dp">
<ImageView android:id="@+id/ing"
android:layout_width="24dp" android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_drawer_carro" />
<TetView android:id="@+id/text"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:text="Menu"
android:textAppearance="?android:attr/textAppearanceedium"
android:textColor="@color/black" android:textSize="14sp" />

A seguir, temos o cdigo da classe do adapter, que vai preencher o Listview. Voc
pode cri-la no pacote adapter.

NavDrawerMenuAdapter.java
package br.com.livroandroid.carros.adapter;
// imports
public class NavDrawerMenuAdapter extends BaseAdapter {
protected static nal String TAG = "livroandroid";
private nal List list;
private nal Context context;
private Layoutlnater inater;
public NavDrawerMenuAdapter(Context context, List list) {
this.context = context;
this.list = list;
this.inater = (Layoutlnater) Layoutlnater.from(context);
}

@Override
public int getCount() { _
return list != null ? list.size() : 0;
}

@0verride
Captulo 13 I Navigation Drawer

public Object getItem(int position) {


return list != null ? list.get(position) : null;
}

@Override

public long getItemId(int position) {


return position;
}

@0verride

public View getView(int position, View view, ViewGroup parent) {


ViewHolder holder = null;
if (view == null) {
// Cria o ViewHolder
holder = new ViewHolder();
view = inater.inate(R.layout.adapter_nav_drawer, parent, false);
view.setTag(holder);
holder.tet = (TetView) view.ndViewById(R.id.tet);
holder.img = (ImageView) view.ndViewById(R.id.img);
} else {
// Reaproveita o ViewHolder
holder = (ViewHolder) view.getTag();
}

// Atualiza a view
NavDrawerMenuItem item = list.get(position);
holder.tet.setTet(item.title);
holder.img.setImageResource(item.img);
if (item.selected) {
// Congura o fundo cinza do item selecionado.
view.setBackgroundResource(R.drawable.seletor_nav_drawer_selected);
holder.text.setTetColor(context.getResources().getColor(R.color.primary))
} else {
view.setBackgroundResource(R.drawable.seletor_nav_drawer);
holder.tet.setTetColor(contet.getResources().getColor(R.color.black));
}

return view;
}

public void setSelected(int position, boolean selected) {


clearSelected();
list.get(position).selected = selected;
notifyDataSetChanged();
}

public void clearSelected() {


if (list != null) {
for (NavDrawerMenuItem item : list) {
408 Google Android - 4 edio
tem.selected = false;
}

notfyDataSetChanged();
}

// Design Patter "VewHolder"


static class ViewHolder [
Textvew text;
Imagevew img;
}

O cdigo da classe do adapterf extenso, mas acredito que voc j consiga entend-lo.
O adapter no apenas est inando seu arquivo de layout para criar a view como
tambm utiliza o padro Vewl-Iolder para reaproveitar as views durante a rolagem. Ob
serve que tambm coloquei os mtodos setSelected() e clearSelected() para controlar o
item de menu que est selecionado, pois uma boa prtica mostrar um fundo com
alguma cor diferenciada quando algum item est selecionado. Justamente por causa
desse fundo, precisamos criar os arquivos de seletores conforme demonstrado a seguir:

/res/drawable/seIetor_nav_drawer.mI

<selector mlns:androd="http://schemas.androd.com/apk/res/androd">
<tem androd:drawable="@color/transparent" androd:state_selected="true" />
<tem androd:drawable="@color/transparent" androd:state_pressed="true" />
<item android:drawable="@color/white" androd:state_selected="false" /

/res/drawabIe/seIetor_nav_drawer_se|etted.xmI

<selector mlns:android="http://schemas.androld.com/apk/res/androd">
<tem androd:drawable="@color/transparent" androld:state_selected="true" />
<tem androd:drawable="@color/transparent" androd:state_pressed="true" />
<tem androd:drawable="@color/gray" androld:state_selected="false" />

Nota: um seletor (selector) um arquivo XML que deve car na pasta /res/drawablc.
Pa ra cada estado, como selecionado (selected) ou pressionado (pressed), podemos
definir 0 fundo f1CSS?1fl0, Seja com imagens ou corcs. Neste caso, estou
denindo a cor de fundo cinza quando o item est selecionado.

DP05 de Criar 0 adlpffff, implemente o mtodo getNavDrawerLi.stAdapter(navFrag) (121


interface NavgatonDrawerCallbacks.
Captulo 13 n Navigation Drawer 409
MainActivity.java
public class MainActivity extends BaseActivity inplenents
NavigationDrawerFragment.NavigationDrawerCallbacks {
private NavDrawerMenuAdapter listAdapter;

@0verride
public ListAdapter getNavDrawerListAdapter(NavigationDrawerFragnent
navigationDrawerFragment) {
List list = NavDrawerMenuIten.getList();
// Deixa o primeiro item selecionado
list.get(0).selected = true;
this.listAdapter = new NavDrawerMenuAdapter(this, list);
return listAdapter;
1

Terminada essa alterao, execute o projeto novamente. Dessa vez, o menu lateral
vai mostrar a`lista com as opes, conforme a gura 13.8.

!| site do Livro

Q Configuraes

'\ 1 "
1 'N-i'.iC'
l

Figura 13.8 - Navigation Drawer com os itens de menu.

Nota: se tivssemos utilizado a action bar, o menu lateral seria aberto por
debaixo da barra. Como utilizamos a Toolbar e ela uma view, o menu lateral
consegue abrir por cima da Toolbar, o que um padro do Material Design.
Mesmo assim, ele no abriu por cima da status bar (barra superior que mostra
as horas), mas isso vamos ver depois.

Para nalizar a criao do Navigation Drawer, s falta implementar o mtodo


onNavDrawerItenSelected(), o qual chamado ao selecionar um item de menu.
410 Google Android - 4 edio
s ManActivty.java
public class MainActivity . . . {

@0verride
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {
List<NavDrawerMenuIten list = Navbrawerenulten.getList();
Navbrawerenulten selectedlten = list.get(position);
]/ Seleciona a linha
this.listAdapter.setSelected(position, true); \
toast("Clicou no item: " + getString(selectedItem.title));
}

Neste cdigo estamos chamando o mtodo setSelected(position,true) para indicar


que o item de menu est selecionado; sendo assim, o adapter vai desenhar o
fundo cinza nesse item. Agora execute o projeto e selecione um item de menu. O
resultado ser um alerta (toast) com o nome do item selecionado.

13.9 Criando o menu overow

Por padro, o Android Studio cria um item de menu com o texto Settings. Vamos
alterar esse arquivo de menu para criar um item chamado Sobre (About).

i /res/menu/menu_main.mI

<menu mlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<item

android:id="@+id/action_about" android:title="@string/action_about"
android:orderInCategory="100" app:showAsAction="never" />

Adicione 0 texto do menu no arquivo /res/values/stringsxml.

/res/values/strings.xmI
<'eSOU'CeS>

<string name="action_about">Sobre</gtrng>

E no cdigo da activity vamos tratar o evento de Sobre (About).


Captulo 13 I Navigation Drawer 411
MainActivity.java

@0verride
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_man, menu);
return true;
}

@Override
public boolean on0ptonsItemSe1ected(Menultem item) {
int id = item.getItemId();
if (id == R.d.acton_about) {
toast("C1icou no Sobre");
return true;
}

return super.on0ptonsItemSe1ected(tem) ;
}

Ao clicar no menu Sobre, um toast ser exibido (Figura 139). Posteriormente vamos me
lhor esta tela e criar um Fragnentalog com a descrio do projeto. Essa funcionalidade
bem comum nos aplicativos para que o usurio obtenha informaes adicionais.

Sf - if 1* f:*" ; 1 ' .f * . ~* '1~<=w


e ~ezu%uu@;
fz

Chcou no Sobre

Figura 13.9 -Item de menu Sobre (About).

13.10 Navigation Drawer com Material Design


Criar o Navigation Drawer at que foi simples, o mais complicado vai ser customi
z-lo para o Material Design, pois se voc olhar o aplicativo do Gmail que segue
as boas prticas o menu deslizante abre por cima da barra de status.
412 Google Android - 4* edio
E, como podemos verificar, nosso aplicativo no est fazendo isso. Ento vamgs
continuar a brincadeira, pois ainda no acabamos. Agora a coisa vai car um
pouco complicada, mas espero que com o passo a passo demonstrado aqui voc
consiga implementar o Navigation Drawer com os padres do Material Design_
Primeiramente, deixe o arquivo stylesxml conforme demonstrado a seguir. Veja
que estou denindo dois temas, o AppTheme e AppTheme.NavDrawer.

/res/values/styIes.xmI

<sty1e name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">


// No mudei nada aqui

sty1e name="AppTheme.Navbrawer" parent="AppTheme">

<item name:"windowActionBar0ver1ay">true

Agora crie o arquivo /res/values-1/21/styles.xml especco para o Android 5.0 (API


Level 21) ou superior. Para criar esses arquivos, costumo clicar no arquivo stylesxml
que j existe e teclar Ctrl+C e Ctrl+V, e no wizard eu digito 0 nome da pasta que
/res/values-1/21. Esse arquivo vai customizar o tema AppTheme.NavDrawer com proprie
dades especficas do Android 5.0 (API Level 21) ou superior.

/res/values-v21/styles.xmI
<`ESOU`CS>

<sty1e name="AppTheme.NavDrawer" parent="AppTheme">


</span></span> <span class='ocr_word' id='word_1_186' title="bbox 868 2962 1067 3010"><span class='xocr_word' id='xword_1_186' title="x_wconf -4">Habiiita</span></span> <span class='ocr_word' id='word_1_187' title="bbox 1097 2980 1119 3011"><span class='xocr_word' id='xword_1_187' title="x_wconf 0">o</span></span> <span class='ocr_word' id='word_1_188' title="bbox 1148 2969 1323 3025"><span class='xocr_word' id='xword_1_188' title="x_wconf -3">overiay</span></span> <span class='ocr_word' id='word_1_189' title="bbox 1353 2973 1452 3019"><span class='xocr_word' id='xword_1_189' title="x_wconf -1">mode</span></span> <span class='ocr_word' id='word_1_190' title="bbox 1483 2989 1528 3021"><span class='xocr_word' id='xword_1_190' title="x_wconf -1">na</span></span> <span class='ocr_word' id='word_1_191' title="bbox 1560 2981 1708 3025"><span class='xocr_word' id='xword_1_191' title="x_wconf -2">action</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1741 2982 1812 3027"><span class='xocr_word' id='xword_1_192' title="x_wconf -1">bar</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1848 3000 1917 3024"><span class='xocr_word' id='xword_1_193' title="x_wconf -4">~-></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 740 3046 2176 3128"><span class='ocr_word' id='word_1_194' title="bbox 740 3046 864 3090"><span class='xocr_word' id='xword_1_194' title="x_wconf -1"><item</span></span> <span class='ocr_word' id='word_1_195' title="bbox 894 3050 2176 3128"><span class='xocr_word' id='xword_1_195' title="x_wconf -4">name="android:windowTransiucentStatus">true</item></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 739 3132 2384 3218"><span class='ocr_word' id='word_1_196' title="bbox 739 3132 863 3176"><span class='xocr_word' id='xword_1_196' title="x_wconf -1"><item</span></span> <span class='ocr_word' id='word_1_197' title="bbox 893 3136 2384 3218"><span class='xocr_word' id='xword_1_197' title="x_wconf -5">name="android:windowDrawsSystemBarBackgrounds">true</tem></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 533 3216 2331 3440">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_31' title="bbox 738 3217 2330 3303"><span class='ocr_word' id='word_1_198' title="bbox 738 3217 862 3261"><span class='xocr_word' id='xword_1_198' title="x_wconf -1"><item</span></span> <span class='ocr_word' id='word_1_199' title="bbox 892 3221 2330 3303"><span class='xocr_word' id='xword_1_199' title="x_wconf -4">name="android:statusBarCo1or"@co1or/primary_dark</item</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 635 3298 836 3354"><span class='ocr_word' id='word_1_200' title="bbox 635 3298 836 3354"><span class='xocr_word' id='xword_1_200' title="x_wconf -4"></styie></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 533 3382 835 3436"><span class='ocr_word' id='word_1_201' title="bbox 533 3382 835 3436"><span class='xocr_word' id='xword_1_201' title="x_wconf -1"></resources></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 622 3591 2806 3701">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 622 3591 2806 3701"><span class='ocr_word' id='word_1_202' title="bbox 622 3591 959 3648"><span class='xocr_word' id='xword_1_202' title="x_wconf -3">Importante:</span></span> <span class='ocr_word' id='word_1_203' title="bbox 976 3611 1035 3657"><span class='xocr_word' id='xword_1_203' title="x_wconf -1">pa</span></span> <span class='ocr_word' id='word_1_204' title="bbox 1045 3613 1091 3644"><span class='xocr_word' id='xword_1_204' title="x_wconf -2">ra</span></span> <span class='ocr_word' id='word_1_205' title="bbox 1110 3597 1298 3650"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">validar</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1313 3621 1366 3652"><span class='xocr_word' id='xword_1_206' title="x_wconf -2">as</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1384 3603 1822 3668"><span class='xocr_word' id='xword_1_207' title="x_wconf -4">funcionalidades</span></span> <span class='ocr_word' id='word_1_208' title="bbox 1840 3618 2105 3674"><span class='xocr_word' id='xword_1_208' title="x_wconf -2">referentes</span></span> <span class='ocr_word' id='word_1_209' title="bbox 2122 3644 2184 3676"><span class='xocr_word' id='xword_1_209' title="x_wconf -1">ao</span></span> <span class='ocr_word' id='word_1_210' title="bbox 2203 3630 2436 3681"><span class='xocr_word' id='xword_1_210' title="x_wconf -2">Material</span></span> <span class='ocr_word' id='word_1_211' title="bbox 2455 3635 2665 3701"><span class='xocr_word' id='xword_1_211' title="x_wconf -3">Design,</span></span> <span class='ocr_word' id='word_1_212' title="bbox 2685 3651 2806 3686"><span class='xocr_word' id='xword_1_212' title="x_wconf -2">teste</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 622 3667 1922 3750">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 622 3668 1921 3750"><span class='ocr_word' id='word_1_213' title="bbox 622 3668 1921 3750"><span class='xocr_word' id='xword_1_213' title="x_wconf -9">noennadoroucspovoconiAndroklO</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 1927 3709 2221 3769">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_36' title="bbox 1927 3710 2221 3769"><span class='ocr_word' id='word_1_214' title="bbox 1927 3710 2221 3769"><span class='xocr_word' id='xword_1_214' title="x_wconf -6">tisuperknz</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 13 I Navigation Drawer 413
O objetivo de criar o tema ApoTheme.NavDrawer deixar a barra de sistema transparen
te. Como s queremos fazer isso na MainActvty, altere o arquivo AndroidManiest.xml
conforme demonstrado a seguir. Com esta congurao, todas as outras activities
que ainda vamos criar vo utilizar o tema padro, somente a ManActvty vai utilizar
o tema AppTheme.NavDrawer.

AndroidManifest.mI
<manifest _ . .>
<app1ication . . . androd:theme="@style/AppTheme" >
<activity
android:name=".activty.ManActyty" android:thene="@sty1e/AppThene.NavDrawer" . _ . />
</app1cation>

Se voc executar o projeto agora, ver que o menu lateral j ocupou a tela inteira.
Mas, por causa dessa alterao, a barra de status est transparente, e precisamos
desenhar a cor dela manualmente. Isso feito com uma nica linha de cdigo,
conforme demonstrado a seguir:

MainActivity.java

protected void onCreate(Bund1e savedInstanceState) {

// Cor de fundo da barra de status


drawerLayout . setStatusBarBackground(R . color . primary_dark);
1

Basicamente o que fizemos foi deixar a barra de status transparente, para que o
contedo da tela seja desenhado por cima dessa barra. Isso foi possvel porque
customizamos as propriedades do tema.
A figura 13.10 mostra o resultado. Veja que a Toolbar est deslocada para cima e
invadindo o espao da barra de status (system bar). Ao abrir o menu lateral, veja
que ele abre por cima de tudo (inclusive da Toolbar e da status bar), o que est
correto no Material Design.
O problema que, quando o menu do Navigation Drawer estiver fechado, no
queremos que a Toolbar seja deslocada para cima. Para resolver esse problema, o
Google criou a classe ScrmInsetsFrameLayout que faz parte do aplicativo do Google l/O
2014, cujo cdigo-fonte est disponvel no Gitl-Iub: https://github.com/google/ioschcd.
41 4 Google Android - 4 edio

!| sn. ao Livro

Confqurn

Figura 13.10 - Action Bar Ovcrlay (transparente).

Eu copiei essa classe para a biblioteca androd-utls, portanto basta adiciona-la


no layout. Para manter o padro, deixei a classe ScrmInsetsFrameLayout no mesmo
pacote que estava no projeto do Google I/O. Para utilizar essa classe, adicione a
propriedade androd:tsSystemwndows="true" na raiz do layout, que o DrawerLayout.
e envolva o fragment do Navigation Drawer l no nal do cdigo-fonte com a
classe ScrmInsetsFrameLayout.

LEE /res/layout/activity_main.xm|

<androd.support.v4.wdget.DrawerLayout xm1ns:androd="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.androd.con/apk/res-auto"
androd:td="@+d/drawer_1ayout"
androtd:1ayout_wdth="match_parent"
androd:1ayout_heght="match_parent"
androd:tsSystemHndows="true">
<LinearLayout ... >

</LnearLayout>
<com.goog1e.sanp1es.apps.iosched.ui.wdget.ScrimInsetsFrameLayout
androd:id="@+id/contanerScrmlnsets"
android:1ayout_width="wrap_content" android:1ayout_heght="match_parent"
android:1ayout_gravty="start" androd:e1evaton="8dp"
android:tsSystemHndows="true" app:insetForeground="#4000"

<fragment
android:id:"@+id/nav_drawer_fragment"
androd:name="1ivroandrod.1b.fragment.NavgatonDrawerFragment"
androd:layout_wdth="@dmen/navgation_drawer_wdth"
androd:1ayout_height="match_parent"
androd:1ayout_gravity="start" />
</com.goog1e.samp1es.apps.iosched.ui.widget.ScrnInsetsFrameLayout>
</androd.support.v4.widget.DrawerLayout>
Captulo 13 I Navigation Drawer 415
O atributo androd:fitsSystemwndows="true" que foi colocado na raiz do layout faz
com que o layout seja desenhado mais para baixo, encaixando-se na janela que
o sistema utiliza para desenhar a aplicao. Ao fazer isso, o Navigation Drawer
tambm vai abrir mais para baixo, porm no caso do menu queremos manter ele
abrindo sobre a barra de sistema, pois esse o padro do Material Design. justa
mente por isso, o fragment que controla o Navigation Drawer foi envolvido com
a classe ScrmInsetsFrameLayout do Google, que por sua vez faz a mgica de desenhar
o Navigation Drawer por cima de tudo. Esse o pulo do gato!
Ao executar o projeto, o resultado deve ser como a gura 13.11.

!| site ao Livro

Q Configuraes

Figura 13.11 - Mgica com o Navigation Drawer

13.11 Material Design no Navigation Drawer


j zemos o Navigation Drawer seguindo os padres do Material Design, pois o
menu lateral est abrindo sobre a barra de status. No entanto, se voc comparar
com alguns aplicativos do Google como o Gmail, ver que est faltando aquele
cabealho em que podemos visualizar uma bonita imagem e geralmente infor
maes do usurio.
Ento vamos fazer isso para dar um acabamento prossional no aplicativo. Adi
cione a seguinte linha no arquivo de layout do menu lateral. Isso vai incluir um
cabealho logo acima do Listvew.

/res/layout/nav_drawer_Iistview.mI
<LinearLayout mlns:androd="http://schemas.android.com/apk/res/androd"
android:id:"@+id/listViewContaner" androd:orentation="vertica1"
androd:1ayout_wdth="natch_parent" android:1ayout_heght="match_parent" >

<nc1ude 1ayout="@1ayout/nav_drawer_1stvew_header" /
LstVew
androd:id="@+id/Iistvew"
416 Google Android - 4' edio
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="1" android:background="#ffffff" />
<TetView . . .
android:tet="Livro Android - Todos os direitos reservados" />

Feito isso, execute o aplicativo, e o resultado sera coino a gu ra l3.12. O arquivo


@1a_vzl/nai_drawer_listiiew_zcaa'er faz parte da biblioteca android-utils, e apresenta
uma bonita imagem de fundo. Se voc quiser, copie o cdigo-fonte do arquivo Q
coloque no seu projeto.

!| Site do Livro

Q Configuraes

Figura 1.3.12 - Cabealho no Navigation Drawci:

Para fechar o assunto com chave de ouro, vamos adicionar uma linha de cdigo no
metodo getNavDrawerView(), o qual responsvel por criar a view do menu lateral. O
que vamos fazer e simplesmente preencher o layout do cabealho com a foto (logol
do aplicativo ou usurio, e algumas informaes adicionais como nome e email.

fs ManActivity.java

public class MainActivity extends BaseActivity implements


NavigationDrawerFragment.NavigationrawerCallbacks {

@0verride
public NavigationDrawerFragment.NavDrawerListView getNavDrawerView(. . .) {
View view = layoutlnater.inate(R.layout.nav_drawer_listview, container, false);
// Preenche o cabealho com a foto, nome e email.
navigationDrawerFragment.setHeaderValues(view, R.id.listViewContainer,
R.drawable.nav_drawer_header, R.drawable.ic_logo_user, R.string.nav_drawer_usernane
R.string.nav_drawer_enail);
return new NavigationDrawerFragment.NavDrawerListView(view,R.id.listView);
}

}
Captulo 13 I Navigation Drawer 417
Dica: na pgina do Android Design, no tpico sobre o Navigation Drawer, podemos
encontrar a especificao do layout para esse cabealho. As boas prticas de
interface denem mtricas de espaamentos, alinhamentos, fontes etc.

Para o cdigo compilar, copie a gura R.drawab1e.c_1ogo_user (imagem do livrinho)


para o seu projeto. Ela pode ser encontrada nos projetos de exemplo do livro. Os
textos R.string.nav_drawer_username e R.strng.nav_drawer_ema1 precisam ser inseridos
no arquivo /res/values/strings.xml.

/res/values/strings.xmI
<I'SOUI`CES>

<strng name="nav_drawer_username">Rcardo Lecheta</strng>


<string name="nav_drawer_emai1">r1echeta@gma1.com

Feito isso, execute o projeto de novo e nalmente teremos o Navigation Drawer


seguindo as boas prticas do Material Design (Figura 1313). No cdigo eu deixei
de forma esttica a gura e os textos com o nome e email do usurio, mas acredito
que com este exemplo voc consiga evoluir a ideia para seu aplicativo e atualizar
as informaes dinamicamente caso seja necessrio.

l Carros

E] Site do Livro

Q Conguraes

Figura 13.13 - Navigation Drawer com Material Design.

Lembre-se de que utilizamos a biblioteca android-utiis para auxiliar na explicao


e deixar o cdigo digitado mais simples, pois ela encapsula boa parte do trabalho
que tivemos para construir o Navigation Drawer. O fragment que voc adicionou
418 Google Android - 4' edio
no arquivo de layout foi baseado no exemplo do prprio Android Studio, ng
wizard de template do Navigation Drawet Eu apenas melhorei a ideia, e deixei
o componente um pouco mais customizvel. Quando achar necessrio, estude o
cdigo-fonte desse fragment para melhorar o seu aprendizado.

13.12 Criando os fragments do projeto


Para nalizar o captulo, vamos criar os fragments que sero adicionados na tela
quando o usurio selecionar as opes do menu.
Faa isso com o wizard New> Fragment > Fragment (Blank) e no campo do nome do
fragment digite CarrosFragnent e no layout digite fragnent_carros. O Android Studio
vai criar os arquivos com um monte de cdigo, mas pelo menos ele j criou tudo
em seu devido lugar. Apague todo o cdigo gerado, e deixe simples conforme
demonstrado a seguir:

CarrosFragment.java
package br.com.livroandroid.carros.fragments;
public class CarrosFragment extends BaseFragnent {
@0verride
public View onCreateView(Layoutlnater inater, Viewroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_carros, container, false);
return view;
}

Importante: no wizard no selecione as opes Include fragment factory methods


e include interface callbacks, pois se zer isso ele vai gerar cdigo demais. De
qualquer forma, depois de gerar as classes com o wizard, sempre deixe-as igual
ao cdigo-fonte demonstrado no livro.

Lembre-se de que todos os fragments do projeto devem herdar de BaseFragment


que criamos anteriormente. Esse o fragment que vai mostrar a lista de carros,
(classicos, esportivos e luxo). Por enquanto, o fragment no faz nada, e no arquivo
de layout temos apenas um texto qualquer.
Captulo 13 I Navigation Drawer 419
/res/layout/fragment_carros.xmI
<FraneLayout xnlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="natch_parent" android;layout_height="natch_parent">
<TextView

android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="Lista de Carros Aqui" android:layout_gravity="center" />

Vamos criar tambm o fragment para abrir a pgina do livro com um WebVievv

SiteLivroFragment.java
package br.con.livroandroid.carros.fragnents;
public class SiteLivroFragnent extends BaseFragnent {
@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_site_livro, container, false);
return view;
}

/res/layout/fragment_site_Iivro.xmI
<FraneLayout xnlns:android="http://schenas.android.con/apk/res/android"
android:layout_width="natch_parent android:layout_height="natch_parent">
<TetView
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Pgina do Livro Aqui" android:layout_gravity="center" />

Ainda no vamos fazer nada com o item de menu configuraes, pois vamos
estudar essa parte no captulo 18, sobre persistncia.
Depois de criar os fragments, vamos adiciona-los dinamicamente no layout da
activity principal. Para isso, altere o mtodo onNavDrawerItenSelected(. . .) da MainActivity.
Por enquanto esse mtodo mostra um toast com o ttulo do menu selecionado, mas
desta vez, ao selecionar um item, vamos adicionar o fragment no layout.
420 Google Android - 4' edio
MainActivty.java

inport android.support.v4.app.Fragnent;
public class HainActivity extends BaseActivity implenents
NavigationDrawerFragment.NavigationDrawerCallbacks {

@0verride
public void onNavDrawerItenSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {
ListNavDrawerHenuIten> list = Navbrawerenulten.getList();`
Navbrawerenulten selectedlten = list.get(position);
// Seleciona a linha i
this.listAdapter.setSelected(position, true);
if (position == 6) {
replaceFragnent(new CarrosFragnent());
} else if (position == 1) {
replaceFragnent(new SiteLivroFragnent());
} else if (position == 2) {
toast("Conguraes");
} else {
Log.e("livroandroid", "Item de menu invlido");
}

}
// Adiciona o fragnent no centro da tela
private void replaceFragment(Fragnent frag) {
getSupportFragnentHanager().beginTransaction().replace(R.id.nav_drawer_container,
frag, "TAG").connit();
}

Veja que criei o mtodo replaceFragmetn(frag) para facilitar a leitura do cdigo, e preste
ateno pois voc precisa importar a classe da biblioteca de compatibilidade, que
android.support.v4.app.Fragment. Veja que a FragmentTransaction est fazendo replace()
e no add(), pois sempre queremos substituir o fragment do contedo por outro.
Ao terminar essas conguraes, execute o projeto novamente, e o resultado deve
ser como a gura 13.14, que mostra o fragment dos carros. Na parte da esquerda
da gura podemos ver o menu aberto, e na direita o fragment que foi adicionado
COH1OCOnRdO3OChGHIK)HWU(m&

O item de menu Site do Livro vai adicionar seu respectivo fragment na tela, e o item
(onguraes vai mostrar um simples toast por enquanto.
Captulo 13 n Navigation Drawer 421

E1 sned Livro

Conguraes

Lista de Carros Aqui

Livro Android ~ o os os irei os

Figura 13.14 - Fragments em ao.

13.3 Links teis

Neste captulo, criamos o projeto dos carros, e talvez este tenha sido o passo mais
difcil, pois geralmente o complicado comear.
Aprendemos a utilizar o padro de navegao do Navigation Drawer, e para con
tinuar os seus estudos separei alguns links da documentao ocial.
Android Developer- Patterns - Navigation Drawer

https://developerandroid.com/design/patterns/navigation-drawen html
Material Design - Navigation Drawer

http://wwwgoogle.com/design/spec/patterns/navigation-drawex html

Android Training - Overlaying the Action Bar

https://developer android.com/training/basics/actionbar/overlaying.html
"* i CAPTULO 14
~l

WebView
*M 1

\
1
\

Neste captulo, vamos estudar detalhes sobre a classe Hebvew, um dos componentes
mais utilizados e flexveis do Android.

14.1 Fragment com Weblew


Vamos continuar o desenvolvimento do projeto dos carros e o prximo objetivo
utilizar um webvew na classe SiteLvroFragment para abrir esta pgina da internet.
http://wwwlivroandroid.com.br/sobre.htm
Atualize o cdigo do arquivo de layout conforme demonstrado a seguir:

/res/layout/fragment_site_Iivro.xmI
<?m1 verson="1.G" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/androd"
android:1ayout_width="match_parent" androd:1ayout_heght="match_parent">
<webView androd:d="@+id/webview"
android:1ayout_width="match_parent" androd:layout_height="match_parent" />
<ProgressBar android:id="@+d/progress"
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:1ayout_gravity="center" /

Observe que o layout raiz um FrameLayout para deixar o ProgressBar por cima dO
webview. No cdigo do fragment basta mostrar a pgina no webvew.

SiteLvroFragment.java
package br.com.1ivroandroid.carros.fragments;

public class SiteLivroFragment extends BaseFragment {

422
Captulo 14 I WebView 423
private static nal String URL_SOBRE = "http://www.livroandroid.con.br/sobre.htm";
private webview webview;
private ProgressBar progress;
@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_site_livro, container, false);
webview = (WebView) view.ndViewById(R.id.webview);
progress = (ProgressBar) view.ndViewById(R.id.progress);
setNebViewClient(webview);
// Carrega a pgina
webview.loadUrl(URL_SOBRE);
return view;
}

private void setwebViewClient(HebView webview) {


webview.setwebViewClient(new HebViewClient() {
@0verride
public void onPageStarted(HebView webview, String url, Bitmap favicon) {
super.onPageStarted(webview, url, favicon);
// Liga o progress
progress.setVisibility(View.VISIBLE);
}
@0verride
public void onPageFinished(HebView webview, String url) {
// Desliga o progress
progress.setVisibility(View.INVISIBLE);
}

});
}

Para o webview acessar a internet, adicione esta permisso no AndroidManiest.xml:

AndroidManifest.xmI
<manifest . . .>
uses-permission android:nane="android.permission.INTERNET" /
<application . . . />

Feito isso, execute o projeto, e o resultado deve ser como a gura 14.1. Acredito
que seja simples entender esse exemplo, pois j estudamos o webview no captulo 7,
sobre views. Sendo assim, nos prximos tpicos vamos explorar algumas funcio
nalidades mais avanadas.
424 Google Android - 4 edio
1 1I 1z1
1: Livro 1.
i
1

1 Google
i Andnd Bau
FIDROID
^'""""":2fl'.'..'

1
V' F .z " WW'
\
1 Este livro dedicado a todos os q
desenvolvedores Android, desde quem
est aprendendo pela primeira vez, at 1 ;
1 quem est buscando aprimorar seus 1 1
p conhecimentos do
funcionalidades e estudar
Androidas 5.0
novas
e 11pl
1 Material Design.
Mais informaes Z
Figura 14.1 - Pgina do livro com Webl/ieu

14.2 Swipe to Refresh


Um padro de design muito conhecido em aplicativos mobile o Swipe to Refresh,
o qual permite atualizar os dados da lista quando 0 usurio faz o gesto de rola
gem para baixo.
Para fazer o Swipe to Refresh, envolva o webview com 0 layout SwipeRefreshLayout.

/res/layout/fragment_site_Iivro.xmI
<?m1 version="1.G" encodng="utf-8"?>
<FrameLayout . . .>
androd.support.v4.widget.SwpeRefreshLayout
androd:id="@+d/swpeToRefresh"
androd:1ayout_width="match_parent" androd:1ayout_height="match_parent">
<NebVew . . . />
</android.support.v4.wdget.SwpeRefreshLayout
<ProgressBar . . . />

O SwpeRefreshLayout um gerenciador de layout que deve envolver outra view (ape


21S Uma), Pra que automaticamente seja adicionado o suporte atualizao da
view por meio do gesto de rolagem para baixo. No cdigo a nica tarefa necessria
COUHIUYPIY 0 ll5fl1f COH1 0 mtodo set0nRefreshLstener(listener). Feito isso, quando
o gesto de rolagem for feito, o mtodo onRefresh() ser chamado - momento em
Captulo 14 1 WebView 425
que podemos chamar o mtodo webview. reload() para atualizar a pgina. Observe
que no mtodo onPageFinished() do webViewClient estamos encerrando a animao
dcSwipeRefreshLayout.

SiteLivroFragment.java

public class SiteLivroFragment extends BaseFragment {

protected SwipeRefreshLayout swipeLayout;


public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {

// Swipe to Refresh
swipeLayout = (SwipeRefreshLayout) view.ndViewById(R.id.swipeToRefresh);
swipeLayout.set0nRefreshListener(0nRefreshListener());
swipeLayout.setColorScheneResources(
R.color.refresh_progress_1,
R.color.refresh_progress_2,
R.color.refresh_progress_3);

1t
return view;
}

private SwipeRefreshLayout.0nRefreshListener 0nRefreshListener() {


return new SwipeRefreshLayout.0nRefreshListener() {
@0verride
public void onRefresh() {
webview.reload(); /I Atualiza a pgina
}

};

private void setwebViewClient(webView webview) {


webview.setwebViewClient(new webViewClient() {

@Override
public void onPageFinished(webView webview, String url) {
// Desliga o progress
progress.setVisibility(View.INVISIBLE);
// Termina a animao do Swipe to Refresh
swipeLayout.setRefreshing(false);
}

});
}

}
426 Google Android - 4 edio
Para o cdigo compilar, crie as cores da animao do SwDER@ff@S|1|-6YU'f CIUC Uma
bolinha girando que ca alternando entre as cores que VOCE! Cllf

i /res/values/colors.xml
<fESOUl'CS>

<color name="refresh_progress_1">#33B5E5
<color name="refresh_progress_2">#99CCGG/color> \
<color name="refresh_progress_3"#CC00@G
</resources

A gura 14.2 mostra o resultado com a animao do Svvipe to Refresh, a m de


atualizar a pgina.

Livro
Google )
Android
_.i\.;'Q_==_';>.L1.>

..-u-puv-:_v.-.~_

Figura 14.2 - Animao do Swipe to Refresh.

Dica: para fazer o gesto Pull to Refresh, toque na lista e segure, depois arraste para
baixo e solte. o mesmo gesto utilizado em aplicativos nativos como o Gmail.

14.3 Interteptando requisies no WebView


Uma funcionalidade muito comum em aplicativos que utilizam webvew inter
ceptar as URLs para executar alguma lgica no aplicativo.
No caso do aplicativo dos carros, o webvew abriu a seguinte pgina:
http://www.livroandroid.combr/sobre.htm

Nesta pgina existe um link Mais Informaes que est chamando a prpria pgina
sobre.htm novamente. Nosso objetivo interceptar o clique nesse link para mostrar
um alerta com algumas informaes adicionais de forma nativa no aplicativo. Isso
Captulo 14 I Weblew 427
simples de fazer, basta sobrescrever o mtodo should0verrideUrlLoading(view,url)
da classe webViewClient.

SiteLivroFragment.java
public class SiteLivroFragment extends BaseFragment {

private void setHebViewClient(webView webview) {


webview . setNebViewClient(new HebViewClient() {

@0verride
public boolean should0verrideUrlLoading(lrlebview view, String url) {
Log.d("livroandroid", "webview url: " + url);_
if (url != null && url.endswith("sobre.htm")) {
toast("Clicou em Mais Informaes");
// Retorna true para.informar que interceptanos o evento
return true;
1

return super.should0verrideUrlLoading(view, url);


}

});
}

14.4 Mostrando alertas com o FragmentDiaIog


No tpico anterior, interceptamos o clique no link Mais Informaes. Portanto, ao
executar o projeto e clicar nesse link, o aplicativo vai mostrar um toast.
Mas em vez do toast queremos mostrar um alerta (dialog) com informaes sobre
o aplicativo e com links para o usurio clicar e ver mais informaes. Para mostrar
o alerta, vamos utilizar a classe DialogFragment, pois a maneira recomendada de
mostrar alertas segundo as boas prticas do Android. A classe DialogFragnent um
fragment usado para mostrar alertas customizados. A vantagem de utilizar um
fragment em vez do convencional Alertialog que podemos customizar a view
do fragment, e o fragment tambm faz parte do ciclo de vida da activity Para
mostrar ou esconder um F ragmentDialog, podemos utilizar a API da Fragmenransaction
conforme j estudamos, portanto todos os conceitos sobre fragments continuam
sendo aplicados. O simples fato de herdar de DialogFragnent far com que a view
desse fragment seja exibida como um alerta.
428 Google Android - 4 edio
O objetivo mostrar no alerta um texto com informaes do aplicativo, portanto
adicione as seguintes mensagens no arquivo /res/values/stringsxml:

/res/values/strings.xmI
< ESOU l'CS>

~ O layout da janela do alerta ser um simples Textvew que mostra o texto


@string/about_dialog_text.

android:id="@androd:id/tet1"
/res/layout/diaIog_about.xmI
<TetVew xmlns:androd="http://schemas.androd.com/apk/res/androd"

androd:1ayout_wdth="match_parent" androd:1ayout_height="match_parent"
android:paddng="24dp" android:text:"@string/about_da1og_tet" />

Para implementar a classe do fragment, temos duas opes. Podemos sobrescrever


o mtodo onCreateVew() como qualquer outro fragment e retornar a view do alerta,
ou sobrescrever o mtodo onCreateDa1og(BundIe) para criar um alerta customizado
<strng name="about_da1og_tet">
O0O

</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_10' title="bbox 548 1159 1499 1224"><span class='ocr_word' id='word_1_40' title="bbox 548 1168 879 1224"><span class='xocr_word' id='xword_1_40' title="x_wconf -4"><b>Ap1icatvo</span></span> <span class='ocr_word' id='word_1_41' title="bbox 908 1166 981 1212"><span class='xocr_word' id='xword_1_41' title="x_wconf -1">dos</span></span> <span class='ocr_word' id='word_1_42' title="bbox 1011 1168 1161 1210"><span class='xocr_word' id='xword_1_42' title="x_wconf -2">Carros</span></span> <span class='ocr_word' id='word_1_43' title="bbox 1193 1159 1499 1216"><span class='xocr_word' id='xword_1_43' title="x_wconf -4">2015</b><br></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_11' title="bbox 548 1253 983 1302"><span class='ocr_word' id='word_1_44' title="bbox 548 1261 700 1302"><span class='xocr_word' id='xword_1_44' title="x_wconf -1">Versao</span></span> <span class='ocr_word' id='word_1_45' title="bbox 728 1253 983 1301"><span class='xocr_word' id='xword_1_45' title="x_wconf -2">%s<br><br></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_12' title="bbox 550 1321 2602 1396"><span class='ocr_word' id='word_1_46' title="bbox 550 1357 597 1389"><span class='xocr_word' id='xword_1_46' title="x_wconf -1"><a</span></span> <span class='ocr_word' id='word_1_47' title="bbox 629 1321 2602 1396"><span class='xocr_word' id='xword_1_47' title="x_wconf -4">href="http://www.1vroandrod.com.br">http://lvroandrod.com.br</a><br><br></span></span></span>
<span class='ocr_line' id='line_1_13' title="bbox 551 1408 2910 1484"><span class='ocr_word' id='word_1_48' title="bbox 551 1444 597 1476"><span class='xocr_word' id='xword_1_48' title="x_wconf -1"><a</span></span> <span class='ocr_word' id='word_1_49' title="bbox 626 1408 2910 1484"><span class='xocr_word' id='xword_1_49' title="x_wconf -5">href="http://www.facebook.com/rcardo1echeta">http://facebook.com/ricardo1echeta</a><br><br</span></span></span>
<span class='ocr_line' id='line_1_14' title="bbox 552 1496 2908 1570"><span class='ocr_word' id='word_1_50' title="bbox 552 1531 598 1563"><span class='xocr_word' id='xword_1_50' title="x_wconf 0"><a</span></span> <span class='ocr_word' id='word_1_51' title="bbox 629 1496 2908 1570"><span class='xocr_word' id='xword_1_51' title="x_wconf -5">href="https://plus.goog1e.com/+RcardoLecheta">https://plus.goog1e.com/+RcardoLecheta</a</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_15' title="bbox 647 1602 717 1659"><span class='ocr_word' id='word_1_52' title="bbox 647 1602 717 1659"><span class='xocr_word' id='xword_1_52' title="x_wconf -2">

<string name="about_da1og_tt1e"Lvro Androd</strng>


<strng name="ok">OK

utilizando o A1ertDa1og. Se voc sobrescrever o mtodo onCreateVew(), ter de re


tornar uma view com pelo menos um boto que ser usado para fechar a janela,
e tambm um ttulo para o alerta. Eu particularmente gosto de sobrescrever 0
mtodo onCreateDa1og(Bund1e) para casos simples como esse, pois podemos utilizar
um A1ertDa1og que j insere um ttulo na janela e cria um boto OK para fech-l8

AboutDiaIog.java
package br.com.1vroandrod.carros.fragments;
import androd.app.A1ertDa1og;
Captulo 14 I WebView 429

import android.app.Dialog;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import livroandroid.lib.utils.AndroidUtils;

public class AboutDialog extends DialogFragment {


// Mtodo utilitrio para mostrar o dialog
public static void showAbout(android.support.v4.app.FragmentManager fm) {
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.ndFragmentByTag("dialog_about");
if (prev != null) {
ft.remove(prev);
}

ft.addToBackStack(null);
new AboutDialog().show(ft, "dialog_about");
}

@0verride
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Cria o HTML com o texto de about
SpannableStringBuilder aboutBody = new SpannableStringBuilder();
String versionName = AndroidUtils.getVersionName(getActivity());
aboutBody.append(Html.fromHtml(getString(R.string.about_dialog_text, versionName)))
// Ina o layout
Layoutlnater inater = Layoutlnater.from(getActivity());
Textview view = (TetView) inater.inate(R.layout.dialog_about, null);
view.setTet(aboutBody);
view.setMovementMethod(new LinkMovementMethod());
// Cria o dialog customizado
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.about_dialog_title)
.setView(view)
.setPositiveButton(R.string.ok,
new Dialoglnterface.0nClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}

)
.create();
}

}
430 Google Android - 4= edio
Veja que estamos utilizando a classe android.support.v4.app.DialogFragnent do pacote
de compatibilidade c no a classe nativa andjroid.app.DialogFragnent. Tambm estou
utilizando a classe livroandroid.lib.utils .AndroidUtils da biblioteca android-utils para
retornar o cdigo da verso do aplicativo, que o atributo versionName cadastrado
no app/buildgradle. Depois de criar o Fragmentialog, vamos atualizar o cdigo da
classe SiteLivroFragment; assim, quando 0 usurio clicar no link Mais Informaes da
pgina, o evento ser interceptado para abrir o alerta.

SiteLivroFragment.java
@0verride
public boolean should0verrideUrlLoading(webview view, String url) {
Log.d("livroandroid", "webview url: " + url);
if (url != null && url.endsHith("sobre.htn")) {
AboutDialog.showAbout(getFragnentHanager());
}

return super.shouldOverrideUrlLoading(view, url);


}

O resultado podemos conferir na gura 143. Veja que o alerta mostrou o ttulo
livro Android e o boto OK que fecha a janela. Se voc clicar no boto voltar, a janela
tambm ser fechada, pois o fragment foi adicionado na back stack. Observe
que no cdigo-fonte utilizei a classe Html.fromHtml(string) para mostrar um cdigo
HTML dentro do Textview. Inclusive o usurio pode clicar nos links para visualizar
a pgina no browser.

Livro Android

Apllcatlvo dos Carros 201 S


Versao l O

I Y r
ljflu "ll\1f(j.n 1j V I

ll1
OK

Figura 14.3 - FragmentDialog.


Captulo 14 I WebView 431
Aproveitando que zemos a classe AboutDialog, vamos ajustar a classe MainActivity do
projeto, pois no captulo anterior deixamos o menu /res/menu/menu_main.xml com
um item de menu chamado Sobre, que mostrado nas opes do menu overow
da action bar, ou seja, a Toolbar.

MainActivty.java

public boolean on0ptionsItenSelected(MenuIten item) {


int id = iten.getItenId();
if (id == R.id.action_about) { // Mostra o dialog com informaes do aplicativo
Aboutbialog . showAbout(getSupportFragnentManager( ) ) ;
return true;
}

return super . on0ptionsItemSelected(item);


i

14.5 Executando JavaScript


Para que o webview execute qualquer script na pgina, precisamos habilitar o
JavaScript, conforme demonstrado a seguir:
webettings settings = webview.getSettings();
settings . setJavaScriptEnabled(true);

Feito isso, qualquer cdigo JavaScript vai funcionar no webview e inclusive voc
poder injetar cdigos JavaScript dentro do webview, como por exemplo:
webView.loadUrl( "javascript:alert( 'Ol' " );

Eu no recomendo esse tipo de tcnica, mas j houve casos em que precisei alterar
o CSS da pgina web ( qual eu no tinha acesso ao servidor) e z isso injetando
um JavaScript na pgina.

14.6 Comunicao do JavaScript com a classe Android


Outra funcionalidade interessante e perigosa a possibilidade de a pgina Web
executar mtodos que esto dentro da activity ou fragment que contm o webview.
Para isso, basta chamar o mtodo addJavascriptInterface(object, noneObjeto) do
webview para congurar o objeto que vai conter os mtodos que o JavaScript pode
chamar. O primeiro parmetro a instncia do objeto que contm os mtodos, e
o segundo parmetro o nome do objeto que o JavaScript vai utilizar.
432 Google Android - 4 edia
O cdigo a seguir mostra como expor um objeto "LivroAndroid" para a pgina.

SiteLivroFragment.java

@Override
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) [
View view = inater.inate(R.layout.fragnent_site_livro, container, false);
webview = (webview) view.ndViewById(R.id.webview);

congJavaScript();
return view;
}

private void congJavaScript() {


HebSettings settings = webview.getSettings();
// Ativa o JavaScript na pgina
settings.setJavaScriptEnabled(true);
// Publica a interface para o JavaScript
webview.addJavascriptInterface(new LivroAndroidInterface(), "LivroAndroid");
}
class LivroAndroidInterface {
@JavascriptInterface
public void sobre() {
toast("Clicou na gura do livro!");
}

Dica: a partir do Android 4.3 (Jelly Bean), somente os mtodos que forem anotados
com @JavascriptInterface sero expostos ao webview. Isso feito por questes de
segurana, pois antigamente a pagina podia chamar qualquer metodo.

, _ Cl
Isso signica que na p inaemos
g po web usar
d o objeto "LivroAndroid" para exe
cutar os metodos que esto denidos na classe LivroAndroidInterface, pois isso
que essa linha de cdigo faz.
webview.addJavascriptInterface(new LivroAndroidInterface(), "LivroAndroid");

56 VOC Olhar 0 codigo-fonte da pgina sobre htm na pgina do livro ver ue ele
esta chamando o seguinte JavaScript ao clicar na gura:
CaPtuIo 14nWebView 433
<5Cl`D'C 'CllDe="text/javascript">
function sobre() {
// Funo JavaScript que chama o mtodo na Activity
LivroAndroid . sobre( );
}

Para testar a brincadeira, execute o projeto novamente e clique na gura do livri


nho. Isso vai chamar o mtodo sobre() dentro da classe SiteLivroFragnent.

14.7 Mostrando cdigo HTML no WebView

Eu no vou prolongar muito o assunto sobre webview, at porque recomendo que


sempre que possvel toda a interface seja feita de forma nativa. Mas uma ltima
dica de que talvez voc possa precisar sobre criar um cdigo HTML em String
e mandar abrir no webview.

Isso pode ser feito chamando o mtodo LoadData():


webview.1oadData(" HTML aqui

14.8 Links teis

Neste captulo, aprendemos detalhes importantes sobre o webview e como utilizar


a classe FragnentDial.og. Para continuar seus estudos, separei alguns links.
Android Developers - Building Web Apps in WebView

http://developer: android.com/guide/webapps/webview. html


WebView API

http://developer android.com/reference/android/webkit/WebWew.html
Android Developers Blog - Using DiaIogFragments

http://android-developers.blogspot.com.br/2012/05/using-dialogfragments.html
DialogFragment API

http://developer android.com/reerence/android/app/DialogFragment.html
Y i* CAPTULO, 15
z- 41

RecycIerView e tabs
H
pl

Neste captulo, vamos criar a lista de carros e aproveitar para revisar os conceitos
do Material Design. O objetivo gui-lo passo a passo no desenvolvimento de
um aplicativo Android. Deixaremos toda a estrutura da lista pronta para quando
formos buscar os carros do web service.

15.1 Criando as classes de domnio

Para continuar o projeto dos carros, vamos criar a classe Carro, mas antes crie o pacote
br .com.livroandroid . carros .domain com o objetivo de armazenar as classes de domnio Uma
maneira de fazer isso clicar com o boto direito no pacote br.com.livroandroid.carros
e usar o menu New> Padtage; depois digite domain no nome do pacote.
Feito isso, crie as classes Carro e CarroService neste pacote, conforme a gura 15.1.

Carro.java
package br.com.livroandroid.carros.domain;
import java.io.Serializable;
public class Carro implements Serializable {
private static nal long serialVersionUID = 66G10667668324739S9L;
public long id;
public String tipo;
public String nome;
public String desc;
public String urlFoto;
public String urllnfo;
public String urlvideo;
public String latitude;
public String longitude;
@0verride

434
(Ptu|o 15 n RecycIerView e tabs 435
public String toString() {
return "Carro{" + "nome='" + nome + '\" + '}';
}

'l l l 'Fi l 'n W ll -'L-' 1 tlff" 'f


v ' ' fr f V- - VI _ JI..
Elmanifests
AU v Cliava
V EIbr.com.Iivroandroid.carros
g Eactlvtty
yu
Eladapter
'h Carro
V 'ia CarroService
Elfragments
@ 'in CarrosAppIication
EI br.com.Iivroandroid.carros (androidTest)
Eres
Gradle Scripts

Figura 15.1 - Pacote com as classes de domnio.

Dica: para gerar o mtodo toString() de uma classe, utilize o menu (ode > Generate
> t0String() e escolha os atributos que voc deseja imprimir. Esse mesmo wizard
apresenta opes para gerar construtores e mtodos do tipo getters e setters e
tambm pode ser invocado pela tecla de atalho Ctrl+Insert.

A classe CarroService contm o mtodo getCarros(contet) para criar uma lista de


carros de forma xa em memria. Futuramente essa lista ser criada a partir de
um web service.

CarroService.java
package br_cormlivroandroid.carros.domain;

public class CarroService {


public static List<Carro getCarros(Contet context) {
List<Carro carros = new ArrayList();
for (int i = 0; i < 20; ++) i
Carro c = new Carro();
c.none = "Carro " + ;
c.desc = "Desc " + i;
c.urlFoto = "httpz//www.livroandroid.com.br/livro/carros/esportivos/Ferrari_FF.png";
carros.add(c);

return carros;
}

}
4% Google Android - 4 edio
15.2 Criando a lista de carros
Para criar a lista de carros, utilizaremos o Recyclervew e Cardvew que j estudamos,
Portanto vamos ser rpidos, pois queremos entrar logo nas partes mais interes_
santes do projeto.
Para comear, declare a dependncia das bibliotecas, inclusive do Picasso
(https://github.com/square/picasso), que uma excelente biblioteca utilizada para o
download de imagens.

app/build.gradIe

Compile 'com.android.support:recyclervew-v7:22.1.0'
compile 'com.androd.supportzcardvew-v7:22.1.0'
comple 'com.squareup.pcasso:picasso:2.5.2'

Como nosso objetivo criar a lista de carros, vamos comear pelo layout do
adapter, que tem um Textvew para o nome e um Imagevew para a foto.

/res/layout/adapter_tarro.xmI
<?ml version="1.0" encodng="utf-8"?>
android.support.v7.wdget.CardView xmlns:androd="http://schemas.androd.com/apk/res/android"
m1ns:card_vew="http://schemas.androd.com/apk/res-auto"
androd:d="@+d/card_vew"
androd:1ayout_wdth="match_parent" android:layout_heght="match_parent"
androd:1ayout_margn="6dp" androd:c1ckable="true"
card_vew:cardBackgroundCo1or="@co1or/white" card_vew:cardCornerRadius="2dp"
card_vew:cardE1evation="6dp"
android:foreground="?attr/selectableltenackground">
<LnearLayout
androd:1ayout_wdth="natch_parent"
androd:1ayout_heght="?androd:attr/1stPreferredItemHeght"
androd:gravty="center_vertcal" android:orientation="hortzonta1">
<FrameLayout

androd:1ayout_wdth="wrap_content" androd:layout_heght="wrap_content">
</span></span> <span class='ocr_word' id='word_1_125' title="bbox 955 3383 1051 3425"><span class='xocr_word' id='xword_1_125' title="x_wconf -1">Foto</span></span> <span class='ocr_word' id='word_1_126' title="bbox 1080 3381 1128 3426"><span class='xocr_word' id='xword_1_126' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_127' title="bbox 1158 3395 1282 3428"><span class='xocr_word' id='xword_1_127' title="x_wconf -2">carro</span></span> <span class='ocr_word' id='word_1_128' title="bbox 1316 3400 1385 3424"><span class='xocr_word' id='xword_1_128' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 825 3467 1639 3530"><span class='ocr_word' id='word_1_129' title="bbox 825 3467 1078 3519"><span class='xocr_word' id='xword_1_129' title="x_wconf -4"><ImageView</span></span> <span class='ocr_word' id='word_1_130' title="bbox 1107 3467 1639 3530"><span class='xocr_word' id='xword_1_130' title="x_wconf -4">androd:d="@+d/img"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 916 3551 2464 3626"><span class='ocr_word' id='word_1_131' title="bbox 916 3551 1627 3614"><span class='xocr_word' id='xword_1_131' title="x_wconf -4">android:1ayout_wdth="116dp"</span></span> <span class='ocr_word' id='word_1_132' title="bbox 1661 3563 2381 3625"><span class='xocr_word' id='xword_1_132' title="x_wconf -4">androd:1ayout_height="50dp"</span></span> <span class='ocr_word' id='word_1_133' title="bbox 2417 3570 2464 3626"><span class='xocr_word' id='xword_1_133' title="x_wconf -1">/</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 825 3638 2136 3705"><span class='ocr_word' id='word_1_134' title="bbox 825 3638 918 3678"><span class='xocr_word' id='xword_1_134' title="x_wconf -1"><!--</span></span> <span class='ocr_word' id='word_1_135' title="bbox 952 3639 1076 3682"><span class='xocr_word' id='xword_1_135' title="x_wconf -2">Barra</span></span> <span class='ocr_word' id='word_1_136' title="bbox 1106 3638 1153 3682"><span class='xocr_word' id='xword_1_136' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_137' title="bbox 1184 3652 1410 3694"><span class='xocr_word' id='xword_1_137' title="x_wconf -2">progresso</span></span> <span class='ocr_word' id='word_1_138' title="bbox 1440 3651 1642 3699"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">enquanto</span></span> <span class='ocr_word' id='word_1_139' title="bbox 1672 3661 1848 3705"><span class='xocr_word' id='xword_1_139' title="x_wconf -1">carrega</span></span> <span class='ocr_word' id='word_1_140' title="bbox 1880 3665 1900 3697"><span class='xocr_word' id='xword_1_140' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_141' title="bbox 1933 3652 2031 3698"><span class='xocr_word' id='xword_1_141' title="x_wconf -3">foto</span></span> <span class='ocr_word' id='word_1_142' title="bbox 2067 3670 2136 3693"><span class='xocr_word' id='xword_1_142' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 825 3723 1897 3792"><span class='ocr_word' id='word_1_143' title="bbox 825 3723 1127 3775"><span class='xocr_word' id='xword_1_143' title="x_wconf -2"><ProgressBar</span></span> <span class='ocr_word' id='word_1_144' title="bbox 1158 3724 1897 3792"><span class='xocr_word' id='xword_1_144' title="x_wconf -5">androd:d="@+td/progresslmg"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_36' title="bbox 917 3806 2119 3876"><span class='ocr_word' id='word_1_145' title="bbox 917 3806 2119 3876"><span class='xocr_word' id='xword_1_145' title="x_wconf -4">style="@androd:style/wdget.ProgressBar.Smal1"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_37' title="bbox 916 3891 2768 3973"><span class='ocr_word' id='word_1_146' title="bbox 916 3891 1807 3958"><span class='xocr_word' id='xword_1_146' title="x_wconf -7">dmd213)/0Ut_Wdth="wrap_content"</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1842 3910 2768 3973"><span class='xocr_word' id='xword_1_147' title="x_wconf -4">androd:1ayout_height="wrap_content"</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 15 I RecycIerView e tabs 437
androd:1ayout_gravty="center|center_vertcal" androd:1ayout_marginRght="6dp"
androd:gravty="center|center_vertca1" androd:vsib1ity="invsb1e" />

<TextView android:d="@+d/text"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="wrap_content"
androd:1ayout_margnLeft="10dp" android:tetCo1or="@co1or/primary" />
</LnearLayout>
/android.support.v7.wdget.CardView

O atributo androd:foreground="?attr/se1ectab1eItemBackground" para o Cardvew criar


o efeito de ripple no Android 5.0 ou superior, e vai manter a compatibilidade
simulando algum efeito de toque nas verses antigas. Lembrando que o efeito
de ripple ou qualquer outro muito importante para fornecer o feedback ao
toque (Touch Feedback) para o usurio. Por padro, o efeito de ripple apresenta
uma cor cinza claro. Para denir a cor, customizamos o tema com a propriedade
co1orContro1High1ght.

/res/values/styIes.xmI

<tem name="co1orContro1Hgh1ight">@co1or/contro1_hgh1ght</tem>

/res/values/coIors.xmI

<co1or name:"contro1_high1ight">#B3E5FC

Se voc utilizou a mesma cor que est neste livro, o efeito de ripple azul claro.
Lembrando que todas as cores so baseadas na paleta de cores do Material Design.
http://www. google. com/design/spec/style/color html

Ateno: cuidado para no deixar a cor co1orContro1High1i.ght igual cor primria


do aplicativo, pois s vezes o efeito ripple pode no aparecer se o componente
utiliza a mesma cor. Mais para frente, vamos fazer a tab (clssicos, esportivos,
luxo) ser da cor primria do aplicativo, pois o padro do Material Design. Neste
caso, se a cor do co1orContro1Hgh1ight for igual, voc no vera o efeito de rippleiao
selecionar uma tab portanto tome cuidado. Esse probleminha aconteceu comigo
1

e levei tempo para descobrir o motivo de o efeito nao aparecer.

A seguir podemos ver o cdigo da classe de adapter que vai utilizar o layout
/res/layout/adapter_carro.xml qu 21C21b1T105 de Criar
438 Google Android - 4= edio

Carro1\dapter.java
package br.com.livroandroid.carros.adapter;

public class CarroAdapter extends Recyclerview.Adapter {


protected static nal String TAG = "livroandroid";
private nal List<Carro carros;
private nal Context context;
private Carro0nClickListener carro0nClickListener;
public CarroAdapter(Context context, List carros, Carro0nClickListener
carro0nClickListener) {
this.contet = context;
this.carros = carros;
this.carro0nClickListener = carro0nClickListener;
}

@Override
public int getItemCount() { return this.carros != null ? this.carros.size() : 6; }
@Override
public CarrosViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = Layoutlnater.from(contet).inate(R.layout.adapter_carro, viewroup, false);
CarrosViewHolder holder = new CarrosViewHolder(view);
return holder;
}

@Override
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) {
// Atualiza a view
Carro c = carros.get(position);
holder.tNome.setText(c.nome);
holder.progress.setVisibility(View.VISIBLE);
// Faz o download da foto e mostra o ProgressBar
Picasso.with(context).load(c.urlFoto).t().into(holder.img
new com.squareup.picasso.Callback() {
@Override
public void onSuccess() {
holder.progress.setVisibility(View.GONE); // download ok
}

@Override
public void onError() {
holder.progress.setVisibility(View.GONE);
}

});
Captulo 15 I Recyc|erView e tabs 439
// Click
if (carro0nClickListener != null) {
holder.itemView.set0nClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// A varivel position nal
carro0nClickListener.onClickCarro(holder.itemview, position);
}
});
}

public interface Carro0nClickListener {


public void onClickCarro(View view, int idx);
}
// ViewHolder com as views
public static class CarrosViewHolder extends RecyclerView.ViewHolder {
public Textview tNome;
Inageview img;
ProgressBar progress;
Cardview cardView;
public CarrosViewHolder(View view) {
super(view);
// Cria as views para salvar no ViewHolder
tNome = (Textview) view.ndViewById(R.id.text);
img = (InageView) view.ndViewById(R.id.img);
progress = (ProgressBar) view.ndViewById(R.id.progressIng);
cardview = (Cardview) view.ndViewById(R.id.card_view);
}

Dica: para carregar a foto do carro, estamos utilizando a biblioteca Picasso,


que facilita o download e inclusive faz cache das imagens. No cdigo usamos
um ProgressBar para fazer a animao durante o download e a interface
con.squareup.picasso.Callback para receber o evento quando o carregamento da
imagem terminar. Outra opo seria utilizar o Picasso apenas com uma imagem
temporria (placeholder) durante o download, mas o exemplo com ProgressBar e
mais difcil e estamos fazendo assim por motivos de aprendizado.

A esta altura do livro, o cdigo do adapter deve ser tranquilo para voc, portanto vamos
continuar. Adicione um Recyclerview no layout do fragment que vai listar os carros.
440 Google Android - 4 edio
/res/layout/fragment__carros.xmI
<?xnl version="1.0" encoding="utf-8"?> ,
<FrameLayout xnlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:padding="14dp">
android.support.v7.widget.Recyclerview android:id="@+id/recyclerview"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:cacheColorHint="@android:color/transparent"
android:clipToPadding="false"
android:divider="@null" android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"
android:scrollbarStyle="outside0verlay" android:scrollbars="vertical" /

Na classe CarrosFragnent vamos utilizar o mtodo CarroService.getCarros(context) para


obter a lista de carros a m de preencher o adapter do Recyclerview.

(arrosFragment.java
package br.con.livroandroid.carros.fragnents;

public class CarrosFragnent extends BaseFragnent {


protected Recyclerview recyclerview;
private List carros;
private LinearLayoutManager nLayoutManager;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_carros, container, false);
recyclerview = (Recyclerview) view.ndViewById(R.id.recyclerview);
mLayoutManager = new LinearLayoutManager(getActivity());
recyclerview.setLayoutManager(mLayoutManager);
recyclerview.setItemAnimator(new DefaultItemAnimator());
recyclerview.setHasFiedSize(true);
return view;
}

@0verride

public void onActivityCreated(@Nullable Bundle savedInstanceState) {


super.onActivityCreated(savedInstanceState);
taskCarros();
}

private void taskCarros() {


// Busca os carros
Captulo 15 n RecycIerView e tabs 441
this.carros = CarroService.getCarros(getContet());
// Atualiza a lista
recyclerview.setAdapter(new CarroAdapter(getContet(), carros, onClickCarro()));
}

private CarroAdapter.Carro0nClickListener onClickCarro() {


return new CarroAdapter.CarroOnClickListener() {
@0verride
public void onClickCarro(View view, int idx) {
Carro c = carros.get(idx);
Toast.makeTet(getContext(), "Carroz " + c.nome, Toast.LENGTH_SHORT).show();
}

};
}

O mtodo onActivityCreated(bundle) do fragment geralmente um bom lugar para


iniciar a lgica da tela, como por exemplo buscar dados do web service ou do
banco de dados. Nele estou chamando o mtodo taskCarros() para criar a lista de
carros e preencher a lista. Por enquanto, o mtodo taskCarros() no faz muita coisa,
mas j estamos preparando a estrutura do cdigo para quando formos utilizar
threads (AsyncTask) para buscar os carros do web service.
Concludos esses passos, execute o projeto, e o resultado deve ser como a gura
15.2. Ao selecionar um carro da lista, o seu nome exibido em um toast. Conforme
explicado anteriormente, adicionamos o item "?attr/selectableltenBackground" no
layout do adapter, assim temos o efeito de ripple ao tocar no Cardview.

I_
I
1

1
_
,Q _. .
U =., .
..,

a
'-_ I'- | __, r *
z _
"*':;
f
Cano?

Cam) 2 Carro 2 K
`_. , , __.

`_, z~-
. Carro3 , __,
V z ,___ _' ..*Carro3
~ 1I
i_W___: __,_'_w, _____,f_ __-._-. Carro: Carro 2
Carro4 C8"4
- _ |\ ___,

Figura 15.2 - Lista de carros.


442 Google Android - 4 Edio
15.3 Tabs e ViewPager
j criamos a lista de carros, mas no aplicativo precisamos de trs tabs, que so;
clssicos, esportivos e luxo.
Conforme explicado no captulo 5, sobre action bar, a navegao por tabs utili
zando a action bar foi descontinuada (deprecated). Porm, no Google l/O 2015, 0
Google anunciou a biblioteca Android Design Support Library que contm classes,
e componentes para auxiliar na construo de aplicativos com Material Design,
Dentre esses componentes foi criado o TabLayout, utilizado para criar as tabs.
Para utilizar essa nova biblioteca, declare a dependncia no app/buildgradle.

app/build.gradIe

compiie 'com.android.support:design:22.2.0'

Ento vamos colocar a mo na massa. Crie um novo fragment chamado


CarrosTabFragment com este arquivo de layout, que contm o TabLayout com ViewPager:

/res/layout/fragment_carros_tab.xmI
<?ml version="1.0" encoding="utf-8"?
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:orientation="vertica1"

<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent" android:1ayout_height="wrap_content"
android:background:"@co1or/primary" />

<android.support.v4.vieu.ViewPager android:id="@+id/viewpager"
android:layout_width="match_parent" android:1ayout_height="9p"
android:layout_weight="1" android:background="@android:color/white" />

CarrosTabFragment.java

package br.com.1ivroandroid.carros.fragments;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
Captulo 15 n Recyclerlew e tabs 443
public class CarrosTabFragment extends BaseFragment
implements TabLayout.OnTabSelectedListener {
private ViewPager mViewPager;
private TabLayout tabLayout;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_carros_tab, container, false);
// ViewPager
nViewPager = (ViewPager) view.ndViewById(R.id.viewpager);
mViewPager.setOffscreenPageLimit(2);
nViewPager.setAdapter(new TabsAdapter(getContet(), getChildFragmentManager()));
// Tabs
tabLayout = (TabLayout) view.ndViewById(R.id.tabLayout);
int cor = getContet().getResources().getColor(R.color.white);
// Cor branca no texto (o fundo azul foi denido no layout)
tabLayout.setTabTextColors(cor, cor);
// Adiciona as tabs.
tabLayout.addTab(tabLayout.newTab().setTet(R.string.classicos));
tabLayout.addTab(tabLayout.newTab().setTet(R.string.esportivos));
tabLayout.addTab(tabLayout.newTab().setTet(R.string.luxo));
// Listener para tratar eventos de clique na tab
tabLayout.set0nTabSelectedListener(this);
// Se mudar o ViewPager atualiza a tab selecionada
mViewPager.addOnPageChangeListener(new TabLayout.TabLayout0nPageChangeListener(tabLayout));
return view;
}

@Override
public void onTabSelected(TabLayout.Tab tab) {
// Se alterar a tab, atualiza o ViewPager
mViewPager.setCurrentIten(tab.getPosition());
}

@Override
public void onTabUnselected(TabLayout.Tab tab) { }
@0verride
public void onTabReselected(TabLayout.Tab tab) { }
}

Como podemos ver, o TabLayout da biblioteca Android Design simples e funciona


da mesma forma que no exemplo feito com tabs na action bar. Porm, quem con
trola todo o contedo da tela e das tabs o ViewPager, pois ele que vai fornecer
O Contedo de Cada pgina por meio de um adapter. Na prtica, a tab apenas
mostra a pgina que est selecionada no ViewPager.
444 Google Android - 4 edig
Observe que esta linha de cdigo faz com que o ViewPager mantenha vivas sempre
duas tabs a mais do que ele est visualizando. Assim, todas as trs tabs cam
sempre em memria. Como so poucas informaes, no caso do apllcanvo dgs
carros isso ca bom.
viewPager.set0ffscreenPageLimit(2);

Para prosseguir, crie a classe do adapter que vai retornar os fragments para 0
viewPager. Como as trs tabs vo mostrar a lista de carros, usaremos sempre O
fragment CarrosFragment que contm a lista, mas vamos passar um argumento pelo
Bundle para indicar o tipo de carro que ser mostrado nalista.

TabsAdapter.java
package br.com.livroandroid.carros.adapter;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class TabsAdapter extends FragmentPagerAdapter {
private Context context;
public TabsAdapter(Context context, FragmentManager fm) {
super(fm);
this.context = context;
}

@0verride
public int getCount() { return 3; }
@0verride
public CharSequence getPageTitle(int position) {
if (position == 0) {
return context.getString(R.string.classicos);
} else if (position == 1) {
return context.getString(R.string.esportivos);
}

return context.getString(R.string.luxo);
}

@0verride

public Fragment getItem(int position) {


Bundle args = new Bundle();
if (position == 0) {
args.putString("tipo", "classicos");
} else if (position == 1) {
args.putString("tipo", "esportivos");
Calltulo 15 n RecycIerView e tabs 445
} else {
args.putString("tipo", "luxo");
}

Fragment f = new CarrosFragnent();


f.setArgunents(args);
return f;
}

Nota: lembre-se de que no cdigo sempre vamos utilizar as verses de


compatibilidade V4 dos fragments. Tenha ateno ao fazer os imports.

Para o cdigo compilar, adicione os textos das tabs no arquivo stringsxml.

/res/values/strings.xmI

<string name="classicos">Clssicos
<string name="esportivos">Esportivos
<string name="luo">Luo

Neste momento, se voc executar o projeto as tabs e o ViewPager j vo funcionar,


porm as trs tabs vo mostrar a mesma lista de carros. Portanto, vamos aprimorar
a lgica da classe CarrosFragnent para ler o tipo do carro dos argumentos e criar
uma lista conforme esse tipo.

CarrosFragment.java

public class CarrosFragnent extends BaseFragnent {

private String tipo;


@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArgunents() != null) {
this.tipo = getArgunents().getStrin9("fP");
}

}
W Google Android - 4 edio
private void taskCarros() {
this.carros = CarroService.getCarros(getCntet(), tipo);

Para o cdigo compilar, adicione o parmetro tipo no mtodo getCarros(contet,tipo)_

CarroService.java
\
000
public static List getCarros(Context context, String tipo) {
for (int 1 = o; 1 zo; 1++`{
Carro c = new Carro();
c.none = "Carro " + tipo + ": " + i; // Nome dinmico conforme o tipo

Note que s preciso alterar uma linha nesse cdigo, apenas para o nome do
carro conter o tipo selecionado. Por enquanto, a lista de carros continuar esttica
para facilitar a construo da navegao do projeto.
Para nalizar a congurao, precisamos alterar o cdigo que trata o evento do
Navigation Drawer, para mostrar o fragment com as Tabs ao selecionar a opo Carros.
Altere o mtodo onNavDrawerItemSelected(. . .) para mostrar a classe CarrosTabFragnent
em vez da CarrosFragment, conforme demonstrado a seguir.

MainActivity.java

@0verride
public void onNavDrawerItemSelected(NavigationDrawerFragment navigationDrawerFragment,
int position) {

if (position == G) {
replaceFragnent(new CarrosTabFragment());
} else if (position == 1) { . . . }
} else if (position == 2) { . . .}
}

O resultado deve ser como a gura 153, que mostra a tab Esportivos selecionada.
Captulo 15 I RecycIerView e tabs 447

Carro esportivos: D
_ ._._e,....-._..... ,_-... a. W., .,_

Carro esportivosti
_.-.. ,~..z.,.-..._......, ..,,. ..._,._,___,- ,,._T__

Carro esportivos: 2

. Carro esportivos: 3
._......._-.T......_ .., . ...v,..:,.,._.,;.--...._T. _ T. _. ,,__",_,,___,__,

..~=n-..

Figura 15.3 - Tabs.

15.4 Navegao de telas


Ao selecionar um carro na lista, 0 aplicativo deve navegar para a activity que mos
tra a tela de detalhes do carro, ento vamos l. Serei breve nas explicaes, pois j
estudamos isso no livro, portanto aproveite para revisar os conceitos e praticar.
Crie as classes CarroActvty e CarroFragment conforme demonstrado a seguir. Note que
a palavra "carro" no nome das classes est no singular. Para a activity recomendo
utilizar o Wizard New> Activity, pois o wizard, alm de criar os arquivos, tambm
configura a activity no arquivo AndroidManiest.xml.
A classe CarroActvty receber o objeto do carro como parmetro pelo Bundle e
envia esse objeto para o fragment por meio do mtodo setCarro(carro), que por
sua vez responsvel tanto pelo layout quanto pelo contedo da tela.

CarroActivity.java
package br.com.livroandrod.carros.actvity;
public class CarroActvty extends BaseActvty {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVew(R.1ayout.actvty_carro);
448 Google Android - 4 edio

// Congura a Toolbar como a action bar


setUpToolbar();
// Atualiza o carro no fragment
CarroFragnent cf = (CarroFragnent) getSupportFragmentManager()
.ndFragmentById(R.id.CarroFragnent);
Carro c = (Carro) getIntent().getSerializableExtra("carro")
cf.setCarro(c);
]/ Titulo da toolbar e boto up navigation
getSupportActionBar().setTitle(c.none);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

O layout da activity apenas insere o fragment CarroFragnent que cuidar do conte


do da tela. Foi dado um id para o fragment, pois assim podemos obt-lo com
o mtodo findFragnentById(id). Tenha ateno aos layouts das activities; todas elas
precisam incluir a Toolbar, pois lembre-se de que desativamos a action bar padro.

/res/layout/activity_carro.mI
<LinearLayout mlns:android="http://schemas.android.con/apk/res/android"
nlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" >
include layout:"@layout/include_toolbar" /
<fragment android:id="@+id/CarroFragment"
class="br.con.livroandroid.carros.fragments.CarroFragment"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:layout:"@layout/fragment_carro" />

No arquivo AndroidManikst.xml configure a activity CarroActivity para que sua


activity me seja a MainActivity, assim podemos utilizar o up navigation.

AndroidManifest.mI
<application . . .>
<activity android:name=".activity.CarroActivity"
android:label:"@string/title_activity_carro"
android:parentActivityName=".activity.MainActivity" >
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:value=".activity.MainActivity" />
Captulo 15 I RecycIerView e tabs

Para o cdigo compilar, crie o fragment.

CarroFragment.java
package br.com.livroandroid.carros.fragments;
public class CarroFragment extends BaseFragnent {
private Carro carro;
@Override

public View onCreateView(Layoutlnater inater, ViewGroup container,


Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_carro, container, false);
return view;
}

// Mtodo pblico chamado pela activity para atualizar os dados do carro


public void setCarro(Carro carro) {
if (carro != null) {
this.carro = carro;
setTextString(R.id.tDesc, carro.desc);
nal Imageview ingview = (InageView) getView().ndViewById(R.id.ing);
Picasso.with(getContet()).load(carro.urlFoto).t().into(imgView);
}

/res/layout/fragment_carro.java
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.con/apk/res/android"
xnlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:orientation="vertical" android:padding="12dp">
<android_support.v7.widget.Cardview android:id="@+id/card_view"
android:layout_width="natch_parent" android:layout_height="match_parent'
android:clickable="true" card_view:cardBackgroundColor="@color/white"
card_view:cardCornerRadius="2dp" card_view:cardElevation="6dp">
<LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent
android:orientation="vertical" android:padding="Zdp">
<InageView and roid:id="@+d/ F19 "
android:layout_width="@dimen/foto_carro_width"
android : layout_height="@dimen/f0t0_CaFF_h@9ht"
android:layout_gravity="center" android:scaleType="center" />
<View android:background="@C010F/Qfy"
android:layout_width="match_parent" android:layout_height="1dp"
450 Google Android - 4= edio
androd:layout_nargnBottom="6dp" androd:layout_nargnTop="6dp" />
<ScrollVew
androd:layout_wdth="match_parent androd:layout_height="wrap_content"
<TetVew android:id="@+d/tDesc"
androd:layout_wdth="natch_parent" androd:layout_heght="match_parent"
androd:layout_weght="1" androd:textColor="@color/prmary_dark"
android:layout_margn="8dp"/>
</ScrollVew>
</LnearLayout>
</androd.support.v7.wdget.CardVew>
</LnearLayout>

/res/values/dimens.xm|
<I'SOUI`CS>

<dmen name="foto_carro_wdth">260dp
<dtnen nane="foto_carro_heght">110dp</dmen>

Para fazer a navegao de telas, altere o cdigo que trata o evento da lista.

CarrosFragment.java

public class CarrosFragment extends BaseFragment {


private CarroAdapter.CarroOnClickLstener onClckCarro() {

public void onClckCarro(View view, int idx) {


Carro c = carros.get(d);
Intent ntent = new Intent(getContext(), CarroActivty.c1ass);
ntent.putExtra("carro", c);
startActivity(intent);
}

Nota: a classe Carro implementa a interface de marcao java.i.o.Serialzable,


por isso podemos passa-la como parmetro pelo Bundle utilizando o mtodo
putEtra(strtng, serlalizable). Basicamente isso faz com que o objeto seja
serializado (convertido para bytes) para depois na outra activity fazer o processo
inverso (bytes para objeto). Vale ressaltar que na documentao do Android
recomendado utilizar a interface androd.os.Parcelable, pois mais performtica.
Captulo 15 I RecycIerView e tabs 451
Eu particularmente nunca senti nenhuma demora ao passar objetos como
Serialzable, portanto fao assim no meu dia a dia e explico deste jeito. Mas,
caso voc precise passar por parmetro um objeto realmente grande ou uma
lista de objetos e perceba algum atraso (delay) na navegao de telas, vale a pena
procurar como implementar a interface Parcelable.

O layout do fragment tem o Imageview para a foto e um Textvew para a descrio do


carro. Foi utilizado um Scrollview para fazer rolagem no caso de telas pequenas, pois a
descrio do carro pode exceder a altura disponvel da tela. Ao executar o aplicativo
e selecionar um carro na lista, o objeto do carro ser passado como parmetro pelo
Bundle, e a activity CarroActivty vai ler esse objeto para configurar o contedo do
fragment. Lembre-se de que a activity responsvel pela navegao e o fragment
pelo contedo. O resultado da navegao de telas deve ser como a gura15.4:

..,. . . .
i Serro esportivos: O

z

.-v-1
.`_ ._, 4l
Carro esforivnsi

, Cam: spczzrrwosi 3
Figura 15.4 - Navegao de telas.

IO
15.5 L|nks uteis
_ - - ' ' ` ca tulo
Neste captulo, implem entamos a lista de carros e revisamos importantes con
ceitos do desenvolvimento de aplicativos para Ar1dI01d- NO Pmxlmo P
vamos buscar a lista de car ros de um Web service. Para continuar seus estudos,
separei um link.
Android Training - Creating Lists d Cafdf*

httpg-//developer android.com/training/material/lists-cards.html
` cAPruLo 16
Parser de XML, JSON e testes
`**. unitrios
1

Geralmente, web services retornam os dados no formato XML ou JSON. Neste


captulo, vamos aprender como ler esse tipo de informao.

16.1 Lendo um arquivo local da pasta /res/raw


Para comear a brincadeira, faa o download destes trs arquivos XML e insira-os
na pasta /res/raw do projeto dos carros:
http://w wwlivroandroid. com. br/livro/carros/carr0s_classicos.xml
http://wwwlivroandroid. com.br/livro/carros/carros_esportivos.xm1
http://wwwlivroandroid.com.br/livro/carros/carros_lux0.xml
A pasta /res/raw utilizada para incluir arquivos estticos que so compilados
junto com o aplicativo. Se essa pasta no existir no projeto, clique com o boto
direito na pasta /res e utilize o menu New Directory para cri-la. Para sua validao, O
resultado deve ser como a figura 16.1.
Sempre que um arquivo for inserido na pasta /res/raw, ser gerado um recurso na
classe R, por exemplo: R. raw.nome_arquvo. Para ler esse arquivo, podemos utilizar O
mtodo getResources() .openRawResource(R.raw.x), que retorna uma InputStream.
Resources resources = contet.getResources();
InputStream in = resources.openRawResource(R.raw.nome_arquvo);

A interface java.to. InputStream dojava dene um fluxo de dados para leitura, c COIT1
ela podemos ler facilmente o arquivo utilizando a classe java.o.F1eInputStreaI'1
BCTTI, 21 partir daqui java puro, portanto, se voc j trabalhou com arquivos em
java vai se sentir em casa.

452
Captulo 16 n Parser de XML, JSON e testes unitrios 453
<5f@*e*==~mfic i , ;a t
it <=ff=-'===i=5~* sst, ts ts .
Wmjava
: mas 1
13
lpmanem r version="1.0" enood_'.ng=="utf~8

ii? ff
,,,, drawabh <!
,t nayout
V raw ]`I>
'I'u::ker 1948 xml
{CDA'I'A[
Emenu , O Tucker foi ,;`%M2l,^1;1`g;V1j;/gl uma _`j;~qy,^`fNg

, iicarros_ezsportivos.xmI E (url info)


l a"5-l''*m' http : / /hotrodekustom. blogsp
_ g W t; <url foto>
Figura 16.1 - Pasta /res/raw com arquivos XML.
|

Para facilitar seus estudos, criei a classe 1vroandrod.1b.ut1s.Fleutls, portanto


para ler um arquivo da pasta /res/raw em formato String basta utilizar este cdigo:
String xml = Filetls.readRawFi1eString(context, R.raw.carros_c1assicos, "UTF-8");

Neste caso, o arquivo /res/raw/carros_classicos.xml ser lido e convertido para St ring.

16.2 Parser de XML

Com o objetivo de criar a lista de carros, vamos aprender a ler o arquivo XML
que colocamos no projeto, o qual contm a seguinte estrutura:

/res/raw/carros_esportivos.xmI
<?xn1 version="1.0" encodng="utf-8"?>

<url_nfo>
<url_vdeo> </ur1_vdeo>
<1atitude> </1atitude>
<1ongitude> </1ongtude>

outros carros aqui


454 Google Androld - 4' ediao

Para ler 0 XML c criar a lista dc carros, atualize 0 cdigo da classe CarroServ\
cunibrme demonstrado a seguir.

ii CarroService.java

package br.com.livroandroid.carros.domain;

import livroandroid.lib.utils.Filetils;
import livroandroid.lib.utils.XMLUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class CarroService {
private static nal boolean LOG_0N = false;
private static nal String TAG = "CarroService";
public static List<Carro getCarros(Context context, String tipo) {
try {
String xml = readFile(context, tipo);
List<Carro carros = parserXML(context, xml);
return carros;
} catch (Exception e) {
// TODO explicar exception
Log.e(TAG, "Erro ao ler os carros: " + e.getMessage(), e);
return null;
}

// Faz a leitura do arquivo que est na pasta /res/raw


private static String readFile(Context context, String tipo) throws IOException {
if ("classicos".equals(tipo)) {
return Filetils.readRawFileString(context, R.raw.carros_classicos, "UTF-8");
} else if ("esportivos".equals(tipo)) {
return Filetils.readRawFileString(context, R.raw.carros_esportivos, "UTF-8")
}

return Filetils.readRawFileString(contet, R.raw.carros_luxo, "UTF-8");


}

// Faz o parser do XML e cria a lista de carros


private static List<Carro parserXML(Context context, String xml) {
List<Carro carros = new ArrayList();
Element root = XMLUtils.getRoot(xml, "UTF-8");
// Le todas as tags
List nodetarros = XMLUtils.getChildren(root, "carro");
// Insere cada carro na lista
for (Node node : nodetarros) {
Carro c = new Carro();
Captulo 'I6 I Parser de XML, JSON e testes unitrios 455
// L as informaes de cada carro
c.nome = XMLUti1s.getTet(node, "nome");
c.desc = XMLUtils.getTet(node, "desc");
c.ur1Foto = XMLUt1s.getTet(node, "ur1_foto");
c.ur1Info = XMLUt1s.getText(node, "ur1_info");
c.ur1Vdeo = XMLUti1s.getTet(node, "ur1_video");
c.1attude = XMLUt1s.getText(node, "1attude");
c.1ongitude = XMLUt1s.getText(node, "1ongtude");
if (LOG_ON) {
Log.d(TAG, "Carro " + c.nome + " > " + c.ur1Foto);
}

carros.add(c);
}

if (LOG_ON) {
Log.d(TAG, carros.sze() + " encontrados.");

return carros;
}

O mtodo CarroServce.getCarros(context, string) recebe o tipo do carro desejado,


que pode ser: esportivos, luxo ou clssicos. Baseado no tipo do carro, o arquivo
XML correto lido da pasta /res/raw. O retorno uma String no formato XML,
ento feito o parser e logo depois criada a lista de carros. Ao executar o projeto
novamente, o resultado deve ser como a figura 16.2, que mostra a lista de carros
criada com base no arquivo XML.

l . TUJ-_k,r V Ffffi BUQHVQYYO


1 tj=;v,zz : z;n;,~ r
_ D
ll 'czrc___p
rx V
l Convertible
^Um(T"pYd
_,.-. -ii p _
__,r_ Faafmnzo
,,__t_,_ ___ ,,,_ _"_,__._.,__-__,__..-._.---,.
. U H I L p P Bchepanam . L- h ghinmevento
l Y H_____(_,_H_ f_w______ _,,,_ ___,,.__.._._.......... l--~--~-- ----.~-~-~~-:~*-'^ft'-"'~"

r Cadzmcevilte LamborghinAvemador 1 Leblan Mi b

Figura 16.2 - Lista de carros do web service.


r
456 Google Android - 4 d
Como no layout do adapter /res/Iayout/adapter_carro.xml foi adicionado um
ProgressBar e utilizamos a biblioteca Picasso para fazer o download da imagem O
ProgressBar mostrado durante o download, conforme a figura 16%. Isso aconteg
porque no arquivo XML os dados dos carros esto reais, inclusive a URL da;
fotos. No lado direito da figura, podemos ver que a navegao de telas continua
funcionando, pois a nica diferena que todas as informaes vieram do XML

I) i
'>

1 j ,`..|e 3\ff:\"*"\.i3f

'JZ' '_

Figura 16.3 - ProgressBar e navegao de telas.


' _,.-' v.

Para finalizar este tpico, gostaria de ressaltar que o parser de XML pode ser feito
com SAX ou DOM. O SAX percorre o XML passo a passo, utiliza menos recursos
e por isso mais performtico. O DOM cria uma rvore em memria que repre
senta a estrutura do XML e permite acessar os elementos de forma rpida. Seo
XML for grande e voc precisar de desempenho, recomenda-se fazer o parser por
SAX, mas eu costumo utilizar o DOM pela simplicidade e nunca tive problemas. A
classe XMLUtls utilizada dentro do cdigo da classe CarroServce facilita justamente
a leitura do XML por DOM.
Tambm gostaria de ressaltar algo importante sobre o cdigo que fizemos na classe
CarroServce. Veja que o mtodo F1eUtls.readRawF1eStrng(context,raw) lana uml
exceo do tipo java.o. IOEcepton, e no mtodo CarroServce.getCarros(contet,tDl
estou fazendo o try/catch para tratar a exceo e imprimir a mensagem de erro DO
Log(at. Na prtica, isso no recomendado, pois dessa forma, se ocorrer qualquer
erro na leitura do arquivo, nenhum alerta ser dado no aplicativo e o usurio HO
ficar ciente do problema. Mas, neste momento, vamos deixar o cdigo assim.
pois o objetivo foi explicar como ler um arquivo da pasta /rcs/raw e fazer o parser
do XML. No captulo 17, sobre web services, vamos organizar melhor o cdigo C
propagar as excees adequadamente.
Captulo 16 I Parser de XML, JSON e testes unitrios 457
16.3 Parser de JSON

Outro formato de arquivo que recentemente cou bastante popular por sua sim
plicidade o JSON, o qual vem substituindo o XML como forma de integrao
de sistemas.

JSON (]avaScript Object Notation) uma estrutura de dados que representa um


ob]eto em JavaScript. Seu formato simples, conforme demonstrado a seguir:
{

"carros": {
"carro": [
{

"nome": "Carro 1",


"desc": "Desc Carro 1",
"url_info": "url aqui",
"ur1_foto": "url foto aqui",
"url_vdeo": "url video aqui",
"latitude": "latitude aqu",
"1ongitude": "longitude aqui"
},
{ // Prximo carro aqui. }
]

No site do livro existem estes arquivos JSON para cada tipo de carro:
http://wwwlivroandroid.com.br/livro/carros/carros_classicos.json

.,_
http://www.li1/roandroid.com.br/livro/carros/carros_esp0rti1/os.json

http://www.li1/roandroid.com.br/livro/carros/carros_luxo.json
Faa o download desses trs arquivos JSON e insira-os na pasta /res/mw do projeto
dos carros. importante que voc apague os arquivos XML que estavam na pasta
para no gerar conflitos na classe R. A figura 16.4 mostra o resultado.

Nota: ao copiar os arquivos .json para a pasta /res/raw, certique-se de apagar os


arquivos xml pois a classe R no leva em considerao a extensao dos arquivos
e neste caso o processo de compilao poderia se perder. Inclusive recomendo
qug Veee utilize O menu Build > Clean do Android Studio para limpar os arquivos
compilados a m de evitar qualquer COHHO
458 Google Android - 4' edio
i~~
1
V ` ' ' .,
i` fuw
,, t
-'__
cm, ,;, nc flcman mv i-;} t:auos,1usces;sm\

ll H 1 1:zo":
" a ros": i
L , mn H Y 42) -f \ Q; ((|YDz_\lSSH.0$j$0
"t """"'
_ menu . , ,
Y' d b "nome": "Tucker -.:f48 IS ,
E lwcm
_ l ari
,at xla ..
"desc":
1no":"O"nttp:/fhttxcuek
Tucker fo; r alma
'zg' os_cIassicos
z "~ ` *~ z"',*Jr*...fO*o
tt.z://'"'.li:1
_;- u,n' n.l'..i:.,
'-' 'NH' X '
carros_2SPom\.cS_<C "u*_*7*Qe hit? - ' f' l""'`^ - J~ ~ ~
, '_saiues
_ :rrtis_lux0.j50 " l at 1 tude": " '* 2 3 . 4 22 4 " 1
3`GrdlcSripts
"lonq:Ltude":"-46.6531369

Figura 16.4 - Pasta /res/raw com arquivos ]SON.

Depois de copiar os arquivos .json para a pasta /res/raw, atualize o cdigo da clas
se CarroService para fazer o parser de JSON. Basicamente vamos criar o mtodo
parserJSON(context,json) para criar a lista de carros.

CarroService.java
package br.com.livroandroid.carros.domain;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSON0bject;
public class CarroService {
private static nal boolean LOG_0N = false;
private static nal String TAG = "CarroService";
public static List getCarros(Context context, String tipo) {
try {
String json = readFile(context, tipo);
List carros = parserJSON(context, json);
return carros;
} catch (Exception e) {
Log.e(TAG, "Erro ao ler os carros: " + e.getMessage(), e);
return null;
}

private static String readFile(Context context, String tipo) throws IOException {


// Mesma coisa aqui, apenas l os arquivos da pasta /res/raw
}

PfV3t Sffi L$t<Cff> DarserJSON(Context context, String json) throws IOException {


List carros = new ArrayList();
try {
JSON0bject root = new JSON0bject(json);
Captulo 16 u Parser de XML, JSON e testes unitrios 459
JSON0bject obj = root.getJSON0bject("carros");
JSONArray jsonCarros = obj.getJSONArray("carro");
// Insere cada carro na lista
for (int i = 0; i < jsonCarros.length(); i++) {
JSON0bject jsonCarro = jsonCarros.getJSON0bject(i);
Carro c = new Carro();
// L as informaes de cada carro
c.nome = jsonCarro.optString("nome");
c.desc = jsonCarro.optString("desc");
c.urlFoto = jsonCarro.optString("url_foto");
c.urlInfo = jsonCarro.optString("url_info");
c.urlVideo = jsonCarro.optString("url_video");
c.latitude = jsonCarro.optString("latitude");
c.longitude = jsonCarro.optString("longitude");
if (LOG_ON) {
Log.d(TAG, "Carro " + c.nome + " > " + c.urlFoto);
}

carros.add(c);
}

if (LOG_ON) {
Log.d(TAG, carros.size() + " encontrados.");
}

} catch (JSONEception e) {
throw new IOEception(e.getMessage(), e);

`etU`I`l CI"I'0S
}

Pronto, isso tudo! Acredito que se voc der uma leve estudada no cdigo ver
que fazer o parser de JSON bem simples, e a classe org.json.JSONObject facilita
bastante esse trabalho. Depois dessa alterao, o aplicativo dos carros deve conti
nuar funcionando normalmente, pois mudamos apenas a classe que faz a busca
dos carros e parser das informaes. Se voc do tipo que precisa ter certeza para
acreditar, altere o nome de um carro no arquivo JSON que est no pro]eto, e esse
nome dever aparecer na lista.

Nota: se voc teve problemas para compilar o projeto, pode ser por causa de
alguma sujeira que cou nos arquivos compilados em razo de o arquivo XML ter
o mesmo nome dos arquivos JSON. Se isso aconteceu com voc, utilize o menu
Build > Clean do Android Studio para limpar os arquivos compilados a rn de V1t8f
qualqugr 0nO_ Depois faa Build > Rebuild Project e execute o projeto novamente.
450 Google Android - 40 .mo
16.4 Testes unitrios no Android

Uma das mctudulugias tl(`LI(`S(`I\\'U|\'lIIWIIIU l1l\iSIt|()l\dlS em diversas v|ripr~,`


u `l`l)I) (li-sr lrivcu l)~vclup|ii~iir). que uaduziudu u dcscnvulviuurm r~,,
iadu ii l'.'NlS. O TDI) mrisistv cm uma liua pratica di: prugramau. quo lurgzr U
dcsrfrivulvclur a pcusar nu quc vai iazcr aulcs dc sair prugramaudu. liufim. ua
nau c uni Iivrusulri~`I`l)l)uu ll\t.`l()L|(d(\).{klS.|3()I`1I1l()()(]llL'VUIId(`Il'lUI\Sll'Il`zrgm-`
c cumu cxvvular um rcslc uuirariu nu Android Studiix

Nvsrc capiiulu. implcmcuuuuus u mrudu CarroServtce.9etCarros(contet .tipo)_ qm.


iwciw u lipu du carru dvscjadu v az u parscr du XML uu JSUN. Na verdade,
ia cara tudu ibiru c i`uucinaudu. curau. apenas para cuusrar: scguiudu as liam
praticas du 'l`l)l). u quo lizvmus Iui crradu. O `I`l)I) prega quc primciru dc tudu
lcvv--sc vscrcvcr u icstv uuitariu. Sunicruc depuis quv u rcsrc uvcr sidu iciru, ri
d~scuvulvvr Iara a iiiiplcriicruiu du cudigu. cum u ulwiclivu dv passar nu lwtc,
uu scja. ulucr a famosa our vcrdc.
(uiuudu. cumu cu dissc, cstv nau 6 um livru suhrc incrudrilugias uu u~su~. cn'
lziu vamus lugu para a pratica. Nu Audruid Studiu. cric uma vlassc chamada
CarroServlceTest c wliiqirc-i dcutru du lucal (androtdTest). cri|urnu~ a figura IM.

-=\mmi=i *ir el? ,


;'|
ii.'iur!*
va
if ~ ir i.i'= r>i.~,\i z aii;
1 fz .tz

?.r~1-'i

zii famiii
fmz;-zzir-i?'.

'~ riu z~\|~i$;z .ihyn


* H! lV~\*-Y!\h\'|.,, \ ,uu
\;dzp.r=!rr~~iir.!
r , oSiqvoTm' p i _ _
P fire:
c.z.r smp..

'..QH'r l_S - (lassc dr' lvstrs.


Captulo 16 I Parser de XML, JSON e testes unitrios 461
CarroServiceTest.java
package br.com.livroandroid.carros;
import android.test.AndroidTestCase;
import java.io.IOEception;
import java.util.List;
import br.com.livroandroid.carros.domain.Carro;
import br.com.livroandroid.carros.domain.CarroService;
public class CarroServiceTest extends AndroidTestCase {
public void testGetCarros() throws IOEception {
List carros = CarroService.getCarros(getContet(), "esportivos");
assertNotNull(carros);
// Precisa retornar dez carros esportivos.
assertTrue(carros.size() == 10);
// Valida as informaes do 1 carro
Carro c0 = carros.get(0);
assertEquals("Ferrari FF",c0.nome);
assertEquals("44.532218",c0.latitude);
assertEquals("10.864019",c0.longitude);
// Valida as informaes do ltimo carro
Carro c9 = carros.get(9);
assertEquals("MERCEDES-BENZ C63 AMG",c9.nome);
assertEquals("-23.564224",c9.latitude);
assertEquals("-46.653156",c9.longitude);
}

A classe de teste unitrio deve ser lha de AndroidTestCase, que por sua vez lha
da classe TestCase do popular framework jUnit (http:/unitorg/). Essa classe per
mite executar um teste unitrio no Android facilmente, pois a nica coisa de que
precisamos o objeto android.app.Contet, o qual pode ser obtido pelo mtodo
getContet(). Com o teste em mos, clique com o boto direito no arquivo ou no
centro do editor e selecione a opo Run > (arroServiceTest. Se estiver com o cursor dentro
de um mtodo do cdigo, o Android Studio vai mostrar a opo para executar o
mtodo, como por exemplo: Run > testGetCarros().
Enm, se voc j conhece como funciona o frameworl<]Unit, nada ser novidade
para voc. Caso contrrio, recomendo procurar uma literatura adicional. Lembre
-se de que zemos o teste no nal do captulo, pois meu objetivo foi apenas de
monstrar como criar um teste unitrio no Android Studio (Figura 16.6). Lembre-se
que ease O TDD estivesse Sendo usado na prtica, esse teste seria a primeira coisa
a ser feita, mas isso assunto para outro tipo de livro.
Z Google Android-4=z
P fz-'
ll ` ie 2 H ( sit. Done
iixszxxtsq .we smz ~;;; ..;.;..;;.:;-;:;.;....;.;;s;
L.:.cu.tiv:i:azurun.\.Luxzu.s..cao
'* `l6**' rrvicr Santi. :ooowznz pm :naun -z '/eae.. ie 1
""" 0! tet()ei(.ma guctu
,5 v'btonmrvanduwdtanustHu5^flf , PNQ, d.oClup,b,_CQ_1v3n@_;
as *I tcstndmdiesti .wseetupllrcpeth ff

U Test rurxmnq staxtedfinish


)(
f
T*
WM
Running tests

Figura 16.6 - Resultado dos testes. \

16.5 Mais informaes


Este foi um breve captulo sobre como fazer parser de arquivos nos formatos
XMLe JsoN.
O que mostrei aqui foi uma maneira bsica de como isso pode ser feito,
mas provavelmente voc vai encontrar por a outras maneiras de fazer a
mesma coisa. Por exemplo, existem bibliotecas bem populares, como o Gson
(https://code. google. com/p/google-gson/), que permite converter um objeto para _]SON
e vice-versa, portanto seria possvel fazer o parser em uma nica linha.
Eu particularmente fao meus projetos exatamente como mostrei neste captulo
e tenho plena convico de que consultar os web services e parser de XML/JSON
so a parte mais simples do desenvolvimento. O complicado e s vezes demorado
criar interfaces grcas bonitas que agradem o usurio.
Mas cada caso diferente, e voc dever adaptar o que voc est aprendendo ao
seu dia a dia e aos requisitos de cada projeto.
CAPTULO 17

Web services
\4

No captulo anterior, aprendemos a fazer parser de arquivos XML e JSON.


Neste captulo, vamos continuar o desenvolvimento do projeto e buscar a lista
de carros de um web service.

17.1 Introduo
Uma dvida comum de quem est iniciando com mobile ou desenvolvimento de
sistemas : como fao para me conectar ao banco de dados do servidor e buscar
ou atualizar informaes nesse banco de dados?
A resposta : o aplicativo no deve conectar-se ao banco de dados do servidor.
A principal razo para isso por questes de segurana dos dados e outra por
padronizao de acesso, uma vez que web services facilitam a interoperabilidade
entre diversas plataformas, pois no importa a linguagem de programao que
o servidor ou cliente utilizam.
Para o mobile buscar informaes do servidor, necessrio utilizar um web service;
portanto, no servidor deve ser criado este Web service em qualquer plataforma ou
linguagem Uava, .NEI PI-IP, Ruby Python). A responsabilidade do Web service
acessar o banco de dados, processar as informaes e retornar os dados no formato
XML ou JSON para o mobile/cliente ler os dados.
Existem muitas maneiras de criar web services, mas isso est fora do escopo deste livro,
pois estamos falando de mobile e no de servidor. Apenas para voc complementar
seus estudos, vou citar alguns temas para voc pesquisar, caso ache necessrio.
1. WSDL (Web Services Description Language) - um formato de servio escrito em
XML, cujo trfego de dados feito via HTTP utilizando o protocolo SOAP
(Simple Object Access Protocol). Esta a maneira clssica de criar web
services. Atualmente, esse modelo est sendo evitado no mundo mobile,
463
464 Google Android - 4 edio
pois o protocolo SOAP um grande XML que trafega pela rede, e pode
causar lentido, principalmente na conexo 3G.
2. REST - Formato leve de web services que so criados geralmente sobre O
protocolo HTTP, utilizando os mtodos GET, DELETE, POST e PUT como padro_
nizao de acesso. Por exemplo, se voc zer um GET na pgina /usuario;
pode ser retornada uma lista de usurios. Se voc fizer um GET na pgina
/usuario/1, o usurio com o id=1 retornado. Mas se voc zer um DELETE na
pgina /usuario/1 , esse usurio ser deletado. Outro exemplo se zermos um
POST na pgina /usuario, que neste caso serve para inserir um novo usurio,
Ento o padro REST utiliza os prprios mtodos do`protocolo HTTP para
criar a lgica necessria para o web service. No REST 0 formato de retorno
mais comum o JSON.
3. Pginas simples com GET ou POST - Outra forma de criar um web service em
qualquer linguagem fazer uma pgina web qualquer que, ao receber uma
requisio por GET ou POST vai retornar os dados no formato XML ou
JSON, em vez de retornar uma pgina HTML.
Essa rpida explicao visa informar os termos necessrios para voc fazer uma
pesquisa no Google e complementar seus estudos, caso ache necessrio.
Neste livro, vamos utilizar arquivos XML e JSON estticos que coloquei no site,
apenas para simular um web service. Conforme j vimos, existem trs arquivos
para cada tipo de carro e podemos optar por ler os dados em XML ou JSON.
// XML e JSON dos carros clssicos
http://www.1ivroandroid.con.br/livro/carros/carros_c1assicos.xml
http://www.1ivroandroid.com.br/livro/carros/carros_c1assicos.json

// XML e JSON dos carros esportivos


http://www.1ivroandroid.com.br/livro/carros/carros_esportivos.xml
http://www.livroandroid.com.br/livro/carros/carros_esportivos.json
// XML e JSON dos carros de luxo
http://www.1ivroandroid.com.br/livro/carros/carros_1uo.xml
http://www.livroandroid_com.br/livro/carros/carros_1uo.json
Para fazer uma requisio HTTP no servidor e buscar os dados no formato Stri9,
podemos utilizar a famosa classe java.net.HttpURLConnection do Java, mas mesmo com
ela so necessrias algumas linhas de cdigo. Para facilitar o cdigo do projeto
criei a classe Tivroandroid.1ib.utiis.HttpHe1per na biblioteca android-utils, que faz a
requisio HTTP em uma nica linha dc cdigo.
String url = "http://www.livroandroid.com.br/livro/carros/carros_c1assicos.json"
String json = HttpHe1per.doGet(ur1);
. . _ rn
Captulo 17 n Web services 465
17.2 Requisio HTTP para consultar o web gafvite
O.
Para continuar o desenvolvimento d Pf0Jto dos carros vou escolher o for ato
de arquivo JSON, pois para o mobile essa estrutura mais compacta que o XML
e consequentemente diminui a quantidade de dados trafegados
Para acessar a URL do web service, adicione esta Cgnstame na classe CarroService:
private static nal String
URL = "httpz//www.livroandroid.com.br/livro/carros/carros {tipo}.json"~

Conforme o.t1po (classicos, esportivos, luxo) solicitado, vamos substituir O textg


{tipo} pelo tipo do carro, para criar a URL correta Web service. No cdigo da
classe CarroService, vamos substituir o mtodo readFile(contet,tipo) que estvamos
usando pelo cdigo que faz a requisio HTTP no web service.
Veja que removi o try/catch que estava no mtodo getCarros(contet,string) e o z
lanar a exceo IOEception. Isso o correto, pois quando a activity chamar este
mtodo para listar os carros, ela deve tratar qualquer erro.

CarroService.java

import livroandroid.lib.utils.HttpHelper;
public class CarroService {
private static nal String
URL = "httpz//www.livroandroid.com.br/livro/carros/carros_{tipo}.json";
public static List getCarros(Context context, String tipo) throws IOException {
String url = URL.replace("{tipo}", tipo);
// Faz a requisio HTTP no servidor e retorna a string com o contedo.
String json = HttpHelper.doGet(url);
List carros = parserJSON(contet, json);
return carros;
}

private static List parserJSON(Context context, String Json) {


// Mesmo cdigo aqui...
l
}

pode ser esportivos, _


O mtodo CarroService.getCarros(con ex desejado,
t t,string) recebe o tipo do carro . I que
luxo ou clssicos. Baseado no tipo do carro, a URL e montada
para buscar Os Carms HQ Servidor. Uma vez que temos a URL do web service, basta
uma linha dg Cdigg para fazer a requisio HTTP por GET e obter o resultado.
// Faz uma requisio do tipo GET e retorna a resposta em String
String json = HttpHelper.doGet(url);
466 Google Android - 4 Qdlo
A classe HttpHelper contm mtodos para fazer a requisio HTTP por GET ou POST, Q
voc pode utiliz-la como base para seus estudos e projetos. internamente essa classe
apenas encapsula a famosa classe java.net.HttpURLConnecton do java. Existem outras
bibliotecas para fazer requisies HTTP no Android, como 0l(Http e Volley do prprio
Google. Recomendo que voc as estude como complemento aos seus estudos.

Importante: no mtodo getCarros(contet,string), removi o trecho de cdigo que


tinha o try/catch para propagar a exceo para quem chamar esse mtodo. Isso
o correto e far o aplicativo travar na prxima execuo. Logo veremos por qu.

O restante do cdigo da classe CarroServce no precisamos mudar. Mas impor


tante que voc tenha percebido que no mais usamos os arquivos da pasta /res/raw,
pois estamos lendo o JSON diretamente do arquivo que est no site do livro.
Lembrando tambm que no meu site esses arquivos so estticos e apenas si
mulam o retorno de um web service, mas, para os objetivos didticos deste livro,
isso su ciente.

Depois de alterar a classe CarroServce para fazer a requisio HTTP no web service,
execute o projeto novamente. O resultado que a aplicao vai travar, mostran
do o famoso Force (lose para o usurio. Se olharmos nos logs, veremos a seguinte
mensagem de erro (stack trace):
br.com.1ivroandroid.carros E/AndrodRuntme? FATAL EXCEPTION: main
Process: br.com.1vroandroid.carros, PID: 988
android.os.Network0nManThreadException
at android.os.StrctMode.onNetwork(StrctMode.java:1147)

Conforme j estudamos anteriormente no captulo 10, sobre threads e Handler, esse erro
acontece porque no podemos acessar a internet na UI Thread, portanto precisamos
criar uma thread ou AsyncTask para desvincular o processamento da th read principal, c.
se possvel, ainda mostrar uma animao no estilo por favor, aguarde para o usurio,
pois a consulta pode demorar, no caso de um dispositivo utilizando 3G.

17.3 Utilizando a classe AsyncTask


NO CQPIUO 10, sobre Hdlef, Cxpliquei o bsico sobre threads, e comentei o uso
da classe AsyncTask. Agora, vamos praticar mais uma vz_
A seguir, podemos ver igo
o cd'
onte -f
a cdasse
l CarrosFragment que utiliza correta
mente a classe AsyncTask:
Captulo 17 I Web services 467
CarrosFragment.java

public class CarrosFragnent extends BaseFragnent {

private void taskCarros() {


// Busca os carros: Dispara a Task
new GetCarrosTask().execute();
}

// Task para buscar os carros


private class GetCarrosTask extends AsyncTaskVoid,Void,List<Carro> {
@0verride
protected ListCarro> doInBackground(Void... params) {
try {
// Busca os carros em background (Thread)
return CarroService.getCarros(getContext(), tipo);
} catch (IOEception e) {
Log.e("livroandroid", e.getMessage(), e);
return null;
}

}
// Atualiza a interface
protected void onPostExecute(List<Carro carros) {
if(carros != null) {
CarrosFragment.this.carros = carros;
// Atualiza a view na UI Thread
recyclerview.setAdapter(new CarroAdapter(getContext(), carros, onClickCarro()));
}

Nesse cdigo criamos uma AsyncTask para consultar o web service em segundo
plano (background) e atualizar a view na UI Thread. Esta linha dispara a thread:
new GetCarrosTask().eecute();

A AsyncTask gerencia um pool de threads internamente e vai executar o mtodo


doInBackground() em segundo plano, que neste caso est retornando a lista de carros.
Internamente a AsyncTask contm um Handler que utilizado para chamar o mtodo
onPostEecute(List) na UI Thread. A vantagem de utilizar a AsyncTask justa
mente essa facilidade, pois no precisamos nos preocupar com o gerenciamento
das threads e a utilizao de Handlers, conforme j estudamos.
468 Google Android - 4 edio
Dica: o mtodo doInBackground() da AsyncTaslg deve executar a lgica pesada que
pode demorar, como consultar um web service ou banco de dados. O mtodo
onPostEecute() chamado na UI Thread a m de atualizar a interface.

Como o aplicativo vai acessar a internet, necessrio declarar a permisso ng


arquivo AndroidManiest.xml. Mas isso s para sua reviso, pois no captulo 14,
sobre webvew, j zemos essa configurao.

AndroidManifest.xmI
<manfest . . . />
uses-permission android:nane="androd.permisson.INTERNET" /
<app1icaton ... />
</manfest>

Ao executar o projeto novamente, a consulta no web service ser feita e voc dever
ver a lista de carros conforme a gura 111. Isso muito legal, pois desta vez a lista
de carros est online, e essa conectividade 0 que d vida palavra mobilidade`

A_ -__)~] ___ `

1-1.
l iH;,lz
Figura 121 - Lista de carros do web service.

NI'1...
17.4 Biblioteca simples para encapsular a AsyncTask
o topico anterior, buscamos os carros do web service utilizando a famosa classe
AsyncTask, mas eu particularmente costumo utilizar uma pequena biblioteca que
encapsula o acesso AsyncTask.
Captulo 17 I Web services 469
Ao utilizar a AsyncTask, temos de controlar vrias coisas manualmente, como por
exemplo: mostrar um ProgressBar ou outra animao durante a execuo da tare
fa, tratar as possveis excees que so lanadas, ter uma maneira de criar vrias
tarefas e cancel-las, alm de tratar vrios probleminhas comuns com o Android
quando o dispositivo rotacionado entre vertical e horizontal.
Na verdade, eu no quero entrar nesses assuntos avanados da AsyncTask neste
momento do livro, pois acho cedo para termos essa conversa. O objetivo do livro
ir aumentando o grau de diculdade durante a leitura, ao mesmo tempo em
que vamos conectando diversos conceitos, e justamente por isso estamos fazendo
esse projeto dos carros passo a passo.
Portanto, por ora, vamos utilizar esta pequena biblioteca que criei para disparar
tarefas em segundo plano, e depois recomendo que voc leia o captulo 31, sobre
AsyncTask, disponvel no nal do livro. Com o tempo, voc pode estudar o cdigo
-fonte da biblioteca que vou mostrar e voc vai perceber que apenas encapsulei
coisas que voc um dia tambm ter de fazer.
Bem, chega de teoria, ento vamos l!
A classe livroandroid.lib.fragment.BaseFragment da biblioteca android-utils contm o
mtodo startTask(codigo,taskListener), que basicamente vai disparar uma AsyncTask e
controlar a sua execuo. Como parmetro do mtodo startTask(codigo,taskListener),
voc deve informar uma implementao da interface TaskListener.
public interface TaskListener {
// Executa em background numa Thread e retorna o objeto
T execute() throws Exception;
// Atualiza a view na UI Thread
void updateView(T response);
// Chamado caso o mtodo execute() lance uma exceo
void onError(Exception exception);
// Chamado caso a task tenha sido cancelada
void onCancelled(String cod);
}

- ' e o T vire um
Nota- 3 notao um tipo genrico e permite que esta interface seja criada
com qualquer tipo, por exemplo, com TaskListener, para qu
Objeto Carro em tempo de execuo. Se achar necessrio, recomendo procurar uma
hteratura adicional ggbre Generics, pois o assunto bem amplo e interessante.
470 Google Android - 4 edio
Acredito que os mtodos da interface Tasktistener sejam autoexplicativos e at
se parecem com os mtodos doInBackground() e onPostExecute() da classe AsyncTask.
Portanto, vamos atualizar o cdigo da classe CarrosFragnent para utilizar o mtodo
startTask(codigo,taskListener), e durante o desenvolvimento do aplicativo vou re
forando algumas das vantagens dessa biblioteca. O cdigo anterior que usava 3
AsyncTask diretamente voc pode remover.

LT CarrosFragment.java

public class CarrosFragment extends BaseFragment {

private void taskCarros() {


// Busca os carros: Dispara a Task
startTask("carros", new GetCarrosTask());
}

// Task para buscar os carros


private class GetCarrosTask inplenents TaskListenerList<Carro> {
@0verride
public List execute() throws Exception {
// Busca os carros em background (Thread)
return CarroService.getCarros(getContext(), tipo);
}

@0verride
public void updateView(List<Carro carros) {
if (carros != null) {
// Salva a lista de carros no atributo da classe
CarrosFragnent.this.carros = carros;
// Atualiza a view na UI Thread
recyclerview.setAdapter(new CarroAdapter(getContext(), carros, onClickCarro()));
}

@0verride
public void onError(Exception e) {
// Qualquer exceo lanada no mtodo execute vai cair aqui.
alert("0correu algum erro ao buscar os dados.");
}

@0verride

public void onCancelled(String s) {


}

}
Captulo 17 1 Web services 471
Ao executar o projeto novamente a lista de carros ser mostrada da mesma for
ma que antes. Mas veja que durante o download foi exibido um ProgressDa1og
conforme a figura 112.

Bugatti Veyron

3
Frrari Enzo
Lamborghinr Reventon

; , Y
Lebianc Mrabau

Figura 122 - Pr0gressDialog e lista de carros.


Esse o comportamento padro ao disparar as tarefas com o mtodo
sta rtTask(codgo,taskLstener) da biblioteca and rod-utls, pois um ProgressDa1og utili
zado para informar ao usurio que a aplicao est processando alguma informao.
Mas para melhorar a experincia do usurio, vamos adicionar um ProgressBar no
layout do fragment, exatamente por cima da lista de carros.

/res/Iayout/fragment_carros.xmI
<?xm1 version="1.G" encodng="utf-8"?>
<FrameLayout . . .>
<androd.support.v7.wdget.Recyc1erView . . . />
<ProgressBar android:d="@+d/progress"
and rod : 1ayout_width="wrap_content" android :1ayout_heght="wrap_content"
androd:1ayout_gravty="center" androd:vsb1ty="nvsib1e"/>

A nica coisa que precisamos fazer no cdigo passar no terceiro parmetro do m


todo startTask(codgo,taskLstener,progress) o identicador R.d.progress do ProgressBar.
,. .
472 Google Android - 4' edio
iwi (arrosFragment.java

public class CarrosFragment extends BaseFragment {


private void taskCarros() {
startTask("carros", new GetCarrosTask(), R.id.progress);
}

O resultado dessa alterao pode ser visto na gura 173, que mostra a animao
.lo ProgressBar durante a execucao da ta refa. Lembrando que esse tipo de animao
muito importante para melhorar a experincia do usurio, principalmente no
cascidezuwhcatuuiseracessadcun uniaxniex3(]intutolerna.

ta , V v
.za

Figura IZ3 - ProgressBar c lista de carros.

Uma dica, ou melhor, um artifcio tecnico, caso queira ver as animaes, e dar um
pequeno sIeep" dentro do metodo eecute(). Algo como 300 u 500 milissegundos
ja e su hciente para voc ver a aniinaco.
public List execute() throws Exception {
Thread.sleep(500);
return CarroService.getCarros(getContet(), tipo);
}

(.omo vimos, utilizar uma pequena classe que encapsula a AsyncTask tein algumas
Vfllllilll, pois ao utilizar a classe AsyncTask voc tera de mostrar e esconder u
ProgressBar inanualinente.
Captulo 17 z Web services 473
Dica: a animao do ProgressBar suave, recomendo utiliz-la para informar ao
usurio que a aplicao est processando algo. j o ProgressDialog deve ser utilizado
caso o usurio precise obrigatoriamente aguardar o processamento, pois a janela
do alerta no estilo modal. Lembrando que, no caso dos tablets, como podemos
ter um layout com vrias views espalhadas pela tela, o recomendado que cada
fragment utilize um ProgressBar, pois as animaes de cada um sero independentes.
Neste caso, teremos vrias bolinhas girando espalhadas pela tela do tablet.

17.5 Atualizao por Pull to Refresh


Como a lista de carros est online, vamos adicionar o SwipeRefreshLayout no arquivo de
layout do fragment para atualizar os dados. O SwipeRefreshLayout um gerenciador de
layout simples que pode conter apenas um lho, portanto faa-o envolver o Recyclerview.

/res/layout/fragment_carros.xml
<?xnl version="1.0" encoding="utf-8"?>
<FrameLayout . . .>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeToRefresh"
android:layout_width="natch_parent" android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView . . . />

<ProgressBar . . . />

Na classe CarrosFragment, congure o SwipeRefreshLayout e adicione o mtodo


0nRefreshListener() para atualizar a lista.

CarrosFragment.java
public class CarrosFragment extends BaseFragment {
private SwipeRefreshLayout swipeLayout;

public View onCreateView(LayoutInater inater, ViewGroup container,


Bundle savedInstanceState){

// Swipe to Refresh
swipeLayout = (SwipeRefreshLayout) view.ndViewById(R.id.swipeToRefresh);
swipeLayout . set0nRefreshListener(0nRefreshListener( ) );
swipeLayout . setColorScheneResources(
474 Google Android - 4 edio
R.color.refresh_progress_1,
R.color.refresh_progress_2,
R.color.refresh_progress_3);
return view;

rivate SwipeRefreshLayout.0nRefreshListener 0nRefreshListener() {


return new SwipeRefreshLayout.0nRefreshListener() {
@0verride
public void onRefresh() {
// Atualiza ao fazer o gesto Pull to Refresh
taskCarros();
}

};
}

Como j utilizamos o SwipeRefreshLayout anteriormente, acredito que esse cdigo


seja tranquilo. Ao tocar na lista e arrastar o dedo para baixo, feita a atualizao
dos dados, conforme a figura 114. H, porm, um problema, pois temos duas
animaes na tela. Na parte superior, existe a bolinha que fica girando, referente
ao SwipeRefreshLayout. E na parte central da tela temos o ProgressBar. Esse problema
acontece porque a animao do SwipeRefreshLayout feita automaticamente logo ao
iniciar o gesto, mas, quando o mtodo startTask(codigo,taskListener,progress) cha
mado, o ProgressBar que est no centro do layout ativado e tambm faz a animao.

Figura IZ4 - Pull to Refresh.


Calltulo 17 n Web services 475
Para solucionar o problema, temos de mostrar uma animao, ou outra. Na
verdade, o recomendado que, na primeira vez que abrir o aplicativo e a lista
estiver vazia, o ProgressBar seja utilizado, pois ele car bem sobre o fundo vazio,
conforme a figura 125. E quando atualizarmos a lista podemos mostrar apenas
a animao do SwipeRefreshLayout, conforme ilustrado no lado direito da gura.

' Tucker 1948

Chevrolai
L/ Corvetve

Chevrolet lmpala Coupe '

j, 'M_ _ _ , < Cadillac Deville


--=;~@ '' Convertible
.-l;

Figura 125 -Atualizao da lista de carros.

Agora que j sabemos o que precisamos fazer, vamos colocar a mo na massa.


Apenas para revisar os conceitos, lembre-se de que o componente SwipeRefreshLayout
inicia automaticamente a sua animao, e para parar a animao voc deve chamar
o mtodo setRefreshng(false). Mas a biblioteca androd-utls j vai fazer isso por
voc, basta ao chamar o mtodo startTask(codgo,taskListener,progress) informar o
identificador do ProgressBar ou do SwipeRefreshLayout conforme desejado.

Para controlar quando utilizar uma animao ou outra, vamos criar um parme
tro do tipo booleano para o mtodo taskCarros(boolean), a m de que seja possvel
escolher entre animar o ProgressBar ou o SwipeRefreshLayout.

CarrosFragment.java
public class CarrosFragnent extends BaseFragment {

private SwipeRefreshLayout.0nRefreshListener OnRefreshListener() {


return new SwipeRefreshLayout.0nRefreshListener() {
@0verrde
public void onRefresh() {
476 Google Android - 4 edio

taskCarros(true);
i
};
}

@0verride
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
taskCarros(false);
}

private void taskCarros(boolean pullToRefresh) {


startTask("carros", new GetCarrosTask(), pullToRefresh
? R.id.swipeToRefresh_: R.id.progress);
}

Pronto! Simples e prtico. Devido velocidade do seu smartphone ou internet.


no caso de uma conexo Wi-Fi, talvez voc no tenha tempo de ver as animaes.
Se quiser, adicione um pequeno sleep durante a execuo da thread para atrasar
um pouco a execuo. Lembrando que isso no deve ser utilizado em produo,
mas uma maneira simples de testar essa funcionalidade.
public List execute() throws Exception {
Thread.sleep(500);
return CarroService.getCarros(getContext(), tipo);
}

17.6 Vericando se existe conexo disponvel


Por enquanto, estamos fazendo o caminho feliz, mas na prtica muitas vezes a
conexo com a internet no est disponvel. Ento, antes de buscar os carros,
precisamos verificar isso. Para testar a disponibilidade da conexo, podemos
utilizar o mtodo AndroidUtils.isNetworkAvailable(context) da biblioteca android-utils.

si AndroidUtiIs.java
public class AndroidUtils {

public static boolean isNetworkAvailable(Context context) {


try i
ConnectivityManager connectivity = (ConnectivityManager)
context.getSystenService(Context.CONNECTIVITY_SERVICE) I 0

if (connectivity == null) {
return false;
Captulo 17 u Web services 477
} else {
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == Networklnfo.State.CONNECTED) {
return true;
}

} catch (SecurityException e) {
alertDialog(context,e.getClass().getSimpleName(), e.getMessage());
}

return false;
}

Para ler as informaes da rede de dados, adicione a permisso ACCESS_NETw0RK_STATE


ao AndroidManifest.xml.

AndroidManifest.xmI
<nanifest . . . />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETw0RK_STATE" /
<application ... />

No prximo captulo, vamos salvar todos os carros no banco de dados, e o aplica


tivo at poder funcionar de forma offline. O que no pode acontecer o usurio
forar a atualizao dos dados sem existir uma conexo ativa. Vamos fazer essa
vericao no mtodo onRefresh().

CarrosFragment.java
public class CarrosFragment extends BaseFragment {

private SwipeRefreshLayout.0nRefreshListener 0nRefreshListener() {


return new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// Valida se existe conexo ao fazer o gesto Pull to Refresh
if(AndroidUtils.isNetworkAvailable(getContext())) {
taskCarros(true);
473 Google Android - 4* edio
} else {
swipeLayout.setRefreshing(false);
a1ert(R.string.error_conexao_indisponve1);
}

};
}

Para o cdigo compilar, adicione a seguinte mensagem ao arquivo stringsxml:

/res/values/strings.xmI
<'eSOU 'CeS>

<strng name:"error_coneao_ndsponve1">Coneo indisponvel. Verque seu Hi-Fi ou 3G.</struxp

Depois desse tratamento, ao tentar atualizar a lista e caso a conexo no esteja


disponvel, o resultado ser como na gura 17.6.

\r$

.tw .A_yw
~._./

e seu wi F!

Figura IZ - Conexo indisponvel.

Observe que a lgica aplicada aqui verifica se existe conexo apenas no momento
de fazer O gesto de Pull to Refresh, porm no veriquei se existe conexao antes de
Chamar 21 mta da Pflmlf Vez quando a lista est vazia. Na verdade, nao z issu
POYQU O PYXW C3PlUl0 vamos salvar os carros no banco de dados, assim O
Captulo 17 n Web services 479
aplicativo poder trabalhar de forma offline. Somente para a atualizao da lista
que vamos precisar fazer alguma validao.
Outro detalhe importante que no captulo anterior comentei algo sobre o trata
mento das excees, e na poca z um try/catch dentro da classe CarroService, o que
matou a exceo, e isso no recomendado. Mas, se voc olhar o cdigo atual da
classe CarroService, ele no tem mais esse problema, e se alguma exceo do tipo
IOEception ou RuntimeException for lanada, ela ser propagada, ou seja, ser lanada
para cima. Isso signica que, caso o mtodo execute() lance uma exceo, a biblio
teca android-utils vai tratar o erro internamente fazendo um try/catch e vai chamar
o mtodo onError(eception) para voc fazer o tratamento adequado da exceo.
Enfim, j vimos vrias vantagens de utilizar esta pequena biblioteca para disparar
tarefas assncronas.
private class GetCarrosTask implements TaskListener<List> {
public List execute() throws Exception {
// Se este mtodo lanar uma exception...
}
public void updateView(List carros) { }
public void onError(Eception e) {
// Vai cair aqui para voc tratar o erro.
alert("0correu algum erro ao buscar os dado: " + e.getMessage());
}

public void onCancelled(String s) { }


}

17.7 Requisies HTTP com Get e Post


Conforme explicado no incio deste captulo, existem vrios tipos de Web services,
e cada um vai exigir um tipo de comunicao. O restante deste captulo tem como
objetivo fornecer o bsico sobre requisies HTTP do tipo GET e POST e tambm
como acessar web services que so escritos com WSDL.
No aplicativo dos carros, zemos uma requisio do tipo GET no servidor, para
buscar o arquivo XML ou JSON. A requisio do tipo GET passa todos os parme
tros na URL e utilizada frequentemente para consulta. Uma URL com GET tem
a seguinte sintaxe:
http://www. site. com.br?pamm1 =1/alorl Cv'param2=val0r2

Apenas para voc traar um paralelo, sempre que voc digita uma URL no browser
para abrir o site desejado, feita uma requisio do tipo GET. A diferena que o
480 Google Android - 4 e<|
site retorna os dados no formato HTML, j o web service geralmente retorna 05
dados em XML ou JSON.
Por sua vez, uma requisio do tipo POST envia os parmetros no corpo da requ_
sio HTTP, e a URL apenas contm o endereo Final (endpoint) em que vo
deseja enviar/postar determinado contedo.
http://www.site.com.br
No corpo da requisio, so enviados os parmetros, por exemplo;
{paramt=va1or1&param2=va1or2}. A vantagem da requisio do tipo POST que, caso vo
precise enviar informaes secretas como uma senha, ela enviada no corpo da
requisio. Se fosse utilizado o GET, a senha seria enviada na URL e caria visvel
junto com os demais parmetros.
Traando um paralelo mais uma vez, quando voc faz login no Gmail ou em
qualquer formulrio de cadastro na internet, geralmente feita uma requisio
do tipo POST no site. A requisio do tipo GET tambm tem um limite em relao
quantidade de parmetros que podem ser enviados, portanto o POST tambm
recomendado para trafegar muitas informaes.
Outro exemplo em que o POST deve ser utilizado para trafegar arquivos, tais como
fazer o upload de fotos. Estou explicando isso porque o web service dos carros
foi utilizado apenas para consulta, e isso um clssico exemplo de utilizao do
GET. Mas se fosse necessrio salvar um carro no servidor, provavelmente os dados
seriam enviados por POST.
Eu no tenho um web service desse tipo disponvel, ento, para brincarmos um
pouco com requisies HTTP do tipo POST, vamos utilizar este web service dis
ponibilizado no site da W3SchooIs:

http://www. w3schools. com/webservices/tempconvert. asmx


Pela extenso .asmx, sabemos que esse web service foi desenvolvido em .NET e na
listagem dos servios (Figura 117) podemos vericar os mtodos Ce1susToFahrenheit
e FahrenhetToCe1sius que podemos consultar.

Ao clicar no servio CeTsiusToFahrenhet, a pgina vai abrir o formulrio para voc


testar o web service por POST (Figura l7.8). Podemos ver que, para chamar o Wb
SfVlC, XlSI apenas o parmetro Celsius, que recebe o valor para converter de
graus Celsius para Fahrenheit.
Ao preencher o campo Celsius com o valor 1 e clicar no boto Invoke, feito o P057
no web service. Podemos conferir o resultado na gura 17.9, que mostra o VHU1'
convertido para Fahrenheit.
(aPtuIo 17 I Web services
..._
*G--)C' El _
. _ _ ...e_ 'F

l'_ _
/,_ ._~.m\.,.. ._ zxz -.~.. .~....; z.....z._.......z=.zz..:,

rf' S x .___ _,,-_ ._ ___., _. ,_ .___ __., _. . _ _

L
;___ _ . *
1 _ g _ wyy~i.w3scl~iools.oon1/websewices/te~mpconvereasmx sf;

i ` The following operations are supported. Por a Formal deniton, please review the Service Deserigtion.
* .i!'..'I.'9.{w.e_.z.,l.!_,._ HMP" f

z ~ tTog. lsigis

l' ' .
l

.~ z __ E X
i fl TempConvert Web S _
Figura 127 - Web service no site da W3Schools.

ii __ V ~ V- V: . `_ f.r'.f' .;.v_ _ A_.,__, _ _ _ , ____VW_ A H __' _


l b (" ^` C' \flf\/iui.\i\{3seljool_s_eorn/viiebsenficesztempcom/ert.asm?op= CelsiusToFahrenheit ?

*
l

|_

l TempConvert
H Click here for a complete list of operations.
,_

l
_

l l CeIsiusToFahrenheit
il i

Test

1l *Inloke
l"

Celsius: il5`
Pararnetarvalue i ` 7 V ` _ _ ' ' z `~ 1
E l; T0 test the .:eret. usina the .H1TP_ PQST Prewelz lik fh.._'If1vlT buttm

1 SP 1.1
l

zi
l ll The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.
l

, lHuse: ru-.1v3:c1xoo1s.:nm ~ , *
i

A POSI rebservices/t:|q:cnnve:t..a:an: HTIP 1-1

5i flzction:
Ccn1ten1:'kng-vh:
Cunteut-Type: text/xml; :hzz:e1=1:1:f-B
length _

fz _ 1
; `^ :cizp:Hmi> _
'keep : ,fwnru . rschonls . :onJ'\eb:~ez'v5.:esCelsu:'!`nf=.h:eabe;u'
?n|.\1 verei:-n="1.D' :m:ndu.q=utf"2>- . \
z

<:ap:Ez1-eelope :::ln.::x:='1:.::p:/'\n..|r3-ozgfl/10E3c1:ena'i:::*1::.u:e" :=ni.n::::d="http:/anr\.\f.:gf.'2G1'3I.S:h.a=' 1115:

7 -~ ~ ~ ' ~-^~ * ' -~ W.__ ,_


Ce1:iu:'2o?hzex-zluec ::m1"1z::p://*WI-'3;:hou1z.:un/'>:exvice='> _ Y
.

Figura 128 - Exemplo de web service.

-z TgnpQzj1&f1I`i~gb xe @www.w3schools.com_ x _ AA __
ll a C' [] www.w3schools.com/Websewices/tempconvertasmx/CelSfffg
This file dggg not appear to have any style fflti associated With it. The
l

l document tree is shown below.


l

<5tng xm15="h1;-izpz//iwiw. wschools . comfwebservices/">33 . 8<s'cr'ng>

_ ...___ : z --~
Figura 129 - Resultado do web service.
482 Google Android - 4= edo
O retorno do web service um pequeno XMLque contm a tag <strngva1or</5mng>_
Esse retorno ser diferente em cada web service, mas o importante nesse caso
que sabemos que podemos fazer o POST nesta URL passando o parmetro Celsius.
http://www. w3schools. com/webservices/tempconvert. asmx/Celsius ToFahrenheit

A partir daqui, os exemplos no sero feitos no projeto dos carros, pois esse as
sunto complementar aos seus estudos. Para continuar, crie um projeto chamado
HeU.owSDL ou abra o projeto de exemplo pronto que est no GitHub.

Tenha ateno, pois como vamos acessar a internet necessrio declarar a per
misso INTERNET. Todos os prximos exemplos vo utilizar o mesmo arquivo dg
layout, que um simples formulrio que permite digitar o valor em Celsius para
converter para Fahrenheit.

/res/Iayout/activity_form_ceIsius_to_fahrenheit.xmI

<LnearLayout n1ns:androd="http://schemas.android.com/apk/res/android"
android:1ayout_width="natch_parent" android:1ayout_height="natch_parent"
android:paddng="16dp" android:orientation="vertica1">
<TetView

android:1ayout_width="match_parent" android:1ayout_height="wrap_content
android:text="Ce1cius"/>
<EdtText androd:d="@+id/tCe1cius"
android:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content" />
<TetView

androd:1ayout_width="match_parent" android:1ayout_heght="wrap_content"
android:text="Fahrenheit"/>
EditText androd:id="@+id/tFahrenheit"
android:1ayout_wdth="match_parent" androd:1ayout_height="wrap_content"
android:edtab1e="fa1se" android:inputType="number"/>
Button

android:1ayout_width="wrap_content" android:1ayout_height="wrap_content"
android:tet="Converter" android:1ayout_gravity="right"
android:onC1ck="onC1ickConverter" />

A0 Clicar U0 boto Converter, O mtodo onC1ckConverter(view) chamado na activity


que por sua vez faz o POST no web service.

CelsiusToFahrenheitPostActivity.java

public class Ce1siusToFahrenheitPostActivity extends AppCompatActivity {


String URL = "http://www.w3schools.com/webservices/tenpconvert.asmx/CelsiusToFahrenheit";
private EditTet tCe1cus;
Captulo 17 I Web services 433
private EditText tFahrenheit;
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_forn_ce1sius_to_fahrenheit);
tCe1cius = (EditTet) ndViewById(R.id.tCe1cius);
tFahrenheit = (EditTet) ndViewById(R.id.tFahrenheit);
}

public void onC1ickConverter(View view) {


new Thread() {
@0verride
public void run() {
String ceicius = tCelcius.getTet().toString();
// Parmetros para enviar por post
Map<String, String> parans = new HashMap<String, String>();
parans.put("Ce1sius", ceicius);
try {
// Retorno: <string mlns="http://www.w3schoo1s.com/webservices/">33.8
String s = HttpHe1per.doPost(URL, params, "UTF-8");
Element root = XMLUtiis.getRoot(s, "UTF-8");
// L o texto do XML
nal String fahrenheit = XMLUti1s.getText(root);
runOnUiThread(new Runnab1e() {
@Override
public void run() {
tFahrenheit.setText(fahrenheit);
}

});
} catch (IOEception e) {
Log.e("livroandroid", "Erroz " + e.getMessa9@(), G);
}

}.start();
}

Observe que estou utilizando as classes HttpHe1per e XMLUtils para fazer o POST no
web service e para ler o retorno do XML, portanto declare a dependencla da
biblioteca android-utils.
484 Google Android - 41 d
app/build.gradle
dependencies { . . .
conpile 'br.con.1ivroandroid:android-utiIs:1.6.0'
}

A gura 17.10 mostra o resultado da converso de Celsius para Fahrenheit.

p Celcuis.
1 ll
i

nftfc-^

33.8

CONVERTER

Figura 1210 - Exemplo de Post.

17.8 Web services com WSDL

No aplicativo dos carros, estamos utilizando os arquivos XML e JSON que cSI0
no site para simular um web service, pois esses dois tipos de retornos so leveS C
essa arquitetura bem simples e muito utilizada.
Mas, dependendo do servio que existe no servidor, s vezes necessrio consumir
web services clssicos, que so descritos por uma WSDL (Web Service Deniti
Language). Neste caso, uma mensagem SOAP (Simple Object Access protocol)
enviada para trafegar os dados. Enm, este no um livro de web services, ent0
se for necessrio, procure alguma literatura adicional.
O fato que s vezes precisamos acessar esses tipos de servios, e para isso Pff'
cisamos gerar um cliente de web service, a m de chamar os mtodos rem0f05
que foram definidos no servidor. No Android, podemos utilizar a biblioteca K53
para acessar esses web services. Essa biblioteca pequena e leve, e basicamlc
encapsula o trabalho chato de criar as mensagens SOAP.
Captulo 17 I Web services 485
http://leobjects.org/ksoap2/
https://code. google. com/p/ksoap2-android/

Como estamos utilizando o Android Studio e o Gradle, no precisamos nos pre


ocupar em baixar essa biblioteca; basta adicionar uma linha com a dependncia
no arquivo app/buildgradle, conforme demonstrado a seguir.

app/buiId.gradIe
dependencies {

compile 'br.com.livroandroid:android-utils:1.0.0'
// Biblioteca KSOAP2 para web services WSDL
compile 'com.google.code.ksoap2-android:ksoap2-android:3.1.1'
}

Para testar o Web service, vamos utilizar o mesmo servio disponibilizado no site
da W3Schools.

http://www.w3schools.com/webservices/tempcom/ert.asmx

Nesta pgina, se voc clicar no link Service Description, a pgina vai redirecionar para
o seguinte link, que apenas adiciona o parmetro ?wSDL no nal da URL:
http://www.w3schools.com/webservices/tempconvert.asmx?WSDL

Ao fazer isso, voc ver o arquivo WSDL desse servio, que basicamente declara
todos os mtodos que o Web service contm. O que geralmente fazemos gerar
um cliente de Web service, utilizando ferramentas que geram cdigo a partir desse
arquivo WSDL. Mas a biblioteca KSoap oferece uma alternativa para esta gerao
de cdigo e permite voc chamar os mtodos desse Web service manualmente.
Para chamar um mtodo de um web service, precisamos montar um XML no
formato do protocolo SOAP e fazer post na pgina do web service. Como isso
trabalhoso, o KSoap permite que voc apenas congure o nome do mtodo, os
parmetros que deseja enviar e internamente ele faz o trabalho pesado para criar
a mensagem no formato SOAP
A seguir, podemos verificar um exemplo de cdigo que mostra como executar o
mtodo CelsiusToFahrenheit deste web service.

CeIsiusToFahrenheitKSoapActivity.java
public class CelsiusToFahrenheitKSoapActivity extends AppCompatActivity {

public void onClickConverter(View view) {


Google Android - 4 em So

String celcius = tCelcius.getText().toString();


nal String fahrenheit = CelsiusToFahrenheit(URL, celcius);
run0nUiThread(new Runnable() {
@0verride
public void run() {
tFahrenheit.setText(fahrenheit);
}

});
}

// Faz o POST com KSOAP e retorna o resultado.


public String CelsiusToFahrenheit(String url, String celsius) throws Exception {
SoapSerializationEnvelope soapEnvelope = new
SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.implicitTypes = true;
soapEnvelope.dotNet = true;
Soap0bject soapReq = new
Soap0bject("http://www.w3schools.com/webservices/", "CelsiusToFahrenheit");
soapReq.addProperty("Celsius", celsius);
soapEnvelope.set0utputSoap0bject(soapReq);
int tineOut = 60000;
HttpTransportSE httpTransport = new HttpTransportSE(url, time0ut);
try {
// Faz a chamada
httpTransport.call("http://www.w3schools.com/webservices/CelsiusToFahrenheif
soapEnvelope);
// L o retorno
Object retObj = soapEnvelope.bodyIn;
if (retObj instanceof SoapFault) {
SoapFault fault = (SoapFault) retObj;
Exception ex = new Exception(fault.faultstring);
throw ex;
} else {
// Retorno OK

Soap0bject result = (Soap0bject) retObj;


if (result. getPropertyCount() G) {
Object obj = result.getProperty(0);
if (Obj != null && obj.getClass().equals(SoapPrimitive.class)) {
56DPFnitive j = (SoapPrimitive) obj;
String resultvariable = j.toString();
return resultvariable;
Captulo 17 I Web services 487
} else if (obj != null && obj instanceof String) {
String resultvariable = (String) obj;
return resultvariable;
}

} catch (Exception e) {
throw e;
}
return "";
}

Nota: vale lembrar que web services com WSDL esto sendo cada vez menos
utilizados como servios, principalmente no mundo mobile. Um dos motivos
disso porque o protocolo SOAP pesado, pois na prtica ele um grande
arquivo XML que ca indo para l e para c, trafegando na rede. Portanto,
preferencialmente, se puder escolher, crie servios simples que retornam os
dados em JSON.

Ao executar esse exemplo, o resultado ser o mesmo do exemplo anterior. Outra


forma de consultar os servios de Web service WSDL gerar o cdigo cliente,
como eu disse anteriormente. No caso do Android, eu j utilizei comercialmente
esta pgina para gerar o cdigo cliente para chamar um web service que tinha
muitos mtodos:
http://www. wsdl2code. com/

Nessa pgina, basta voc digitar a URL da WSDL do Web service e clicar para gerar
o cdigo. necessrio fazer um cadastro antes, mas at o momento que este livro
estava sendo escrito esse servio era gratuito. Depois de gerar o cdigo cliente do
servio, copie as classes para o projeto do Android Studio. No caso deste servio da
W3SchooIs, a classe que foi gerada chama-se TenpConvert e basicamente encapsula a API
do KSoap2, facilitando a vida do desenvolvedor para utilizar o web service. Interna
mente, essa classe vai utilizar exatamente a mesma API do KSoap2 que j estudamos.
A seguir, temos um exemplo de cdigo que utiliza a classe TenpConvert que foi ge
rada automaticamente pela pgina WW1zlWSdl2C0d6.CO77'l. Veja como o cdigo nal
simples. Podemos concluir que, se for necessrio acessar web services criados
com WSDL, vale a pena utilizar algum gerador de cdigo como zemos aqui.
488 Google Android - 41 Qdio
ffl CeIsiusToFahrenheitActivty.java

public class CelsiusToFahrenheitActivity extends AppCompatActivity {

public void onClickConverter(View view) {


new Thread() {
public void run() {
tenpconvert t = new TenoC0vert();
String celcius = tCelcius.getTet().toString();
nal String fahrenheit = t.CelsiusToFahrenheit(celcius);
run0nUiThread(new Runnable() {
@0verride
public void run() {
tFahrenheit.setTet(fahrenheit);
}

});
}

}.start();
}

17.9 Links teis

Neste captulo, aprendemos a consultar web services e ler o retorno nos formatos
XML e JSON, alm de termos estudado como consultar servios com WSDL.
Lembre-se de que, sempre que acessar a internet, preciso abrir uma thread para
no travar o processamento, e para isso utilizamos o mtodo startTask(codig0.
tasktistener, progress) da biblioteca android-utils. Eu particularmente fao assim
em meus projetos, e inclusive utilizo uma verso melhorada e com mais mtod05
da mesma classe HttpHelper que mostrei aqui.
Existem muitas outras bibliotecas para consultar web services no Android, C
devido variedade cada desenvolvedor acaba optando por utilizar a biblioteCH
que mais o agrada.
Apenas para fechar o captulo, vale a pena explicar um clssico bug referente 21
requisies HTTP no Android. Desde o incio no Android, existem duas clas55
que podem fazer requisies HTTP: a classe HttpURLConnection e a classe Httptliet
da famosa biblioteca da Apache. Mas, segundo um post ocial no blog do Google
Developers, a biblioteca HttpURLConnection apresenta alguns bugs at a verso 2.2 dv

\
Captulo 17 I Web services 439
Android, e deve-se utilizar a biblioteca HttpCl.ent. No entanto, do Android 23 ou su~
perior, esse bug foi resolvido, e o Google recomenda utilizar a classe HttpURLConnecton.
Para evitar esta salada de fruta, podemos utilizar uma biblioteca que encapsula esses
problemas e faz a correta utilizao destas APIs dependendo da verso do Android.
Esta biblioteca a 0KHttp, que est disponvel no seguinte endereo:
http://squaregithub.io/okhttp/
Sua utilizao bem simples, e recomendo que voc d uma olhada. Eu no posso
demonstrar exemplos de cada uma das bibliotecas neste livro, mas recomendo
uma leitura complementar para complementar seus estudos.
Android Developers Blog - Explicao do problema clssico com
I-IttpURLConnection vs I-IttpClient.
http://android-developers.blogspotcom.br/2011/09/androids-http-clients.html
0kHttp - Biblioteca para fazer requisies HTTP no Android.
http://squamgithub. io/okhttp/
Retrot - Biblioteca muito famosa para acessar servios REST
http://squaragithub. io/retrot/
Volley - Biblioteca ocial do Google para fazer requisies HTTP de forma
assncrona. Recomendo assistir a palestra feita no Google I/ O 2013 sobre
o Volley
http://developer android.com/training/1/olley

E para download de imagens existem tambm algumas bibliotecas bem populares


na comunidade de desenvolvedores Android.
Picasso - j conhecemos essa.
http://squamgithub. io/picasso/
Universal Image Loader - Outra biblioteca para fazer download de imagens.
https://gthub.com/nostra13/Android-Universal-Image-Loader
Android-Query (AQuery) - Outra biblioteca para fazer dovvnload de imagens.
https://code. google. com/p/androd-query
*~ `* cA|>ruLo18
z

Persistncia
"*'1

O Android tem integrao com o SQLite, um leve e poderoso banco de dados,


permitindo que voc utilize banco de dados normalmente em sua aplicao.
Embora o armazenamento em banco de dados seja a forma mais comum de
persistncia, o Android tambm permite que arquivos sejam salvos na memria
interna ou no SD card e tambm tem um sistema simples de persistncia de chave
e valor chamado de preferncias.

18.1 Salvando as preferncias do usurio com a classe SharedPreferences


Para comear a brincadeira, vamos aprender a salvar dados simples no formato
de chave e valor como se fosse uma HashTab1e. Para isso, podemos usar a classe
androd.content.SharedPreferences, que salva automaticamente os dados em um banco
de dados interno da aplicao.
A classe SharedPreferences deve ser utilizada para salvar valores pequenos, como
tipos primitivos e pequenas strings. Para encapsular as chamadas para a classe
SharedPreferences, costumo criar uma classe chamada Prefs, lembrando o termo
preferncias pois frequentemente essa funcionalidade chamada de salvar um
informao nas preferncias do usurio.
Essa classe faz parte da biblioteca androd-uts, ento o cdigo-fonte a seguir apenas para
sua referncia. Os mtodos setBoo1ean(contet,chave,va1or) , setInteger(contet,chave,vaiorl
S@t5'CF9(C0t@'C,Chave,va1or) salvam os valores no banco de dados interno do Android.
e os mtodos getBoo1ean(contet,chave) , getInteger(contet ,chave) e getString(contet,Ch3Vel
leem os valores salvos do banco de dados.

Prefs.java
package 1vroandrod.1ib.ut1s;
import androd.content.Contet;

490
Captulo 18 I Persistncia

import android.content.SharedPreferences;
public class Prefs {
// Identicador do banco de dados destas preferncias
public static nal String PREF_ID = "livroandroid";
public static void setBoolean(Context context, String chave, boolean on) {
SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(chave, on);
editor.commit();
}

public static boolean getBoolean(Context context, String chave) {


SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
boolean b = pref.getBoolean(chave, true);
return b;
}

public static void setInteger(Contet context, String chave, int valor) {


SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putInt(chave, valor);
editor.commit();
}

public static int getInteger(Context context, String chave) {


SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
int i = pref.getInt(chave, O);
return i;
}

public static void setString(Context context, String chave, String valor) {


SharedPreferences pref = context.getSharedPreferences(PREF_ID, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putString(chave, valor);
editor.comnit();
}

public static String getString(Context context, String chave) {


SharedPreferences pref = context.getSharedPreferences(PREF_ID, O);
String s = pref.getString(chave, "");
return s;
}

Nota; ao Salvar um valor com a classe SharedPreferences, criado um objeto do


tipo android.content.SharedPreferences.Editor e depois chamado 0 mtodo comnit(),
que efetiva as alteraes no banco de dados interno da aplicao.
492 Google Android - 4' edio
Para praticarmos o conceito, vamos aprimorar o projeto dos carros Ie salvar 0
indice da ltima tab selecionada pelo usurio, assim quando O lphcamo fm
aberto novamente a ltima tab ser mostrada automaticamente. No metodo
onTabSelected(tab), vamos salvar o ndice da tab selecionada, para dep0iS fwpefaf
o ndice salvo quando o fragment for criado.

tfsi (arrosTabFragment.java

inport livroandroid.lib.utils.Prefs;
public class CarrosTabFragnent extends BaseFragnent {
private TabLayout tabLayout;
private ViewPager viewPager;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {

// Cria o ViewPager e as tabs...

// Ao criar a view, mostra a ltima tab selecionada


int tabId = Prefs.getInteger(getContet(), "tabIdx);
viewPager.setCurrentIten(tabIdx);
return view;
}

@0verride
public void onTabSelected(TabLayout.Tab tab) {
// Se alterar a tab, atualiza o ViewPager
nViewPager.setCurrentIten(tab.getPosition());
// Salva o indice da pgina/tab selecionada
Prefs.setInteger(getContext(), "tabldx", nViewPager.getCurrentIten());
}

Feitas essas alteraes, execute o projeto novamente e selecione alguma tab. Depois
feche o aplicativo com o boto voltar e inicie o aplicativo novamente. O resultado
e que a aplicao ser iniciada com a ltima tab/pgina selecionada.
Neste exemplo, utilizamos a classe Prefs, que eneapsula o acesso classe
SharedPreferences. Conforme acabamos de ver, salvar pequenas informaes no
formato de chave e valor muito til em casos simples como esse, e a vantagem
que internamente o Android cria um banco de dados interno da a plicao para
armazenar essas informaoes.
Captulo 18 n Persistncia 493
18.2 Activity de conguraes
comum encontrar nos aplicativos telas de configuraes (settings) para o usu
rio personalizar os dados. Sendo assim, para praticarmos, vamos implementar
uma dessas telas no projeto dos carros, por isso deixamos preparado o menu
(onguraes no menu lateral.

Uma maneira de implementar esse tipo de funcionalidade criar o layout de uma


tela de conguraes manualmente e utilizar a classe SharedPreferences para salvar
os dados. Felizmente, o Android j tem um processo automatizado para isso, e a
vantagem que os dados so salvos automaticamente, alm de deixar a interface
de usurio coerente com o resto da plataforma.
Nosso objetivo criar duas activities: uma compatvel com o Android 2.x e outra
compatvel com Android 3.x ou superior. Portanto, ao selecionar o item (onguraes no
menu lateral, ser aberta uma dessas duas activities, conforme mostra a gura 18.1. O
lado esquerdo da gura mostra a activity executando no Android 5.0 e o direito mostra
o Android 23. Naturalmente, cada plataforma tem o seu padro de interface. Voc
no consegue ver, porque o livro impresso em preto e branco, mas no Android 5.0,
a action bar ser azul (cor primria) e alguns componentes como o checkbox cam
vermelhos (cor de acentuao), pois conguramos o tema Material com essas cores.
Na tela inicial que tem o Navigation Drawer, estamos atualizando o contedo
central do aplicativo apenas utilizando fragments. Foi dessa forma que zemos a

. . |
lista de carros e a pgina com o WebView. Mas, ao selecionar o item Conguraes no
menu, vamos abrir uma nova activity devido a problemas tcnicos.

V Notificacoes I
5562HVGA_

Receber alertas de Push sobre `


Livro dfi Iancamentos de novos carros.
j Noticacoes
Receber alertas de Push sobre
ncarnentos de novos carros.

Figura 18.1 - Activity de configuraes.


494 Google Android - 4 ed5
At o Android 23, a maneira de criar esse tipo de tela era utilizando uma activityi
lha android.preference.PreferenceActivity, fato que nos obriga a abrir uma nova act1v1tY
E a partir do Android 3.0 foi criado o fragment android . preference.PreferenceFragment,
que facilita criar telas de configuraes, mas infelizmente, at o momento
em que este livro est sendo escrito, esse fragment somente existe com a ver
so nativa (android.app.Fragment) e no com a biblioteca de compatibilidade
(android.support.v4.app.Fragment). Portanto, vamos ter de criar uma activity normal
(sem ser de compatibilidade) e congur-la para utilizar o tema Material.
Dadas as devidas explicaes, vamos colocar a mo na massa, ento crie estas
duas activities:

ConguracoesActivvity.java
package br.com.livroandroid.carros.activity.prefs;
@SuppressHarnings("deprecation")
public class ConguracoesActivivity extends android.preference.PreferenceActivity {
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Carrega as conguraes
addPreferencesFromResource(R.ml.preferences);
}

A prxima activity chamei de V11, para lembrar que ela compatvel com Android
3.0 (API Level 11) ou superior. Note que ela declara um fragment interno do tipo
android.preference.PreferenceFragnent e o insere como a raiz do layout.

Conguracoesvl1Activivity.java
package br.com.livroandroid.carros.activity.prefs;
@TrgetApt(But1d.vERs1oN_cooEs.HoNEvcoMB)
public class ConguracoesV11Activivity extends android.app.Activity {
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
// Adiciona o fragment de conguraes
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, new PrefsFragment());
ft.comnit();
}
C3PtuIo 18 I Persistncia 495
public static class PrefsFragment extends android.preference.PreferenceFragnent {
@Override

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
// Carrega as conguraes
addPreferencesFromResource(R.xml.preferences);
}

O mtodo addPreferencesFromResource(prefsResId) carrega um arquivo XML que


contem as conguraes das preferncias; portanto, crie o seguinte arquivo de
congurao na pasta /res/xml:

/res/xml/preferences.xmI
<?ml version="1.0" encoding="utf-8"?>
<PreferenceScreen mlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Livro Android">
<CheckBoPreference
android:key="PREF_CHECK_PUSH"
android:summary="Receber alertas de Push sobre lanamentos de novos carros."
android:title="Noticacoes" />

Esse arquivo XML declara uma tela de preferncias PreferenceScreen e cria uma
categoria PreferenceCategory chamada Livro Android, sendo que uma categoria pode
conter vrios componentes. O item CheckBoPreference mostra um checkbox para
o usurio selecionar uma opo. A vantagem de utilizar esse arquivo XML que
todos os valores so salvos automaticamente pelo Android, sem a necessidade de
nenhuma programao. Observe que no arquivo XML foi denido que a chave
do CheckBoxPreference PREF_CHECK_PUSH, conforme este cdigo:

<CheckBoxPreference android:key="PREF_CHECK_PUSH" ... />

Isso significa que, depois de salvar os dados na tela de configurao, basta lermos o
valor da chave PREF_CHECK_PUSH com a classe SharedPreferences.A classe PrefsUtils mostra
como ler se o checkbox est selecionado. Note que criei o pacote util no projeto
para adicionar as classes utilitrias que vamos criando durante o desenvolvimento.

PrefsUti|s.java
package br.com.livroandroid.carros.util;
public class PrefsUtils {
495 Google Android - 4 edio
// Verica se o usurio marcou o checkbox de Push ON nas congura@S
public static boolean isCheckPush0n(nal Context context) {
SharedPreferences sp = Preferenceanager.getDefaultSharedPreferences(context);
return sp.getBoolean("PREF_CHECK_PUSH", false);
}

Para a activity compatvel com Android 3.0 ou superior, vamos customizar as cores
do tema Material, mas no podemos utilizar o tema AppCompat, pois eSSa Uma
activity nativa que herda de android.app.Activity. Sendo assim, vamos criar mais um
tema chamado AppThene.Material. Repare que, embora a activity seja compatvel com
Android 3.0 (API Level 11), vamos customizar as cores apenas para o Android 5.0
(API Leve 21), pois foi quando o tema Material foi criado. Para verses anteriores,
a tela de configuraes ter a interface padro de cada plataforma.

/res/values/styles-v21.xml

<style name="AppTheme.NavDrawer" parent="AppTheme">

<style nane="AppTheme.Material" parent="android:Thene.Material.Light.DarkActionBar">

<iten nane="android:colorPrinary">@color/primary/iten>
<iten nane="android:colorPrinaryDark">@color/prinary_dark/iten
item nane="android:colorAccent">@color/accent/iten>

Para nalizar as conguraes, declare ambas as activities no AndroidManifest.xml.


Observe veja que a activity Vll utiliza o tema AppTheme.Material que acabamos de
congurar. Repare tambm que adicionei ambas as activities no pacote .activity.prefs.

AndroidManifest.mI

<activity android:name=".activity.prefs.ConguracoesActivivity"
android:label="Conguraes">
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:value=".activity.MainActivity" />

<activity android:name=".activity.prefs.ConguracoesV11Activivity"
android:label="Conguraes"
Captulo 18 u Persistncia 497
android:parentActivityName=".activity.MainActivity"
a"d"1d ="e="@sty1e/AppThene . Material">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android : value=" .activity . MainActivity" />

Nota: lembre-se de que o atributo android:parentActivityName somente existe para o


Android 4.0 ou superior e ele define a activity pai na hierarquia para voltar a navegao
caso o usurio pressione o boto de up navigation. Por questes de compatibilidade
com versoes anteriores, a tag congurada com o mesmo propsito.

Uma vez que as activities esto devidamente conguradas, altere o mtodo que
trata o evento de clique do menu lateral para chamar a activity apropriada con
forme a verso do Android.

MainActivity.java

@0verride
public void onNavDrawerItemSelected(. . .) {
if (position == 0) { . . . }
else if (position == 1) { . . .}
else if (position == 2) { // Abrir as conguraes...
if (Androidtils.isAndroid3Honeyconb()) {
startActivity(new Intent(this, ConguracoesV11Activivity.class));
} else {
startActivity(new Intent(this, ConguracoesActivivity.class)) ;
}

O resultado deve ser como a gura 18.1, que foi apresentada no incio deste tpico.
Lembre-se de que, depois de salvar os dados na tela de configuraes, voc precisa
ler o valor que foi salvo, e para isso criamos a classe PrefsUtils.
boolean b = PrefsUtils.isCheckPushOn(this); // true se marcou

Onde inserir esse cdigo para ler essa informao deixarei como lio de casa para
voc pois o objetivo foi apenas mostrar como criar esse tipo de tela de configura
7

es (preferncias). Para complementar seus estudos, recomendo uma leitura da


documentao ocial, assim voc pode Ver outros exemplos e mais componentes
que podem ser utilizados para criar este tipo de tela.
http://developerandroid.com/guide/topics/ui/settings.html
493 Google Android - 4 edio
18.3 Lendo e salvando arquivos
Para ler e salvar arquivos no Android, so utilizadas as tradicionais interfaces
java.o.InputStream e java.o.0utputStrean da linguagem Java. Portanto, se voce 12
trabalhou com arquivos em java, vai se sentir em casa. A nica coisa que precisa
mos fazer obter uma pasta para salvar ou ler os arquivos, representada por um
objeto do tipo java.io.File.
Podemos salvar arquivos na memria interna ou externa do dispositivo. A memria
interna utilizada para salvar arquivos que ficam privados para sua aplicao. A
memria externa, ou seja, o carto de memria (SD card), permite salvar os ar
quivos de forma pblica, mas nesse caso tanto o usurio quanto outras aplicaes
podero ter acesso aos arquivos.

18.4 Trabalhando com arquivos na memria interna


Para abrir arquivos privados da aplicao, podemos utilizar os seguintes mtodos da
classe androd.content.Context, que s para lembrar a classe me da androd . app.Actvty.
F1eInputStream openF1eInput(nome) -Abre um arquivo para leitura, retornando
um F1eInputStream, ou a exceo F1eNotFoundException lanada caso o arquivo
no seja encontrado.
Fe0utputStream openF1e0utput(name, modo) - Abre um arquivo para escrita,
retornando um F1e0utputStream. No parmetro modo, podem ser informadas
as constantes Context.MODE_PRIVATE, que o padro, ou Context .MODE_APPEND, para
sempre usar o arquivo j existente e ir concatenando as novas informaes
no nal do arquivo.
boolean de1eteF1e(Strng nome) - Exclui o arquivo e retorna verdadeiro se a
operao foi realizada com sucesso.
Utilizando esses mtodos para ler e salvar arquivos, o arquivo ser criado no dire
trio /data/data/pacote.do.apIicativ/les/, o qual e privado da aplicao e justamente
por isso conhecido como memria interna. Caso o objetivo seja salvar o arquivo,
podemos utilizar o mtodo openF1e0utput("arquvo.tt", Contet.MODE_PRIVATE) para
abrir arquivo e automaticamente retornar uma FiIeOutputStream para escrever
s dados. Mas caso o seu objetivo seja somente obter um objeto java.io.F1e que
represente a localizao do arquivo, utilize o mtodo getF1eStreamPath (arquivo) da
classe Context. Como exemplo, veja este trecho de cdigo:
File le = context.getF1eStreamPath("arquvo.txt");
Captulo 18 I Persistncia 499
Nesse caso, retornada a localizao do arquivo (sem cri-lo):
/data/data/pacote.do.aplicativo/les/arquivo.txt

Para demonstrar como salvar arquivos na memria interna da aplicao, vamos


salvar em arquivo o prprio arquivo JSON que estamos buscando do site do
livro. Com esse propsito, vamos criar o mtodo salvaArquivoNaMenoriaInterna() na
classe CarroService.

(arroService.java

public class CarroService {

public static List getCarros(Context context, String tipo) throws IOEception {


String url = URL.replace("{tipo}", tipo);
String json = HttpHelper.doGet(url);
salvaArquivoNaMenoriaInterna(context, url, json);
List carros = parserJSON(contet, json);
return carros;
}

private static void salvaArquivoNaMenoriaInterna(Context context, String url, String json) {


String leName = url.substring(url.lastInde0f("/")+1);
File le = FileUtils.getFile(context,leNane);
IOUtils.writeString(le, json);
Log.d(TAG, "Arquivo salvo: " + le);
}

Feito isso, execute o projeto novamente e voc ver as seguintes mensagens no


LogCat, indicando que os arquivos foram salvos com sucesso.
Arquivo salvo: /data/data/br.com.livroandroid.carros/les/carros_classicos.json
Arquivo salvo: /data/data/br.com.livroandroid.carros/les/carros_esportivos.json
Arquivo salvo: /data/data/br.con.livroandroid.carros/les/carros_luo.json

Lembre-se de que a pasta /data/data/brcomlivroandroid.carros/les privada da


aplicao e para visualizarmos o seu contedo no emulador podemos utilizar a
ferramenta adb disponvel no Android SDK. Para visualizar os arquivos, abra um
terminal e digite os seguintes comandos:
adb shell
cd /data/data/br.com.livroandroid.carros/les
ls
500 Google Android - 4 ed
. A~ . ~_ .-~f
" _rional
alado.
- z~
--~' . -z -. '~ - -
zj `izeo
Dica: lembre-se de queocomandoadbdeve estarnoPAlH dosistcmaopcrac z ,

` )CS
ou voce deve estar na pasta /arzdrozd-sd/z/pIairm-iIs/ na qual o adb Lbf mf
No Android Studio, para visualizar o local onde o SDK esta instalado, util `
menu File > Project Structure > SDK Location. Aproveite c vcriquc as outris opzl
dessa janela, pois h mais informaes interessantes nela.

Caso voc queira visualizar o contedo do arquivo no prompt, d1gltC O cm2d0


cat none_arquivo, como por exemplo:
cat carros_esportivos.json

(>
A Figura 18.2 mostra os comandos explicados e os arquivos salvos na memoria
interna do emulador.

E Prompt de Comando " E n

Figura 18.2 - Arquivos salvos no emulutloi

Outra maneira de visualizar os arquivos utilizando a licrramcnta Android Device


Monitor, que pode ser aberta pelo menu Tools > Android > Android Device Monitor do Android
Studio. Na janela File Explorer, ao navegarmos na estrutura de arquivos do emula
dor, podemos ver que os arquivos estaro la, con lormc mostra a figura l83. (laso
queira copiar o arquivo do emulador para seu computador, utilize o botao Pulla
File from de Device, localizado no canto direito superior da janela.

Nota: o emulador permite visualizar todos os arquivos, inas um dispositivo


real ter acess a estas pastas liloqueadas por motivos de seguranca. Apenas
dispositivos que foram rooteados tem acesso a essa pasta. Porem, dar acesso root
no Android no e recomendado, uma vez que isso pode trazer vulneralvilidades
de segurana para sistema.
Captulo 18 I Persistncia 501
Android Device Monitor - U
Fie Edit Run Window Help

lQil<.^FF5.? -ll Hf Ilz z. - fa Q.


|igg._||-<~,'fl@lm.z| |...|egz
QQ Dev if li pe iii .Inf-z..l milzz-zlenfzl Hfez-
Nama Size Date TimeV
22 19 Em- .aaa-~ E' 5;

^ 4 LB data 2015-05-03 18:15


| Ige-nexus_5-065f3c830t P 18- app 2015-05-03 18:17
emulator-5554 f f app-asec 2015-05-03 13;14
system_process 9 E5 app-Iib 2015-05-03 18:14
com.android.systemuq Ez app-private 2015-05-03 18:14
com.android.inputme ' baCl<UD 2015-05-03 18:17
com.androici.server.te bugreports 2015-05-03 18:14
com.android.phone ' IB dalvik-cache 2015-05-03 18:14
android.process.med 1 data 2015-05-03 13;17
android.process.acon 4 br.com.iivroandroidcarros 2015-05-03 18:17
com.android.echang LL* cache 2015-05-03 18:17
com.android.Iaunche 1 LB- files 2015-05-03 18:17
com.android.emaii carros_ciassicosJson 9042 2015-05-03 18:17
com.google.android. carros_esportivosjson 9188 2015-05-03 18:17
com.googie.process.g carros_iuxoJson 7892 2015-05-03 18:17
< 2,-,-.
` _ nn.
___lo z..,-:mm
> _-.-" 5cf-
,i nh , _ V , ams-n=:-na 1a-17
. _ 2 - ,.___~} 1
I 1 43M of 491M

Figura 18.3 - Arquivo salvo ria pasta privada da aplicao.

18.5 Trabalhando com arquivos na memria externa (SD card)


O carto de memria (SD card) muito utilizado como forma de armazenamento,
principalmente para salvar arquivos temporrios da aplicao que no precisam ser
privados e com isso liberar espao na memria interna do aparelho. Esse conceito
pode ser utilizado, por exemplo, para fazer dovvnload de fotos para o SD card.
Para salvar ou ler arquivos no carto de memria, precisamos obter o objeto
java.io.Fiie que representa essa pasta, conforme mostra o seguinte codigo:
// Retorna a pasta do SD card
File sdcardDir = android.os.Environment.getExternalStorageDirectory();

Depois de obter o objeto File, podemos criar pastas e arquivos na raiz do SD


Card e a partir daqui pura programao. Mas o recomendado no poluir o
carto de memria do usurio, principalmente na pasta raiz. O correto utilizar
35 pastas padres para fotos, msicas, downloads etc. Isso pode ser feito com o
mtodo getExterna1StoragePub1icDirectory(tipo), cujo parmetro tipo pode ser qual
ClL1I` LIITIEI ClS'[2lS COI'1S[8I1SI DI 1
RECTORY_MUSIC, DIRECTORY_PODCASTS, DIRECTORY_RINGTONES

DIRECTORY ALARMS DIRECTORY NOTIFICATIONS, DIRECTORY_PICTURES, DIRECTORY_MOVIES,

DIRECTORY_DONNLOADS, Ou DIRECTORY_DCIM.
502 Google Android - 4 edio
// Retorna a pasta do SD card (/sdcard/DCIM)
File sdcardDir = Environment.getEternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);

Entretanto, caso seja necessrio salvar arquivos de forma privada no carto de


memria, utilize o mtodo getEternalFilesDir(tipo) da classe Context, no qual 0
tipo pode ser nulo para acessar a raiz da pasta, ou uma das constantes explicadas
anteriormente, menos a DCIM.
// Retorna a pasta raiz do SD card privada da aplicao.
File sdcardDir = context.getEternalFilesDir(null);

A vantagem do mtodo getEternalFilesDir(tipo) que ele automaticamente vai


excluir os arquivos do SD card, quando a aplicao for desinstalada. Para prati
carmos o conceito, vamos salvar novamente o arquivo JSON dos carros em ar
quivo, mas desta vez no SD card. Antes de continuarmos, adicione as permisses
wRnE_ExrERNAL_sToRAcE e REAp_ExrERNAL_sroRAcE no arquivo AndroidMmie5r.xml, pois
so necessrias para ler e escrever arquivos no carto de memria.

AndroidManifest.mI

<nanifest . . . />
<uses-permission android:nane="android.pernission.INTERNET" />
<uses-permission android:nane="android.pernission.ACCESS_NETw0RK_STATE" />
<uses-permission android:nane="android.permission.HRITE_EXTERNAL_STORAGE" />
<uses-permission android:nane="android.permission.READ_EXTERNAL_STORAGE" /
<application ... />

Na sequncia, atualize o cdigo-fonte da classe CarroService.

(arroServce.java

public class CarroService {

public static List getCarros(Contet context, String tipo) throws IOException {


String url = URL.replace("{tipo}", tipo); '
String json = HttpHelper.doGet(url);
salvaArquivoNaMenoriaEterna(context, url, json);
List carros = parserJSON(contet, json);
return carros;
}

private static void salvaArquivoNaMemoriaExterna(Context context, String url, String json) {


String leNane = url.substring(url.lastInde0f("/")+1);
// Cria um arquivo privado
Captulo 18 I Persistncia 503 I .V !
File f = SDCardUtils.getPrivateFile(context leNane Environment.DIRECTORY_DONNLOADS)~
IOUtils.writeString(f, json);
Log.d(TAG, "1) Arquivo privado salvo na pasta downloads: " + f);
// Cria um arquivo pblico
f = SDCardUtils.getPublicFile(leName, Environment.DIRECTORY_DONNLOADS);
IOUtils.writeString(f, json);
Log.d(TAG, "2) Arquivo pblico salvo na pasta downloads: " + f);
}

Feito isso, execute o projeto novamente e voc ver as seguintes mensagens no


LogCat, indicando que os arquivos foram salvos no SD card:
D/CarroService? 1) Arquivo privado salvo na pasta downloads:
/storage/sdcard/Android/data/br.com.livroandroid.carros/les/Download/carros_esportivos.json

D/CarroService? 2) Arquivo pblico salvo na pasta downloads:


/storage/sdcard/Download/carros_esportivos.json

E novamente, se voc quiser, possvel visualizar os arquivos com o auxlio da


ferramenta adb ou pelo Android Device Monitor. Veja que salvei um arquivo na pasta
pblica a qual o usurio tem acesso e outro na pasta privada.

Nota: estou utilizando no cdigo as classes FileUtils e SDCardUtils, dentre outras,


para facilitar a explicao. Mas no pense que porque estou escondendo a
complexidade de voc. Acredito que essas classes vo facilitar seu aprendizado,
at porque todo o cdigo-fonte est disponvel no Gitl-Iub para sua consulta. No
meu dia a dia, exatamente assim que gosto de programar, pois prero encapsular
a lgica em classes utilitrias, que resolvem o problema em uma linha de cdigo.

18.6 Outros mtodos da classe Context


Outra maneira de obter as principais pastas do sistema para criar arquivos
utilizar os mtodos utilitrios da classe Context, explicados logo a seguir:
^At0d0 Descno
File getFilesDir() letorna o caminho absoluto da pasta em que
os arquivos privados so salvos, com o mtodo
openFile0utput(String, int).
File getFileStreamPath(name) Idem ao anterior, mas retorna o caminho completo do
arquivo.
504 Google Android - 4 edio

Mit?? _.. _ ,., ___t. a _ __.,_--.?,$2(9P:l,_--_a,.-__.----_ e - e P se c


File getEterna1Fi1esDir(type)j explicado anteriormente e retorna a paSf3 Pflvada
dentro do SD card para o tipo fornecido (DCM
Downloads etc.).
File getCacheDr() Retorna uma pasta temporria de sistema ideal para
fazer cache de arquivos. Se o sistema ficar com baixa
memria, ele pode excluir os arquivos desta pasta para
obter mais recursos.

Caso queira brincar com esses mtodos, insira o seguinte cdigo dentro de qual
quer activity:
Log.d tag","getF1esDr > " + getFi1esDir());
Log.d tag","getFlesDir > " + getFi1eStreamPath("arquivo.tt"));
Log.d tag","getF1esDir > " + getExterna1F1esDr(Environment.DIRECTORY_DCIM));
Log.d tag","getF1esDir > " + getCacheDir());

Como resultado, as seguintes mensagens sero impressas no LogCat:


D/tag getFi1esDir /data/data/br.con.1vroandroid.carros/les
D/tag: getF1esDir /data/data/br.com.Ivroandroid.carros/les/arquivo.txt
D/tag: getFi1esDir /storage/sdcard/Android/data/br.com.1ivroandroid.carros/les/DCIH
D/tag: getFi1esDir /data/data/br.com.1ivroandroid.carros/cache

18.7 Brincando de fazer cache

O projeto dos carros est buscando os carros do servidor, fazendo uma requisi
o HTTP no web service que est no site. Mas j que aprendemos a salvar os
arquivos, por que no aproveitamos e fazemos um cache?
Caso o JSON dos carros esteja salvo em arquivo, podemos ler o JSON direta
mente desse arquivo e evitar uma conexo com a internet, alm de melhorar o
desempenho do aplicativo. A lgica para a busca ser a seguinte:
1. O aplicativo deve fazer a consulta dos carros tentando ler o arquivo JSON
que est salvo na memria interna.
2. Se o arquivo existir, faz a leitura e retorna a lista de carros.

3. Se o arquivo no existir, busca os carros no web service e salva o arquivo


com o JSON.

Para implementar essa funcionalidade, vamos renomear 0 mtodo getCarros() para


getCarrosFromwebServce(). Pela assinatura, j deu para entender o que 5 mtodo
vai fazer. No seu lugar, vamos criar outro mtodo getCarros().
Captulo 18 I Persistncia 505
Feito isso, vamos criar o mtodo getCarrosFronArquivo() para criar a listar de carros a
partir do arquivo que est salvo na memria interna, caso o arquivo exista, claro.
E o mtodo getCarros() vai conter a grande lgica, que consiste em tentar ler os
carros do arquivo. Caso o arquivo no exista, vamos buscar os dados do Web
service. O cdigo-fonte a seguir mostra como fazer essa lgica:

CarroService.java

public class CarroService {


public static List getCarros(Context context, String tipo) throws IOException {
List carros = getCarrosFronArquivo(context,tipo);
if(carros != null && carros.size() > G) {
// Encontrou o arquivo
return carros;
}

// Se no encontrar, busca no web service


carros = getCarrosFromNebService(context,tipo);
return carros;
}

// Abre o arquivo salvo, se existir, e cria a lista de carros


public static List getCarrosFromArquivo(Context context, String tipo)
throws IOException {
String leNane = String.fornat("carros_%s.json",tipo);
Log.d(TAG,"Abrindo arquivo: " + leNane);
// L o arquivo da memria interna
String json = FileUtils.readFile(contet,leName,"UTF-8");
if(json == null) {
Log.d(TAG,"Arquivo "+leNane+" no encontrado.");
return null;
}
List carros = parserJSON(context, json);
Log.d(TAG,"Carros lidos do arquivo "+leNane+".");
return CI``0S

l/ Faz a requisio HTTP, cria a lista de carros e salva o JSON em arquivo


public static List getCarrosFromwebService(Context context, String tipo)
throws IOException {
String url = URL.replace("{tip0}" DO);
Log.d(TAG,"URL: " + url);
String json = HttpHelper.d0G(UFl)3
salvaArquivoNaMenoriaInterna(context, url, json);
505 Google Android - 4' edio
List carros = parserJSON(contet, json);
return carros;
}

private static void sa1vaArquivoNaMemoriaInterna(Contet context, String url. 5f19 1500) {

l
private static List parserJSON(Contet context, String json) throws I0EceDU0 U
}

Veja que no codigo estou salvando os arquivos na memria inteml, 6 11510 alterei
nada nessa parte de salvar arquivos, nem no mtodo que faz o parser do JSON.
Portanto, se voc executar 0 projeto novamente, tudo dever continuar funcio
nando. Caso os arquivos no existam na memria interna, voc ver as seguintes
mensagens no I.ogCat:
Abrindo arquivo: carros_1uo.json
Arquivo (carros_1uxo.json) no encontrado.
URL: http://www.iivroandroid.con.br/livro/carros/carros_1uo.json
Arquivo salvo: /data/data/br.com.1ivroandroid.carros/ies/carros_1uxo.json

E. caso os arquivos ja existam, sera leito o cache e voc ver as seguintes mensagens:
Abrindo arquivo: carros_iuo.json
Carros lidos do arquivo carros_1uo.json

Nesta brincadeira, voc aprendeu a salvar e ler arquivos, tanto na memoria interna
quanto na memria externa. O resultado at que cou legal, pois o aplicativo dos
carros fez cache dos arquivos, portanto, a lista sera carregada mais rapidamente.
Mas o nosso principal objetivo neste captulo salvar os carros em um banco de
dados, c no em arquivos. Portanto, no prximo topico vamos comear o nosso
estudo sobre SQLite.

18.8 Banco de dados SQLite

O Android tem suporte ao SQLite (Iirt://wwuzsqlitc.org), um leve e poderoso ban


co de dados. Cada aplicao pode criar um ou mais banco de dados que ficam
localizados na seguinte pasta:
/rlzzru/lur1/u're..alicuri1.'/lmluses/

Nota: um banco de dados e visvel somente ii aplicacao que o criou.


Captulo 18 I Persistncia 507
O banco de dados pode ser criado de vrias formas:
Utilizando a API do Android para o SQLite. Logo depois de criar o banco
de dados, possvel executar um script SQL pela API para criar as tabelas
e preencher os dados.
Usando um cliente do SQLite como o SQLite Expert Personal, que gratuito e
est disponvel para download em: http://wwwsqliteexpert.com/. Eu tambm
gosto muito do plugin SQLite Manager para o Firefox.

P)
Utilizando o aplicativo sqIite3 pelo console do emulador. Mais detalhes sobre
o sqIite3 podem ser acessados em http://www.sqlite.org/sqlite.html.
Basicamente, podemos dizer que existem duas maneiras de criar o banco de
dados no aplicativo. Ou utilizamos a API e escrevemos os scripts SQL para criar
as tabelas, ou criamos o banco de dados com uma ferramenta externa e depois
importamos o banco de dados j pronto no projeto. Neste ltimo, voc precisa
escrever algum cdigo que vai copiar o arquivo do banco de dados j pronto para
dentro das pastas do aplicativo.
No ro`eto dos carros o banco de dados ser criado utilizando a rimeira o o
que utilizando a API e executando um script SQL para criar as tabelas neces
srias para o aplicativo funcionar.

18.9 Criao de um banco de dados diretamente com a API


No projeto, vamos criar um banco de dados com uma tabela chamada carro,
conforme este script SQL.
create table if not exists carro (_id integer primary key autoincrement,none text, desc
text, url_foto text,url_info text,url_video text, latitude tet,longitude text, tipo text);

Eu acredito que a API do Android seja to simples que a maneira mais fcil de
explicar mostrar logo de uma vez o cdigo-fonte da classe que vamos utilizar.
Ento crie a classe CarroDB no pacote domain, conforme demonstrado a seguir. Essa
classe contm mtodos para listar todos os carros por tipo, salvar ou atualizar
um carro, excluir um carro e excluir todos os carros pelo tipo.

CarroDB.java
package br.com.livroandroid.carros.domain;
public class CarroDB extends SQLite0penHelP@f f
private static final String TAG = "SCi1"
// Nome do banco
Google Android - 4= edio

public static nal String NOHE_BANCO = "livro_android.sqlite";


private static nal int VERSAO_BANCO = 1;
public CarroDB(Context context) {
// context, nome do banco, factory, verso
super(context, NOHE_BANCO, null, VERSAO_BANCO);
}

@0verride
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "Criando a Tabela carro...");
db.execSQL("create table if not exists carro (_id integer primary key
autoincrenent,nome text, desc text, url_foto text,url_info text,url_video text,
latitude text,longitude text, tipo tet);");
Log.d(TAG, "Tabela carro criada com sucesso.");
}
@0verride
public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
// Caso mude a verso do banco de dados, podemos executar um SQL aqui
}
// Insere um novo carro, ou atualiza se j existe
public long save(Carro carro) {
long id = carro.id;
SQLiteDatabase db = getwritableDatabase();
try {
Contentvalues values = new ContentValues();
values.put none", carro.nome);
values.put("desc", carro.desc);
values.put("url_foto", carro.urlFoto);
values.put| url_info", carro.urlInfo);
values.put url_video", carro.urlVideo);
values.put latitude", carro.latitude);
values.put longitude", carro.longitude);
values.put tipo", carro.tipo);
if (id != 0) {
String _id = String.valueOf(carro.id);
String[] whereArgs = new String[]{_id}
// update carro set values = ... where _id=?
int count = db.update("carro", values, "_id=?", whereArgs);
return count;
} else {
// insert into carro values (...)
id = db.insert("carro", "", values);
~return id;
}

} nally {
509
Captulo 18 n Persistncia

db.close();
}

}
// Deleta o carro
public int delete(Carro carro) {
SQLiteDatabase db = getwritableDatabase();
try {
// delete from carro where _id=?
int count = db.delete("carro", "_id=?", new String[]{String.value0f(carro.id)});
Log.i(TAG, "Deletou [" + count + "] registro.");
return count;
} nally {
db.close();
}

}
// Deleta os carros do tipo fornecido
public int deleteCarrosByTipo(String tipo) {
SQLiteDatabase db = getwritableDatabase();
try {
// delete from carro where _id=?
int count = db.delete("carro", "tipo=?", new String[]{tipo});
Log.i(TAG, "Deletou [" + count + "] registros");
return count;
} nally {
db.close();
}

}
// Consulta a lista com todos os carros
public List ndAll() {
SQLiteDatabase db = getwritableDatabase();
try {
// select * from carro
Cursor c = db.query("carro", null, null null, null, null, null, null);
return toList(c);
} nally {
db.close();
}

try
|| ti { 7
// Consulta o carro pelo tipo
public List ndAllByTipo(5F9 IPO) {

Q:_"
SQLiteDatabase db = getwrtbteDtbaS@();

// select * from carro where ip


'" + tipo + "'", null, null, null, null)
Cursor c = db.query("carro", HU, "IDO
return toList(c);
Google Android - 4 edio

} nally {
db.close();
}

// L o cursor e cria a lista de carros


private List toList(Cursor c) {
List carros = new ArrayList();
if (c.moveToFirst()) {
do {
Carro carro = new Carro();
carros.add(carro);
// recupera os atributos de carro
carro.id = c.getLong(c.getColumnInde("_id"));
carro.nome = c.getString(c.getColumnInde("nome"));
carro.desc = c.getString(c.getColumnInde("desc"));
carro.urlInfo = c.getString(c.getColumnInde("url_info"));
carro.urlFoto = c.getString(c.getColumnInde("url_foto"));
carro.urlVideo = c.getString(c.getColunnInde("url_video"));
carro.latitude = c.getString(c.getColunnInde("latitude"));
carro.longitude = c.getString(c.getColumnInde("longitude"));
carro.tipo = c.getString(c.getColumnInde("tipo"));
} while (c.moveToNet());

return CI'`0Sj
}

// Executa um SQL
public void execSQL(String sql) {
SQLiteDatabase db = getwritableDatabase();
try {
db.eecSQL(sql);
} nally {
db.close();
}

// Executa um SQL
public void execSQL(String sql, 0bject[] args) {
SQLiteDatabase db = getwritableDatabase();
try {
db.eecSQL(sql, args);
] nally {
db.close();
}

}
Captulo 18 n Persistncia 511
Agora vamos explicar um pouco do cdigo. No Android, as classes que precisam
criar ou abrir um banco de dados devem herdar de SQLite0penHelper.
public class CarroDB extends SQLiteOpenHelper {

Feito isso, no construtor voc deve informar o nome do banco de dados e a verso,
que um cdigo numrico que podemos incrementar.
public CarroDB(Context context) {
super(context, NOME_BANCO, null, VERSAO_BANCO);
}

Para utilizar o banco de dados, basta chamar os mtodos getwritableDatabase()


ou getReadableDatabase(), que retornam um objeto do tipo SQLiteDatabase, o qual
representa a conexo com o banco de dados. Caso o banco de dados no exista,
o mtodo onCreate(SQLiteDatabase) chamado para executar um script SQL com o
objetivo de criar as tabelas no banco de dados.
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table if not exists carro (_id integer primary key
autoincrement,nome text, desc text, url_foto text,url_info tet,url_video text,
latitude text,longitude text, tipo text);");
}

Caso a verso do banco de dados informada no construtor seja diferente da verso


atual, por exemplo, se voc mudou de 1 para 2, o mtodo onUpgrade(SQLiteDatabase
db, int oldversion, int newversion) chamado. Nesse momento, podemos executar
algum script para atualizar a verso do banco de dados, seja para criar novas
tabelas, adicionar/ alterar colunas etc.
public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
// Caso mude a verso do banco de dados, podemos executar um SQL aqui
if(oldVersion == 1 && newVersion == 2) {
// Mudou a verso...

}
}

- Il ' ll
Nota: recomendvel que para cada tabela crie-se uma coluna com o nome _id
que seja do tipo autoincremento. Dessa forma, ao inserir o registro o valor do
campo " id" omitido, pois gerado automaticamente pelo SQLite.

Depois de criar esse template bsico, dentro da classe voc pode criar os mto
dos que quiser. A lgica sempre consiste em obter o objeto SQLiteDatabase, fazer
S12 Google Android - 4 edio
alguma operao no banco de dados e depois fechar a conexo. A segulf, m05
um template bsico de um mtodo qualquer:
public class CarroDB extends SQLite0penHelper {

public void neuetodo (){


SQLiteDatabase db = getNritableDatabase();
try {
// Faa o que quiser aqui...
db.insert(...);
db.update(...);
db.delete(...);
db.eecSQL("sql aqui");
} nally {
db.close(); // Fecha a conexo.
}

Nota: ao chamar o mtodo getwritableDatabase(), o Android vai tentar abrir o


banco de dados e caso o banco no exista o mtodo onCreate(db) ser chamado.
Nesse momento, voc deve executar algum SQL para criar as tabelas. Caso o
parmetro da verso que foi passado no construtor da classe seja diferente da
verso atual, o mtodo onUpgrade(db,oldVersino,newversion) ser chamado para o
aplicativo atualizar o banco de dados.

Embora o cdigo-fonte da classe CarroDB esteja completo, nos prximos tpicos


vou explicar separadamente cada parte do cdigo, a fim de detalhar um pouco
como funciona a API do SQLite.

18.10 Insero de registros no banco de dados


Para inserir registros no banco de dados, necessrio criar um objeto do tipo
android.content.ContentValues, que contm uma estrutura simples de chave e valor 1

em que cada chave precisa corresponder ao nome de uma coluna da tabela.


Depois de criar o objeto Contentvalues com os valores para inserir o registro, basta
chamar o mtodo insert(tabela,nullColunnHack, contentvalues). A seguir, podemos
visualizar um exemplo de como inserir um carro em uma tabela que contm 35
colunas nome e descrio.
Captulo 18 I Persistncia 513
// Insere um novo carro
Contentvalues valores = new ContentValues();
valores.put("nome", "Carro");
valores.put("desc", "Descrio");
db.insert("carro", null, valores);

Este cdigo equivalente ao seguinte SQL:


insert into table carro(nome,descricao) values ("Carro","Descro");
Veja a explicao dos parmetros do mtodo tnsert( . . . ).
Parmetro Descrio
SFHQ tabela Nome da tabela.
String nullColumnHack Nome de uma coluna opcional usada para no permitir que
um registro completamente nulo seja inserido. No utiliza
remos esse campo.
Contentvalues values Estrutura de chave e valores, com os valores para inserir.

18.11 Atualizao de registros no banco de dados


Para atualizar um registro, deve-se utilizar o mtodo update(tabela, contentvalues,
where, whereArgs) e passar uma string para o argumento where do mtodo, em que
o valor do id do registro pode ser utilizado para identicar o registro que deve
ser atualizado.
O exemplo a seguir mostra como atualizar o carro de id=1:
// Cria os dados para atualizar o carro td=1
long id = 1;
Contentvalues valores = new ContentValues();
valores.put("nome", "novo nome");
valores.put("desc", "Descrio");

// Atualiza o carro com id=1 com estes valores


db.update("carro", valores, "_d = " + d, HUU);

// Outra forma informar os parmetros no terceiro parmetro (array)


db.update("carro",valores,"_d=?",new Strtng[]{Strng.value0f(id)});

Esse cdigo equivalente ao seguinte SQL:


update carro set nome="novo nome", descro="Descrto" where _id=1;

Veja a explicao dos parmetros do mtodo update( . . . ).


514 Google Android - 41 edio
Parmetro Descrio
string rabeta P PN5BS*ciiSitli
Contentvalues values Estrutura de chave e valores, com os valores para atualizao.
String where String com a clusula where utilizada para identicar o registro.
Nesse caso, pode ser uma string com o texto "id=1", ou uma string
com o texto "d=?" , tornando necessrio usar o ultimo argumento
para informar o valor dos parmetros.
String whereArgs[] Array com os parmetros necessrios, caso a clusula where dena
algum parmetro com "?".
O cdigo para inserir e atualizar um registro na tabela simples. Veja que basi
camente voc precisa preencher o objeto Contentvalues, em que o nome de cada
chave precisa ser exatamente o nome da coluna da tabela.
A classe Contentvalues facilita o trabalho para inserir ou atualizar registros, pois
voc no precisa escrever a instruo SQL de cada comando. Caso voc queria
fazer manualmente cada SQL, basta utilizar o mtodo eecSQL( sql).

18.12 Excluso de registros do banco de dados


Para remover um registro, deve-se utilizar o mtodo delete(tabela,where,whereArgs).
O exemplo de cdigo a seguir mostra como excluir um carro com o id=1:
// Deleta o carro 1
long id = 1;
// Deleta o carro com d=1
db.delete("carro", "_id=" + d,null);
// Ou informando o terceiro parmetro
db.delete("carro", "_d=?",new Strng[]{String.value0f(id)});
Esse cdigo equivalente ao seguinte SQL:
delete from carro where _id=1;

18.13 Busca de registros no banco de dados

Para consultar os registros de uma tabela, utilizado o mtodo query(. . .) informando


as colunas desejadas e a clusula where para criar o SQL. O retorno um objeto do
tipo androd.database.Cursor, que deve ser utilizado para ler os valores da consulta
Para exemplicar, digamos que voc precise executar este SQL:
SELECT _i'd,nome,descricao from carro where id z 2
Captulo 18 I Persistncia S15

Pela API, isso pode ser feito com o seguinte cdigo:


String id : Illll;
Cursor c = db.query("carro", new String[] {"_id","nome","descricao"}, "_id=?", new
String[]{id}, null, null, null);
// Se encontrou...
if (c.getCount() > 0) {
Carro carro = new Carro();
//Posiciona no primeiro resultado
c.moveToFirst();
// Faz a leitura dos valores
carro.id = c.getLong(0);
carro.nome = c.getString(1);
carro.descricao = c.getString(2);
}

Veja a explicao dos parmetros do mtodo query( . . . ).


Parmetro Descrio
boolean distinct Mesmo funcionamento da palavra distinct em um SQL normal.
ll tilizado ara ue o resultado no contenha re istros re etidos.
Esse parmetro opcional e existe uma assinatura desse mtodo
sem ele.
String tabela Nome da tabela.
String colunas[] Array com os nomes das colunas para seleo. Se for passado
como parmetro um array nulo, todas as colunas sero retornadas.
String selecao[] Contm a clusula where utilizada para ltrar os registros. Se for
informado um parmetro nulo, todos os registros sero retor
nados
String selecaoArgs[] Argumentos "?" da clusula where, caso necessrio.
String groupBy Nome das colunas para agrupar (group by).
String orderBy Nome das colunas para ordenar (order by).

18.14 Mtodos da classe Cursor


A classe android.database.Cursor utilizada para ler os resultados de um registro
contm os seguintes mtodos:
515 Google Android - 4 edio M

Mtodo ' ` Descriao ___ V_*_,____,__ __ _ _______, _.-_ -.


c1ose() Fecha o cursor, liberando os recursos e a memoria.
int getCo1umnCount() Retorna o nmero de colunas da consulta.
int getCo1umnInde(String) Retorna o ndice da coluna para o nome de coluna
informado. O ndice comea em zero e -1 retornado
se a coluna no existir. O ndice da coluna posterior
mente utilizado para ler o valor da mesma, usando
os mtodos getString(), getInt(), getLong() etc.
int getCo1umnIndex0rThrow(String) Idem ao mtodo getCo1umnIndex(String),
mas se no encontrar a coluna, uma exceao
I1lega1ArgumentException lanada.
Strng[] getColumnNames Retorna um array de string com os nomes de co
lunas disponveis.
String getCoIumnName(ndce) Retorna o nome da coluna para o ndice especicado.
int getCount() Retoma o nmero de registros retornados pela con
sulta. Funciona de forma similar ao count(*) em SQL.
String getString(i) Mtodo usado para retornar o valor da coluna no
formato string, informando o ndice. Podemos ler
outros tipos de dados com os mtodos getInt(i.),
getF1oat(i), getDoub1e(), getShort() etc.
boolean moveToFrst() Posiciona o cursor para leitura no primeiro registro.
Mtodo frequentemente utilizado para descobrir se
foi retornado algum registro pela consulta, sendo
que retorna um valor booleano.
boolean moveToLast() Posiciona o cursor para leitura no ltimo registro.
boolean moveToNet() Posiciona o cursor para leitura no prximo registro.
Mtodo frequentemente utilizado para ler todos
os registros retornados em um loop. Retorna um
booleano informando se existe mais um registro
para ser lido.
booiean noveToPosition(pos) Posiciona o cursor para leitura na posio desejada.
booiean moveToPrevious() Posiciona o cursor para leitura na posio anterior.
Nem todos os mtodos esto listados aqui, apenas os principais. Consulte o
javadoc da classe Cursor para obter mais informaes. Lembre-se de chamar 0
mtodo c1ose() da classe Cursor para liberar os recursos do cursor quando ele n
for mais necessrio.
Captulo 18 I Persistncia 517
18.15 Continuando o projeto dos carros
Depois de estudar a API do banco de dados SQLite, vamos continuar 0 desen
volvimento do projeto dos carros. Nosso objetivo ser salvar todos os carros no
banco de dados logo depois de consultar a lista no web service.
Na verdade, j zemos essa lgica de cache, pois os arquivos JSON esto sendo
salvos na memria interna. O que vamos fazer agora simplesmente alterar o
cdigo para salvar e ler os carros no banco de dados, substituindo pela verso
que salva os arquivos.

Nota: depois de salvar os carros no banco de dados, vamos criar telas para editar
e excluir os carros localmente. Assim podemos brincar com os dados do banco,
e quando quisermos basta atualizar a lista e puxar os dados do web service.

O cdigo-fonte a seguir mostra como salvar e consultar os carros do banco de dados.

(arroService.java
public class CarroService {

public static List getCarros(Contet context, String tipo) throws IOException {


List carros = getCarrosFronBanco(context, tipo);
if (carros != null && carros.size() > 0) {
// Retorna os carros encontrados do banco
return carros;
}

// Se no encontrar, busca no web service


carros = getCarrosFronNebService(context, tipo);
return carros;

ublic static List getCarrosFromBanco(Context context, String tipo)


throws IOException {
CarroDB db = new CarroDB(context);
try {
List carros = db.ndAllByTipo(tip0)
L0g_d(TAG, "Retornando " + carros.size() + " carros [" + DO + "l do bC0");
return carros;
} nally {
db.close();
}

}
S18 Google Android - 4 Ed5
public static List getCarrosFromwebService(Contet context, String tipo)
throws IOException {
String url = URL.replace("{tipo}", tipo);
Log.d(TAG, "URL: " + url);
String json = HttpHelper.doGet(url);
List<Carro carros = parserJSON(contet, json);
// Depois de buscar salva os carros
salvarCarros(Context, tipo, carros);
return carros;
}

/I Salva os carros no banco de dados


private static void salvarCarros(Context context, String tipo, ListCarro> carros) {
CarroDB db = new CarroDB(context);
try {
// Deleta os carros antigos pelo tipo para limpar o banco
db.deleteCarrosByTipo(tipo);
// Salva todos os carros
for (Carro c : carros) {
c.tipo = tipo;
Log.d(TAG,"Salvando o carro " + c.none);
db.save(c);
}

} nally {
db.close();
}

Depois dessa alterao, execute o projeto e tudo dever continuar funcionando. Na


primeira execuo, o banco de dados ser criado e os carros sero salvos. Recomendo
voc depurar o cdigo para entender o funcionamento e tambm inserir alguns
logs caso voc ache necessrio. Um bom comeo voc olhar os logs do log(at, que
mostram as seguintes mensagens na primeira vez que o banco de dados criado:
D/sql? Criando a Tabela carro... // Aqui a classe CarroDB executou o SQL
D/sql? Tabela carro criada com sucesso.
D/CarroService? Retornando 0 (zero) carros [classicos] do banco
D/CarroService? URL: http://www.livroandroid.com.br/livro/c arros/carros_classicos.json
D/CarroService? Salvando o carro Tucker 1948
D/CarroService? Salvando o carro Chevrolet Corvette

D/CarroService? Carros salvos no banco


Captulo 18 I Persistncia 519
Veja que mostrei os logs apenas dos carros clssicos, mas como o aplicativo abre
as trs tabs, voc ver os logs de todos os carros. E caso voc feche o aplicativo e
abra novamente, os carros j estaro salvos no banco de dados, nesse caso, voc
ver os seguintes logs:
D/CarroServce:Retornando 10 carros [classicos] do banco.

No prximo tpico, vamos aprender a visualizar o banco de dados que est no


emulador.

18.16 Visualizando o banco de dados com a ferramenta adb


Para visualizar o banco de dados criado, vamos utilizar o emulador, pois no dis
positivo real por motivos de segurana no possvel acessar a pasta privada do
aplicativo. Depois de executar o projeto no emulador, o banco de dados foi criado
na pasta /data/data/bx comlivroandroid.carros/databases, conforme mostra a gura 18.4.
Isso signica que podemos copiar esse arquivo para o computador para abrir o
banco de dados em qualquer cliente de SQLite. Mas antes vamos demonstrar uma
tcnica bem interessante, a de conectar-se ao emulador com o comando adb shell.
Lembrando que o comando adb deve estar no PATH do sistema operacional, ou
voc deve estar na pasta /android-sdk/platform-tools/, que onde o adb est instalado.

17o '7oz- aK
2 r Q W @V
l lm il
.
'" Android Device Monitor

lNam
pl '15
l
j@,,,,,,,
-
. fzx:
a L
i Devifes 53 *Pia rs---l @ Hsfl .l9:~~..z.T*T.EsE;-_ le 23 .9F:-;ef 5Ytt;;_
lj =~ dk'__--_.''_2___@._
"kw 2015-02-O2 10245 l
1l

Mame 2 _,
Android ,W _%z.@.data
[emulator-5554] 2015-01-26
, app 2915020216:18
1

15;43 dr
d,
. c<m.android.systemui ,_app_aSec 2915-01-25 16318 drw:
<;om.android.iauncher
corn.google.android.gms.u ,jj
:_app_b 2015-O1-26
' 157699-private 16:18
2015-01-26 16:18drw;
dnm
com.andrcid_deskclock , Ebackup 2015-02-OZ 15:47 drw;
:om.android.keychain Q bugreports 2015-01-26 16:18 lrwx
comandroid.inputmethod.iatin fj , 5daVk_Cache 2015-01-26 16:18 drw:
com_google.process.gaDPS Yrzfzdata 2015-02-02 15:48 drw:
mm-99le~a"drld'9m5 ' 1/3br.com.livroandroidcarros 2015'02'02 10159 TWJ
com.android.defcontainer ju , ;_app_webVew 2015-02-02 10:50 drw:
com.google.process.location Ecache 2015-01-28 22:47 drw;
l androd.process.meda vgdatabaseg 2015-02-02 10 46 drw:
l Svsemyfves 'i ;llVff);1'1'd.SQl{E 4sos ams-ozoz 1o:4
j: com.android.se|er.telecom
android.process.acore r gles 2015-O2-02
A i Vm_am_|,-Qd_5qzejgumal 11:0510:46
16928 2015-0202 drw:
-rw
comgoogie.android.gms.unstable b 2015~01~28 22:47 lrwx
|1

` com.android.phone ` , shafedjfefs 2015-02-02 10250 Clfw


com.google_android.gms.warble J- _f___g_'_f __/_ _ _ __`.__v _/__ __ 701'-01-75 1543 fE\^._

Figura 18.4 -Arquivo do banco de dados.


520 Google Android - 4 edio
Dica: no Android Studio, pa ra visualizar o local onde o SDK est instalado utilize
o menu File > Project Structure > SDK Location.

Abra um terminal e digite este comando para se conectar ao emulador:


adb shell

Na sequncia, digite o comando sqlite3 local_do_arquivo_do_banco_de_dados.


sqlite3 /data/data/br.com.livroandroid.carros/databases/livro_android.sqlite
Se a conexo for bem-sucedida, o terminal do SQLite estar aberto aguardando
um comando SQL.

sqlite>
J Cl
SQLite version 3.8.6 2014-08-15 11:46:33
Enter ".help" for usage hints.

Para testar, fa a um SELECT na tabela dos carros. Ve`a ue deixei a enas trs linhas
para economizar espao no livro, mas 0 SELECT retorna todos os carros da tabela.
sqlite> select _id,nome,tipo from carro;
1|Bugatti Veyronlluxo
2|Ferrari Enzolluxo
3|Lamborghini Reventonlluxo

Depois de consultar o banco de dados, digite ".exit" para fechar o prompt do


SQLite. Ento, digite "eit" novamente para fechar a conexo com o emulador, a
qual foi aberta com o comando adb shell.

18.17 Visualizando o banco de dados com um cliente SQLite


Caso voc prera utilizarlalgum software cliente de SQLite para visualizar o
contedo do banco de dados, abra a janela File Explorer e copie o arquivo do banco
de dados para seu computador; para isso, utilize o boto Pulla File from the device.

Para abrir o arquivo, podemos utilizar qualquer software cliente do SQLite, como por
exemplo o SQLite Expert Personal, mas eu costumo utilizar o simples e prtico plugin SQLite
Manager para o Firefox. A figura 18.5 mostra a consulta select * from carro e o resultado.
Captulo 18 I Persistncia

z
. .l z -.row
ea rm; - -7- - f'^' --W '
SQLite Manager - R:\temp\llvro_androd_carros.sqlite

i Directory > (Select Profile Database) Vl Go l


structure l Browse sz seareh Ee<urz sol l os serringsl

1 Enter SQL E

SELECT * FROM carro

p Sun 501- Actions ' Last Erron ,not an er or


i Jd nome desc urljoto uri_nto url_video latitude longitude tipo
ll Bugatti Veyron O carro custa US http://www.livro... http://www.buga... http://www.livroi -23564224 -46653156 luxo
l2 Ferrari Enzo Preo de US 5 1 ,... http://www.livro... http://www.lerra... http://www.l|vroi 23.S64224 -46.6S3156 luxo
l Lamborghini Rev... Este carro de lux... httpy/www.livro... http://www.lamb... http://www.l|vroi -23.564.224 -46.659.156 luxo
14 Leblanc Mirabeau Leblanc, a empre... http://www.Iivro... http://en.wikiped... http://www.livroi -23564224 -46653156 luxo
ls Shelby Supercars... Este carro vem C... http;//www.livro... http://www.shelb... http://www.livroi -23564224 -46553156 luxo
l Pagani Zonda Pagani Zonda t... http://www.livro... http://www.paga... http://www.|ivroi -23564224 4~6.6S3156 luxo
7 Koenigsegg CCX Este carro elega.. http://www.livro... http://www.l:oen... http://www.Iivroi... -23564224 -46553156 luxo
,8 Mercedes SLR M... lsto pode no se. http://www.livro... http://www.mefc... http://www.livroi -23564224 -46653156 luxo
t9 Rolls Royce Phan... O carro diferen... http://www.livro... http://www.roIls-_.. http://www.livroi -23564224 -46553156 luxo
t l10 Lexus LFA Esta besta bra .. http]/www.livro... http://www.Ieus... http://www.livror -23564224 -46653156 luxo
l11 Ferrari FF AFerrari FF acab. http://www.livro... http://www.ferra... http://www.Iivroi 44532218 10364019 esportivos
l l12 AUDI GT Spyder Omais novo mo... http;//www.livro... httpy/www.audi .... http://www.livro -22951285 -43211262 esportivos
~13 Porsche Panamera OPorsche Pana... http;//www.|ivro... http://wvvw.pors... http://www.livroi -23564224 -46553156 esportivos
l 114 Lamborghini Ave... Outro modelo q... http://www.livro... http://www.aven... http://www.livroi ~23,S64224 -46653156 esportivos
l 1S Chevrolet Corvet... Chevrolet Con/et. http://www.livro... http://wwwchevr... http://www.livroi -23564224 -46653156 esportivos
'16 BMW M5 BMW M5 um http://www.|ivro... http://www.bmw... httpy/www.lvroi ~23.564224 -46653156 esportivos
E i17 Renault Megane ARenault aprese... httpy/www.livro... http://wwwjnfo... htlir//www.lvroi -23564224 -46653156 esportivos
`18 Maserati Granca... Avariante mais http://www.livro... http://www.mase... httpd/www.lvroi... -23564224 -46553156 esportivos
lt 19 MCLAREN MP4-1... Criada para se to http://www.livro... http://www.mcla... http://www.Iivroi -23564224 -46553156 esportivos
l 20 MERCEDES-BEN... Aalem Merced. httpy/www.lvro http://'www.merc... http://www.livroi -8564224 -46653156 esportivos

l, .21 Tucker1948 OTucler foi real... http://www.livro http://hotrodeku... http://www.livroi... -23564224 46.653156 classicos
22 Chevrolet Corvette OChevrolet corv... http://www,livro http://wwwchevr... http://www.lrvroi -23564224 46.653156 classicos
l

l 23 Chevrolet lmpala... O Impala foi lan... http://www.livro... http://ertwikiped http://www.||woi -23564224 -46553156 classicos
i

l Z4 Cadillac Deville OCadillac 1968 http://www.livro... http:/enlvvilcigd... _-46658156 classicos


L.

Figura 18.5 - Visualizando a tabela carro.

18.18 Banco de dados versus web service


Como a classe CarroService est salvando e depois buscando os carros do banco de
dados, precisamos ter uma maneira de atualizar os dados, para buscar novamente
do Web service.
Para solucionar esse problema, vamos adicionar um parmetro boolean refresh no
mtodo CarroService . getCarros(contet, tipo, refresh). Por padro, sempre vamos passar
ref resh=false para retornar os carros do banco de dados (caso este]am salvos). Mas se 0
usurio atualizar a lista de carros com o gesto Pullto Refresh, vamos passar o parametro
ref resh=true para forar a busca no Web service, com o objetivo de atualizar os dados.

CarroService.java
public class CarroService {

public static List getCarros(Contet context, String tipo, boolean refresh)


throws IOException {
S22 Google Android - 4 edio

List carros = null;


boolean buscaNoBancoDeDados = !refresh;
if (buscaNoBancoDeDados) {
// Busca no banco de dados
carros = getCarrosFronBanco(context, tipo);
if (carros != null && carros.size() > 0) {
// Retorna os carros encontrados do banco
return carros;
}

// Se no encontrar, busca no web service


carros = getCarrosFromHebService(context, tipo);
return carros;
}

Na classe CarrosFragrnent, o mtodo taskCarros(boolean) j recebe um parmetro


booleano indicando se o mtodo foi chamado pelo gesto de Pullto Refresh ou no.
private void taskCarros(boolean pullToRefresh) {
startTask("carros", new GetCarrosTask(), pullToRefresh
? R.id.swipeToRefresh : R.id.progress);
}

Portanto, a nica coisa que precisamos fazer repassar esse parmetro booleano
at a classe CarroService, para que l internamente seja possvel decidir se a busca
deve ser feita no banco de dados ou no web service.

CarrosFragment.java

public class CarrosFragment extends BaseFragment {

private void taskCarros(boolean pullToRefresh) {

startTask("carros", new GetCarrosTask(pullToRefresh),


pullToRefresh ? R.id.swipeToRefresh : R.id.progress)
}

private class GetCarrosTask implements TaskListener<List> {


private boolean refresh;
public GetCarrosTask(boolean refresh) {
this.refresh = refresh;
}
Captulo 18 I Persistncia 523
@0verride
public List execute() throws Exception {
// Passa o ag refresh
return CarroService.getCarros(getContext(), tipo, refresh);
}

Pronto, depois de atualizar o cdigo dessa forma, por padro todos os carros se
ro buscados do banco de dados. Mas ao fazer o gesto Pull to Refresh os carros sero
buscados do web service e a tabela dos carros ser atualizada com esses registros.
Ao atualizar os dados, voc ver os seguintes logs no LogCat:
D/CarroService:URL: http://www.livroandroid.com.br/livro/carros/carros_classicos.json
D/sql:Deletou [10] registros
D/CarroService: Salvando o carro Tucker 1948 // Aqui vai salvar os outros carros tambm.

18.19 Adicionando aes na action bar


Um dos objetivos de ter salvado os carros no banco de dados, porque vamos
criar telas para editar e excluir os registros. Assim, podemos brincar localmente
com os dados, pois podemos restaurar a lista original a qualquer momento.
Crie o seguinte arquivo de menu, que j contm todas as aes que vamos utilizar
durante o desenvolvimento do projeto.

/res/menu/menu_frag_carro.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<iten android:id="@+id/action_video"
android:icon="@drawable/ic_action_video" android:title="Video"
app:showAsAction="always" />
<item android:id="@+id/action_maps"
android:icon:"@drawable/ic_action_map" android:title="Maps"
app:showAsAction="always" />
<item android:id="@+id/action_share"
android:icon="@drawable/ic_action_share" android:title="Share"
app:showAsAction="ifRoom" />
<iten android:id="@+id/action_edit"
android:icon="@android:drawable/ic_menu_edit" android:title="Editar"
app:showAsAction="ifRoom" />
524 Google Android - 4' edio
item android:id="@+id/action_remove"
android:icon="@android:drawable/ic_menu_delete" android:title="Remover"
app:showAsAction=ifRoom" /

Voc vai precisar de algumas imagens, que esto disponveis nos projetos de exem
plo no GitHub do livro. No cdigo da classe, basta inflar o menu do fragment e
tratar os eventos. Por enquanto, vamos mostrar um simples toast ao selecionar as
opes. No se esquea de chamar o mtodo setHas0ptionsMenu(true).

Carrofragmentjava

public class CarroFragment extends BaseFragment {


private Carro carro;
@0verride
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_carro, container, false);
setHas0ptionsHenu(true); // Precisamos informar o Android que este fragment contm menu
return view;
}

@Override
public void onCreate0ptionsMenu(Menu menu, Menulnater inater) {
super.onCreate0ptionsMenu(menu, inater);
inater.inate(R.menu.menu_frag_carro, menu);
}

@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_edit) {
toast("Editar: " + carro.nome);
return true;
} else if (item.getItemId() == R.id.action_remove) {
toast("Deletar: " + carro.nome);
return true;
} else if (item.getItemId() == R.id.action share) {
toast("Compartilhar");
} else if (item.getItemId() == R.id.action_maps) {
toast("Mapa");
} else if (item.getItemId() == R.id.action_video) {
toast("Video");
}A
Captulo 18 I Persistncia 525
return super.onOptonsItemSelected(tem);
}

A gura 18.6 mostra o resultado com as aes para visualizar o Vdeo e Mapa, assim
como as opes Share, Editar e Remover no menu action overow

Share

Editar

Remover

A Ferrari FF acaba de ser revelada. Setrata A Ferrari FF acaba de ser revelada. Se trata
do primeiro modelo da marca a ter trao do primeiro modelo da marca a ter trao
integral. Alm disso, ele conta com um integral. Alm disso, eh conta com um
motor dianteiro V12. Se trata de um modelo motor dianteiro V12. Se trata de um modelo
GT de quatro lugares que no s substitui a GT de quatro lugares que no s substitui a
612 mas tambm atrai um novo tipo de 612 mas tambm atrai um novo tipo de
cliente, daquele que gosta de percorrer cliente, daquele que gosta de percorrer
caminhos mais dificeis que exigem trao caminhos mais d iflceis que exigem trao
integ ra li Este modelo revolucionrio (dentro integral. Este modeo revolucionrio (dentro
da marca) tem um novo chassi com entre da marca) tem um novo chassi com entre
eixos maion alm de suspenso eixos maior, alm de suspenso
independente que incorpora a ltima independente que incorpora a ltima
gerao de amortecedores aju stveis, alm gerao de amortecedores austveis, alm
de freios de cer mica da Brembo. de freios de cermica da Brembo.

Figura 18.6 - Menus da action bar

Nota quando eu me rero a adicionar aes na action bar, lembre se de que


na prtica estamos utilizando a Toolbar. O termo action bar virou um padro
de navegao to famoso que continuamos usando ele, mesmo que na prtica
a Toolbar seja utilizada. A Toolbar na verdade s uma view que permite criar o
efeito da action bar, porm com mais exibilidade.

18.20 Editando o nome do carro


Quando o usurio selecionar a opo Editar no menu, vamos mostrar um alerta
com um FragmentDtalog para editar o nome do carro. A seguir, podemos ver o layout
desse FragmentDalog.

/res/layout/fragment_editar_carro.mI
<LinearLayout xmlns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_width="match_parent" android:layout_heght="match_parent"
526 Google Android - 4 edio

android:padding="16dp" android:orientation="vertical">
<TetView android:tet="Nome"
android:layout_width="match_parent" android:layout_height="wFD_Cte"t" />
<EditTet android:id="@+id/tNome"
android:layout_width="match_parent" android:layout_height="wrD_C"tetH
android:layout_margin="8dp"/>
<Button android:id="@+id/btAtualizar"
android:layout_width="wrap_content" android:layout_height="WFP_C"t@"t"
android:tet="Atualizar" android:layout_gravity="right" />

Quando zemos o alerta de Sobre o aplicativo, j aprendemos a utilizar a classe


FragmentDialog. Naquele momento, implementamos 0 mtodo onCreateDialog(Bundle)
para criar um alerta customizado utilizando o AlertDialog. Dessa vez, como pre
cisamos de um layout customizado, vamos codicar 0 mtodo onCreateView() do
fragment normalmente, como faramos com qualquer outro fragment.

EditarCarroDiaIog.java
package br.com.livroandroid.carros.fragments;

import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.Fragmentanager;
import android.support.v4.app.FragmentTransaction;
public class EditarCarroDialog extends DialogFragment {
private Callback callback;
private Carro carro;
private Textview tNome;
// Interface para retornar o resultado
public static interface Callback {
public void onCarroUpdated(Carro carro);
}

// Mtodo utilitrio para criar o dialog


public static void show(FragmentManager fm, Carro carro Callback callback) {
FragmentTransaction ft = fm.beginTransaction();'
Fragment prev = fm.ndFragmentByTag("editar_carro");
if (prev != null) {
ft.remove(prev);
}

ft.addToBackStack(null);
EditafCarroDialog frag = new EditarCarroDialog();
frag.callback = callback;
Captulo 18 u Persistncia

Bundle args = new Bundle();


// Passa o objeto carro por parmetro
args.putSerializable("carro",carro);
frag.setArguments(args);
frag.show(ft, "editar_carro");
}

@0verride
public void onStart() {
super.onStart();
if (getDialog() == null) {
return;
}

// Atualiza o tamanho do dialog


int width = getResources().getDinensionPielSize(R.dinen.popup_width);
int height = getResources().getDinensionPielSize(R.dimen.popup_height);
getDialog().getwindow().setLayout(width, height);
}

@0verride
public View onCreateView(Layoutlnater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_editar_carro, container, false)
view.ndViewById(R.id.btAtualizar).set0nClickListener(onClickAtualizar());
tNome = (Textview) view.ndViewById(R.id.tNome);
this.carro = (Carro) getArguments().getSerializable("carro");
if (carro != null) {
tNome.setTet(carro.nome);

return view;
}

private View.OnClickListener onClickAtualizar() {


return new View.0nClickListener() {
@0verride
public void onClick(View view) {
String novoNome = tNome.getTet().toString();
if (novoNone == null II novoNome.trin().length() == 0) {
tNone.setError("Informe o none");
return;
}
Context context = view.getContet();
// Atualiza o banco de dados
carro.none = novoNone;
CarroDB db = new CarroDB(context);
db.save(carro);
528 Google Android - 4= edio
if(callback != null) {
callback.onCarroUpdated(carro);
}
// Fecha o DialogFragment
dismiss();
}

};
}

Lembre-se de importar a classe android.support.v4.app.DialogFragnent da biblioteca


de compatibilidade.
Note que no mtodo onStart() do fragment estou congurando o tamanho do
alerta com o seguinte cdigo:
int width = getResources().getDinensionPielSize(R.dimen.popup_width);
int height = getResources().getDimensionPixelSize(R.dimen.popup_height);
getDialog().getwindow().setLayout(width, height);

Portanto, adicione estas dimenses no projeto:

/res/values/dimens.xmI
<I'SOUI`CES>

<dimen name="popup_width">300dp
<dimen nane="popup_height">300dp

Para utilizar o DialogFragment que criamos simples, basta chamar o mtodo uti
litrio show() que encapsula o cdigo para criar o fragment. O mais importante
que voc precisa entender que estamos passando como parmetro um objeto
do tipo EditarCarroDialog.Callback, que a interface de retorno (callback) que criei
dentro desse fragment. Essa uma prtica muito comum na Orientao a Objetos
quando um objeto precisa retornar dados para outro. Essas interfaces fazem o meio
de campo entre os objetos. Para continuar, altere o cdigo da classe CarroFragment
conforme demonstrado a seguir: '
) (arroFragment.java

public boolean onOptionsItemSelected(Menultem item) {


if (item.getItemId() == R.id.action_edit) {
//toast("Editar: " + carro.nome);
CaPtuIo 18 u Persistncia 529
EditarCarroDialog.show(getFragmentManager(), carro, new EditarCarroDialog.Callback() {
@0verride
public void onCarroUpdated(Carro carro) {
toast("Carro [" + carro.nome + "] atualizado");
// Atualiza o titulo com o novo nome
getActionBar().setTitle(carro.nome);
}
});
return true' 9

Observe que internamente no cdigo da classe EditarCarroDialog os dados do


carro estao sendo salvos, e depois disso o resultado entregue pelo mtodo
onCarroUpdated(carro) da interface de callback.

// EditarCarroDialog.java
CarroDB db = new CarroDB(context);
db.save(carro);
if(callback != null) {
callback.onCarroUpdated(carro);
}

A gura 18.7 mostra o alerta com o formulrio para editar o nome do carro e depois
o toast que mostrado quando o usurio clica no boto Atualizar. No cdigo inclu
sive estamos atualizando o ttulo da action bar para reetir o novo nome do carro.

Nome

Ferrari FF Testel A Ferrari FF acaba de ser revelada. Se trata


i i " i l do primeiro modelo da marca a ter trao
integral. Alm disso, ele conta com um
ATUALIZAR motor dianteiro V1 2. Se trata de um modelo
d t l no s substitui a
GT e qua ro ugares que
612 mas tambm atrai um novo tipo de
cliente, daquele que gosta de percorrer
caminhos mais dificeis que exigem trao
int ral Este modelo revolucionario dentro

Carro [Ferrari FF Teste] atualizado. 1

gerao de amortecedores ajustveis, alm


de freios de cermica da Brembo.

Figura 18.7 - Editando um carro.


530 Google Android - 4 edio
Problema: depois de atualizar o nome do carro e voltar para a lista, vOC Vf8
que o nome antigo ainda est l. Isso porque precisamos ler novamente os dados
para refletir a alterao que zemos no banco de dados. Ento, para ter CFfZ
de que os dados foram atualizados no banco de dados, basta VOC fCh3f 0
aplicativo e abrir novamente. Vamos solucionar esse problema logo, mas atS
vamos implementar a ao de deletar.

18.21 Excluindo um carro do banco de dados


Para excluir um carro do banco de dados, vamos mostrar uma alerta para 0 usu
rio conrmar a excluso com as opes Sim e No.
Um alerta de conrmao pode ser criado com o seguinte cdigo:
Dialoglnterface.0nClickListener dialogClickListener = new Dialoglnterface.
0nClickListener() {
@0verride
public void onClick(DialogInterface dialog, int which) {
switch (which){
case Dialoglnterface.BUTTON_POSITIVE:
// Clicou em Sim
break;
case Dialoglnterface.BUTTON_NEGATIVE:
// Clicou em No
break;
}

};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Deletar esse carro?");
builder.setPositiveButton("Sim", dialogClickListener);
builder.setNegativeButton("No", dialogClickListener);
builder.show();

Particularmente, no gosto de deixar cdigos grandes assim espalhados na activity


ou no fragment, pois isso polui o visual do cdigo e diculta a manuteno.
Portanto, novamente vamos encapsular esse alerta em um Fragmentialog. No m
todo onCreateDialog(Bundle), vou utilizar exatamente o trecho de c digo mostrado
anteriormente, e caso o usurio selecione a opo Sim vamos retornar o res ultado
pela interface de retorno (callback).
(aPtuIo 18 n Persistncia

DeIetarCarroDiaIog.java
package br.com.livroandroid.carros.fragnents;
public class DeletarCarroDialog extends DialogFragnent {
private Callback callback;
private Carro carro;
public static interface Callback {
public void onCarroDeleted(Carro carro);
}
public static void show(FragmentManager fm, Carro carro, Callback callback) {
FragnentTransaction ft = fn.beginTransaction();
Fragnent prev = fn.ndFragnentByTag("deletar_carro");
if (prev != null) {
ft.remove(prev);
}

ft.addToBackStack(null);
DeletarCarroDialog frag = new DeletarCarroDialog();
frag.callback = callback;
Bundle args = new Bundle();
args.putSerializable("carro",carro);
frag.setArguments(args);
frag.show(ft, "deletar_carro");
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.carro = (Carro) getArgunents().getSerializable("carro");
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialoglnterface.OnClickListener dialogClickListener = new
Dialoglnterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which){
case Dialoglnterface.BUTTON_POSITIVE:
// Deleta o carro
Carropg db = new CarroDB(getActivity());
db.delete(carro);
if(callback != null) {
callback.onCarroDeleted(carro);
}
break;
532 Google Android - 4 edio
case Dialoglnterface.BUTTON_NEGATIVE:
break;
}

1;
AlertDialog.Builder builder = new Alertialog.Builder(getActivity());
builder.setMessage("Deletar esse carro?");
builder.setPositiveButton("Sim", dialogClickListener);
builder.setNegativeButton("No", dialogClickListener);
return builder.create();
}

Na classe CarroFragment, ao selecionar a opo para excluir um carro, vamos mos


trar o alerta de conrmao. Caso o carro seja excludo, mostrado um toast, e
a activity encerrada com o mtodo nish().

CarroFragment.java

public boolean on0ptionsItemSelected(Menultem item) {

else if (item.getItemId() == R.id action_remove) {


DeletarCarroDialog.show(getFragmentManager(), carro, new DeletarCarroDialog.Callback() {
@Override
public void onCarroDeleted(Carro carro) {
toast("Carro [" + carro.none + "] deletado.");
// Fecha a activity
getActivity().nish();
}

});
return true;
}

A gura 18.8 mostra o alerta com a conrmaozpara excluir olcarro. Ao selecio


nar a opo Sim, o carro excludo e o aplicativo volta para a tela da lista. Porm ,
podemos ver que na listagem o carro excludo ainda e st l, pois essa lista precisa
ser atualizada para refletir o estado atual do banco de dados.
Captulo 18 I Persistncia 533

Ferrari FF Teste

AUD: er Spyder

Porsche Panamera

Carro [Ferrari FF Teste] deletado. l

Figura 18.8 - Excluindo 0 carro.

18.22 Atualizando a lista com dados do web service

At o momento, temos um problema no aplicativo, pois logo depois de excluir ou


editar um carro, ao voltar para a lista, os dados no esto atualizados. Inclusive
carros que foram excludos permanecem na lista.
Na verdade, isso acontece se voc clicar no boto de voltar do Android. Porm 7

se voc clicar no up navigation, a lista atualizada, pois nesse caso disparada


uma intent para a activity pai, fazendo com que ela seja executada novamente.
Mas vamos estudar mais detalhes sobre esse comportamento depois.
Para solucionar esse problema, temos de atualizar a lista de carros sempre que
zermos qualquer alterao na tabela do banco de dados. Existem vrias formas de
fazer isso, e eu vou fazer uma das mais simples, que utilizar uma varivel global
no aplicativo, que um ag booleano que indica se a lista deve ser atualizada.
Para isso, altere a classe CarrosApplication, que o singleton que controla a aplicao.

CarrosAppIication.java

public class CarrosApplication extends Application {


// HashMap onde a chave o tipo do carro e o boolean indica se precisa atualizar
private Map<String, Boolean> maDUDd3te = new Ha5hMD<5tF9B00l6>()5
534 Google Android - 4 edio
public void setPrecisaAtualizar(String tipo, boolean b) {
// Congura o tipo (clssico,esportivo,luxo) para atualizar a lista
this.napUpdate.put(tipo,b);
}

public boolean isPrecisaAtualizar(String tipo) {


if(mapUpdate.containsKey(tipo)) {
boolean b = mapUpdate.renove(tipo);
return b;
}
return false;
}

Nota: lembre-se de que, quando criamos o projeto dos carros, voc registrou a
classe CarrosApplication no AndroidManifest.xml.

Feito isso, nos mtodos onCarroUpdated(carro) e onCarroDeleted(carro), vamos atualizar


este flag para true, assim a lista de carros pode ler o valor desse flag para descobrir
se precisa ler os dados novamente. Note que, alm do flag booleano, informado
o tipo do carro (clssicos, esportivos ou luxo) para o fragment que controla a lista
de carros atualizar a lista do tipo correto.

CarroFragment.java

public void onCarroUpdated(Carro carro) {


toast("Carro [" + carro.nome + "] atualizado.");
CarrosApplication.getInstance().setPrecisaAtualizar(carro.tipo,true);
// Atualiza o titulo com o novo nome
getActionBar().setTitle(carro.none);
}

public void onCarroDeleted(Carro carro) {


toast("Carro [" + carro.nome + "] deletado.");
CarrosApplication.getInstance().setPrecisaAtualizar(carro.tipo,true);
// Fecha a activity
getActivity().nish();
}

Feito isso, no mtodo onResune() da classe CarrosFragnent, vamos testar se houve al


teraes no banco de dados, e, se for necessrio, vamos buscar os carros do banco
de dados novamente para atualizar a lista. f
Captulo 18 I Persistncia 535
CarrosFragment.java

public class CarrosFragment extends BaseFragment {


@0verride
public void onResume() {
super.onResume();
if(CarrosApplication.getInstance().isPrecisaAtualizar(this.tipo)) {
// Se teve alteraes no banco de dados, vamos atualizar a lista.
taskCarros(false);
toast("Lista de carros atualizada!");
}

O mtodo onResune() foi escolhido para fazer essa vericao, pois ele executa
do sempre que a activity exibida para o usurio. Consequentemente, todos os
fragments tambm recebem esse evento. Lembre-se de que a tela da lista de carros
tem trs tabs (clssicos, esportivos e luxo), portanto, esses trs fragments da classe
CarrosFragment vo executar o mtodo onResune(). Justamente por isso, passado o
tipo do carro para o mtodo isPrecisaAtualizar(tipo), para atualizar a lista correta.

18.23 Modo de execuo da activity "IaunchMode"


Ainda existe outro problema no aplicativo que simples de resolver, ento faa
comigo o seguinte teste. Role a lista de carros para baixo at o nal e selecione
o ltimo carro. Feito isso, a tela de detalhes do carro vai aparecer. Ao clicar no
boto voltar do Android, a activity simplesmente desempilhada, e a lista de
carros volta a aparecer com o seu estado intacto (na posio que estava), ou seja,
a lista est com a rolagem l no nal. Mas, se voc clicar no boto up navigation
(seta para esquerda na action bar), a rolagem da lista de carros volta para o incio,
perdendo o estado.
Isso acontece porque o padro de navegao do up navigation desempilhar todas
as activities at a raiz. Nesse caso, a activity de detalhes CarroActivity e a principal
MainAcivity so destrudas e uma nova MainActivity criada. Por isso, perde-se o
estado ao clicar no up navigation.
Isso acontece porque o modo de execuo da activity chamado de launchMode,
denido com standard por padro. Para alterar esse comportamento, congure o
atributo android:launchMode="singleTop" da MainActivity no AndroidManifest.xml.
536 Google Android - 4 edio
AndroidManifest.xmI
<manifest . . . /
<app1cation ... /
<activity
androd:nane=".activity.MainActvty" android:1aunchHode="sng1eToP" --- />

Feita essa configurao, ao voltar na hierarquia pelo up navigatiO, 8 IYISCI3


da mesma activity ser utilizada e no ser criada uma nova. Quando issoacon
tecer, ser chamado o mtodo onNewIntent(ntent) na ManActvity, assim a aplicao
pode tratar esse evento caso seja necessrio. Basicamente, o modo de execuo
standard cria uma nova activity sempre que for disparada uma nova intent com o
startActvity(ntent). Nesse caso, o mtodo onCreate(bund1e) sempre chamado na
nova activity O modo de execuo sng1eTop mantm apenas uma instncia da
activity viva e ao chamar o mtodo startActivty(intent) essa activity colocada
no topo da pilha e seu mtodo onNewIntent(ntent) chamado.
Este um assunto avanado, portanto, recomendo que voc complemente seus
estudos lendo a documentao oficial do Android quando achar necessrio. Vale
a pena procurar pelo termo FLAG_ACTIVITY_CLEAR_TOP, pois ele muito til, uma vez
que voc pode iniciar uma activity limpando o topo da pilha.
http://dcvelopexandroid.com/guide/components/tasks-and-back-stack. html

http://developerandroid.com/guide/topics/manifest/activity-element.htm1#lm0de

18.24 Fazendo backup na nuvem


Outra forma interessante de persistncia disponvel no Android a API de backup.
Com ela, podemos salvar pequenas informaes de forma transparente na nuvem
(cloud) do Google e restaur-las futuramente. Isso til nos casos em que o
os dados dos aplicativos. '
usurio troca de aparelho, pois, ao configurar o novo celular, possvel restaurar

Para fazer o backup dos dados, podemos utilizar a classe BackupAgent, na qual preci
samos implementar os mtodos onBackup() e onRestore(), responsveis por transferir
os dados do servidor para a nuvem e restaurar o backup posteriormente. Mas im
plementar essa tarefa no trivial e exige algumas linhas de cdigo. Por esse motivo
existe a classe BackupAgentHe1per, que lha de BackupAgent e faz todo esse trabalho.
conjunto com a classe BackupAgentHe1per, podemos utilizar outras duas classes para
Captulo 18 n Persistncia 537
automatizar o processo de backup. A primeira delas a SharedPreferencesBackupHelper,
capaz de fazer backup do que foi salvo nas preferncias do usurio, e a segunda a
FileBackupHelper, utilizada para fazer backup de arquivos. No aplicativo dos carros,
estamos salvando nas preferncias do usurio SharedPreferences a tab selecionada,
portanto vamos aprimorar o aplicativo e salvar isso na nuvem. Para comear a brin
cadeira, crie a classe EenploBackupAgent. Ateno para o pacote que voc vai utilizar,
pois ser necessrio congurar essa classe no AndroidManiest.xml.

ExempIoBackupAgent.java
package br.com.livroandroid.carros.backup;

public class EemploBackupAgent extends BackupAgentHelper {


@0verride
public void onCreate() {
// Cria um helper. Far backup dos dados utilizando a chave Prefs.PREF_ID.
SharedPreferencesBackupHelper helper = new
SharedPreferencesBackupHelper(this, Prefs.PREF_ID);
// Adiciona o helper ao agente de backups
addHelper("livroAndroid", helper);
}
@0verride
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
super.onBackup(oldState, data, newState);
Log.d("backup","Backup efetuado com sucesso.");
}

@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
super.onRestore(data, appVerS0C0d, HGWSGG);
Log.d("backup","Backup restaurado com sucesso.");
l
}

Essa classe precisa implementar apenas o mtodo onCreate() e adicionar quantos


helpers forem necessrios com o mtodo addHelper(helper). Neste caso, estamos
utilizando a classe SharedPreferencesBackupHelper a fim de copiar todos os dados
Salvos na preferngia do usurio para a nuvem do Google. Justamente por isso,
em seu construtor informada a chave das preferncias, que, neste caso, e a string
prefS,pREp_1D, Conforme podemos visualizar neste trecho de cdigo:
SharedpreferenceSBackupHe1per helpef z new SharedPreferencesBackupHelper(this, Prefs.PREF_ID);
533 Google Android - 4 edio
A string Prefs.PREF_ID utilizada, pois queremos fazer backup dos dados que foram
salvos com a classe Prefs. Internamente, se voc olhar o cdigo-fonte da classe Prefs,
ela salva todas as informaes utilizando essa chave "livroandrotd .
public class Prefs {
public static nal String PREF_ID = "livroandroid";
}

Uma vez que a classe que far o backup est criada, precisamos adicionar as tags
android:allowBackup e android:backupAgent ao arquivo AndroidManifest.xml.

A tag android:allowBackup recebe um booleano indicando que o aplicativo faz


backup, portanto, neste caso, deve ser true.
A tag android:backupAgent recebe o nome da classe responsvel pelo backup.
Baseado nessas informaes, altere o arquivo AndroidManifest.xml conforme de
monstrado no cdigo a seguir:

AndroidManifest.xmI
<manifest . . . >
<application android:allowBackup="true"
android:backupAgent=".backup.ExemploBackupAgent" . . >

<meta-data android:name=con.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3RuVsUKmTxDy1Xr3YwRUdD0j9p-tEsebzArJZg" /

Observe que tambm adicionei uma tag , que declara a chave de au


tenticao do servidor de backup utilizado pelo Google:

<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3RuVsUKmTDy1Xr3YwRUdD0j9p-tEsebzArJZg" />

O valor dessa chave nico para sua aplicao e deve ser gerado nesta pgina:
https://developerandroid.com/google/backup/signup.html

Ao abrir a pgina, voc deve ler e aceitar a licena e preencher o campo com o
nome do pacote do seu projeto, que, neste caso, br.com.livroandroid.carros. O re
sultado disso que a chave necessria para a tag ser exibida na tela
e voc poder copiar o valor para seu projeto.
Captulo 18 I Persistncia 539
Pronto, estamos quase l! A congurao do backup j est feita, e agora falta
apenas ad1c1onarmos uma linha de cdigo para executar o backup sempre que
alguma informao for salva nas preferncias. Com esse objetivo, altere a classe
CarrosTabFragment desta forma:

CarrosTabFragment.java
public class CarrosTabFragment extends BaseFragment {
private Backupanager backupManager;

public View onCreateView(LayoutInater inater, Viewroup container,


Bundle savedInstanceState) {
// Gerenciador de backup
backupanager = new BackupManager(getContext());

mSlidingTabLayout.set0nPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// Salva o indice da pgina/tab selecionada
Prefs.setInteger(getContext(), "tabId", viewPager.getCurrentItem());
backupManager.dataChanged(); // Faz o backup
}

});
}

A classe responsvel por disparar o processo de backup a classe BackupManager,


por isso, logo depois de alterarmos a tab selecionada, estamos chamando o
mtodo dataChanged(). Feito isso, o Android vai disparar a classe de agente de
backup que est registrada no AndroidManifest.xml, portanto, o mtodo onBackup()
da classe EemploBackupAgent ser chamado. E felizmente, como utilizamos a classe
SharedPreferencesBackupHelper para fazer o backup das preferncias, ela j faz tudo
automaticamente.
Para testar o backup, execute o projeto mais uma vez e clique em alguma tab.
Como o aplicativo ainda no est publicado no Google Play precisamos executar um
comando para fazer o backup. Portanto, abra um prompt e digite os seguintes coman
dos. Lembrando que a ferramenta adb fica na pasta /android-studio/sdk/platorm-tools.
540 Google Android - 4 edio
adb shell bmgr enable true
Backup Manager now enabled
adb shell bmgr backup br.com.llvroandroid.carros
adb shell bmgr run
adb unlnstall br.com.lvroandrod.carros
Success

Esses comandos disparam o backup no emulador e logo depois dSlr1SI8l8fI1 0


aplicativo, simulando que o backup foi realizado. Nos logs do LogCat, inclusive
voc poder ver algumas mensagens como essas:
V/BackupServceBnder? doBackup() nvoked
D/backup? Backup efetuado com sucesso.

E, para testar se a teoria funciona, execute o projeto novamente. Se tudo funcionar


conforme o esperado, voc ver que a tab selecionada ser aquela tab que voc salvou.
No momento da restaurao do backup, voc ver as seguintes mensagens no LogCat:
V/BackupServceBnder? doRestore() nvoked
D/backup? Backup restaurado com sucesso.

Nota: caso o aplicativo seja instalado pelo Google Play, naturalmente no


necessrio executar esses comandos para efetuar o backup. Fizemos isso porque
estamos no ambiente de desenvolvimento.

18.25 Fazendo backup de um arquivo


No exemplo anterior, fizemos o backup das preferncias do usurio, mas, se qui
sermos, tambm podemos fazer backup de arquivos. importante ressaltar que,
devido a possveis restries de velocidade de conexo, recomendado no fazer
backup de arquivos grandes.
Como anteriormente no projeto dos carros, salvamos os arquivos JSON na me
mria interna; se quisermos, possvel fazer o backup desses arquivos.
/data/data/bn com.l ivroand roid. ca rros/ les/ca rros;classi cos. json

O cdigo a seguir mostra como fazer backup dc um arquivo que est salvo na
memria interna. Note que utilizada a classe FleBackupHelper.
Captulo 18 I Persistncia 541
ExempIoBackupAgent.java
public class EemploBackupAgent extends BackupAgentHelper {
@Override
public void onCreate() {

// Faz backup de um arquivo


FileBackupHelper le = new FileBackupHelper(this,"carros_classicos.json");
addHelper("livroAndroid-le", le);
}

A classe FileBackupHelper faz backup somente de arquivos salvos na memria interna.


Caso voc precise salvar um arquivo que est no SD card ou qualquer outra infor
mao, crie sua prpria classe agente de backup e implemente os mtodos onBackup()
e onRestore(). Se isso for necessrio, consulte a documentao ocial do Android.
Lembrando que, se essa soluo no lhe servir por algum motivo, existem diver
sos servios e APIs que podem ser utilizados para fazer backup de dados, como
Google Drive, Amazon S3, Dropbox etc.

18.26 Links teis

Neste captulo, estudamos mecanismos de persistncia do Android. Para continuar


seus estudos, separei alguns links interessantes.
Android Training - Saving Key-Value Sets

http://developer android.com/training/basics/data-storage/shared-preerences. html


Android API Guides - Settings

http://developer android.com/guide/topics/ui/settings.html
Android Training -Saving Files

http://developer android.com/training/basics/data-storage/les.html
Android Training -Saving Data in SQL Databases

http://developer android.com/training/basics/data-storage/databases.html
Android Training - Backup API

http://developer android.com/training/cloudsync/backupapi.html
Android Developers - Explicao sobre o atributo IaunchMode=singIeTop

http;//developer android.com/guide/components/tasks-and-back-stack.html
1 ` cAPruLo 19
Action bar de contexto e
`'. compartilhamento

Neste captulo, vamos aprender a utilizar a contextual action bar, mas vamos chama
-la apenas de action bar de contexto ou simplesmente CAB.
Tambm vamos praticar a ao de compartilhamento para enviar informaes
como texto ou fotos para outras aplicaes.

19.1 Introduo
Geralmente, em telas de listagem, o usurio est acostumado a tocar em uma
linha e segurar por mais ou menos dois segundos. Feito isso, o sistema geralmente
mostra algumas opes com um menu utuante ou s vezes at pode criar uma
action bar de contexto.
A gura 19.1, extrada da documentao ocial do Android, mostra duas opes
comuns ao tocar e segurar na lista por mais de dois segundos. No lado esquerdo,
temos o Floating Context Menu e na direita a Contextual Action Bar (CAB).
Criar a primeira opo relativamente simples, e tenho certeza de que, se voc
olhar a documentao ocial, vai conseguir fazer. Mas criar a action bar de con
texto requer um pouco mais de trabalho, por isso vamos estud-la. A action bar
de contexto (CAB) muito utilizada em diversos aplicativos nativos do Android.
Com certeza, ao utiliz-la, deixaremos nossa aplicao mais renada, e o usurio
vai gostar, uma vez que esse um comportamento com o qual ele est acostumado
e espera que seu aplicativo comporte-se da mesma maneira. PorO,eO*1ITl8l
xemp l C 'l
permite selecionar vrias mensagens ao mesmo tempo para exclui-las, e a Galeria
de Fotos permite selecionar vrias fotos para compartilh-las. Vamos implementar
exatamente essa funcionalidade no aplicativo dos carros , para permitir que vrios
carros possam ser excludos ou compartilhados.

542
Captulo 19 I Action bar de contexto e compartilhamento 543

aaaa f
Henry IV (1)

Henry V

Henry Viii

Richard Ill

Merchanl of Venice

Omdm

King Lear

Figura 19.1 - Opes de menu.

19.2 Detectando toques longos - 0nLongCIickListener


Para comear a brincadeira, precisamos detectar o toque longo na view do adapter,
e isso feito com o mtodo set0nLongClickListener(listener). Ento vamos l, atualize
o cdigo da classe CarroAdapter conforme demonstrado a seguir:

CarroAdapter.java
public class CarroAdapter extends Recyclerview.Adapter {
@Override
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) {

// Click normal
if (carroOnClickListener != null) {
holder.itemview.setOnClickListener(...);
}

// Click longo
holder.itemView.set0nLongClickListener(new View.0nLongClickListener() {
@0verride
public boolean onLongClick(View v) {
carroOnClickListener.onLongClickCarro(holder.itemView, position);
I`EtUI'I`\ true;
544 Google Android - 4' edio
});
}

public interface Carro0nClickListener {


public void onClickCarro(View view, int idx);
public void onLongClickCarro(View view, int idx);
}

Basicamente o que zemos foi detectar o toque longo na view do adapter e chamar o
mtodo onLongClickCarro(View,int) da interface Carro0nClickListener. Lembrando que essa
interface implementada na classe CarrosFragment e por meio dela o fragment recebe
os eventos de clique feitos no adapter. Novamente essa a interface de callback que
j utilizamos em outros exemplos, mas aqui chamei de listener, pois outro nome
comum que esse tipo de interface recebe. Para terminar essa congurao, atualize o
cdigo da classe CarrosFragment para implementar o mtodo onLongClickCarro(View,idx).

a (arrosFragment.java

private CarroAdapter.CarroOnClickListener onClickCarro() {


return new CarroAdapter.Carro0nClickListener() {
@0verride
public void onClickCarro(View view, int idx) { . . . }
@0verride
public void onLongClickCarro(View view, int idx) {
Carro c = carros.get(idx);
toast("Clicou e segurou: " + c.nome);
}

};
}

A gura 19.2 mostra o resultado ao tocar em um carro e segurar por dois segundos.

19.3 Ativando o ActionMode na action bar

Quando o usurio tocar em algum carro da lista e segurar por alguns segundos 7

vamos habilitar a action bar de contexto para mostrar as opes para zxluir ou
compartilhar vrios carros.

Nesse momento, ser possvel selecionar vrios carros ao mesmo tempo conforme
7

mostra a Figura 193. Na figura tambm podems ver que foi feito o cmpqr]h,_
mento dos carros selecionados, e os nomes foram passados por 9MS
Captulo 19 I Action bar de contexto e compartilhamento 545

l trrarr FF

l r.i
Porsche Panarr 1

Lamborghim Av nt do

mmol e setwotz .=JDl GT Sitwdez

H ' L _.
Figura 19.2 - OnLongClickListener na lista.

vl ,;j;'_
~ -lmpala Coupe}] _
l
Carros: [Carro{nome='Tucker
1948}, Carro{nome='Chevrolet
- Corvette}, Carro{nome='Chevrolet
l

l |Type message
~ -- Cadilac Deville L; i
Lq-5; f' Convertible
, _ _ , - _ __ ,mw ._ .,__ , ,,. _ ._ .,..._...-.

Figura 19.3 - Action bar de contexto (CAB).


Ao selecionar os carros na lista, precisamos de um flag para controlar se o carro
est selecionado ou no. Se estiver, vamos pintar a linha da lista de azul ou outra
cor. Para criar o ag, altere a classe Carro conforme mostra este cdigo:
S46 Google Android - 4= edio

-:E (arro.java

public class Carro inplenents Serializable {

public boolean selected; // Flag para indicar que o carro est selecionado
}

Para pintar a linha de azul caso o carro esteja selecionado, altere o cdigo do
adapter:

CarroAdapter.java
public class CarroAdapter extends Recyclerview.Adapter {
@0verride
public void onBindViewHolder(nal CarrosViewHolder holder, nal int position) [

// Pinta o fundo de azul se a linha estiver selecionada


int corFundo = context.getResources().getColor(c.selected
? R.color.prinary : R.color.white);
holder.cardview.setCardBackgroundColor(corFundo);
// A cor do texto branca ou azul, depende da cor do fundo.
int corFonte = context.getResources().getColor(c.selected
? R.color.white : R.color.primary);
holder.tNone.setTextColor(corFonte);
}

j temos o cdigo que vai pintar a linha selecionada de azul, ento chegou o
momento de ativar a action bar de contexto para permitir selecionar os carros.
Com esse propsito, podemos utilizar o mtodo startActionMode(callback) da classe
android.app.Activity, mas como estamos utilizando a biblioteca de compatibilidade
v7, vamos utilizar os mtodos com o prexo support.

CarrosFragment.java

import android.support.v7.view.ActionMode;
public class CarrosFragment extends BaseFragment {
private Actionode actionode;

private CarroAdapter.CarroOnClickListener onClickCarro() {


return new CarroAdapter.CarroOnClickListener() {
Captulo 19 n Action bar de contexto e compartilhamento

@0verride
public void onClickCarro(View view, int idx) { . . . }
@0verride
public void onLongClickCarro(View view, int idx) {
if (actionMode != null) { return; }
// Liga a action bar de contexto (CAB)
actionMode = getAppCompatActivity().
startSupportActionMode(getActionModeCallback());
Carro c = carros.get(idx);
c.selected = true; // Seleciona o carro
// Solicita ao Android para desenhar a lista novamente
recyclerview.getAdapter().notifyDataSetChanged();
// Atualiza o titulo para mostrar a quantidade de carros selecionados
updateActionModeTitle();
}

};
}

// Atualiza o titulo da action bar (CAB)


private void updateActionModeTitle() {
if (actionMode != null) {
actionMode.setTitle("Selecione os carros.");
actionMode.setSubtitle(null);
List selectedCarros = getSelectedCarros();
if (selectedCarros.size() == 1) {
actionMode.setSubtitle("1 carro selecionado");
} else if (selectedCarros.size() > 1) {
actionMode.setSubtitle(selectedCarros.size() + " carros SeleC0d0S");
}

// Retorna a lista de carros selecionados


private List getSelectedCarros() {
List list = new ArrayList();
for (Carro c : carros) {
if (c.selected) { list.add(c); }
}
return list;
}

}
543 Google Android - 4' edio
Dica: o mtodo notifyDataSetChanged() do adapter tem como objetivo iflvlldf 3
lista, forando o Android a redesenhar a lista. Isso utilizado para rClSh3f
a view quando voc adiciona ou remove itens da lista, ou deseja alterar 3 COI' do
carro selecionado, como o nosso caso.

Esta linha de cdigo inicia o modo de ao (action mode), que na prtica signica
que vamos mostrar a action bar de contexto (CAB).
actionMode = getAppCompatActivity().startSupportActionMode(getActionHodeCallback());

Observe que o objeto ActionMode retornado salvo como um atributo da classe,


pois precisamos saber se a action bar de contexto j foi ligada ou no.

Nota: nas prximas citaes, para simplicar o texto, vou chamar o termo action
bar de contexto apenas de CAB, pois a sigla para contextual action bar na
documentao ocial.

Mas para o cdigo compilar precisamos criar o mtodo getActionModeCallback(),


que vai retornar um objeto do tipo ActionMode.Callback, o qual contm os mtodos
para inar as opes de menu que a CAB vai ter, alm de tratar os eventos de
clique nestas aes.

CarrosFragment.java

public class CarrosFragment extends BaseFragment {

private ActionMode.Callback getActionHodeCallback() {


return new ActionMode.Callback() {
@Override

public boolean onCreateActionHode(Actionode mode, Menu menu) {


// Ina o menu especico da action bar de contexto (CAB)
Menulnater inater = getActivity().getMenuInater();
inater.inate(R.menu.menu_frag_carros_c0ntet, menu);
return true;
}

@0verride

public boolean onPrepareActionHode(ActionHode mode, Menu menu) {


return true;
}

@Override

public boolean onActionItemClicked(Actionode mode, Henultem item) {


Captulo 19 I Action bar de contexto e compartilhamento 549
List selectedCarros = getSelectedCarros();
if (item.getItemId() == R.id.action_remove) {
toast("Remover " + selectedCarros);
} else if (item.getItemId() == R.id.action_share) {
toast("Compartilhar: " + selectedCarros);
}

// Encerra o action mode


mode.nish();
return true;
}

@0verride

public void onDestroyActionMode(ActionMode mode) {


// Limpa o estado
actionMode = null;
// Congura todos os carros para no selecionados
for (Carro c : carros) {
c.selected = false;
}

recyclerView.getAdapter().notifyDataSetChanged();
}

};
}

Para o cdigo compilar, crie o arquivo de menu que a CAB vai inar.

/res/menu/menu_frag_carros_context.xmI
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share" android:icon="@drawable/ic_action_share"
android:title="Share" app:showAsAction="always" />
<item
android:id="@+id/action_renove" android:icon="@android:drawable/ic_menu_delete"
android:title="Remover" app:showAsAction="always" />

Tambm ser preciso adicionar mais duas propriedades no arquivo stylesxml, respon
svel por congurar o tema da aplicao. Congure o atributo windowActionMode0verlay
para true, a m de posicionar a CAB por cima da Toolbar. Voc pode testar o
aplicativo com e sem essa linha para ver a diferena. Outro atributo necessrio
o actionModeBackground, que dene a cor da CAB, e neste caso deixarei com a cor
primria do aplicativo.
550 Google Android - 4 edio
/res/values/styIes.xml

<sty1e name="AppTheme" parent="Thene.AppCompat.Light.NoActionBar'>

<iten name:"windowActionMode0ver1ay"true/iten>

iten name="actionHodeBackground">@co1or/prinary
/sty1e>

No se assuste com o tamanho do cdigo para criar o CAB, pois se voc olhar os m
todos da interface ActionMode.Ca11back eles so simples. O mtodo onCreateActionMode()
responsvel por inflar o menu da CAB. Neste menu, vamos mostrar as aes para
Compartilhar ou Remover os carros. O mtodo onPrepareActionMode() chamado sempre
que o menu da CAB invalidado, mas neste exemplo no vamos utiliz-lo. O
mtodo onActionItenC1icked() chamado ao clicar em algumas das opes da CAB
e funciona da mesma forma que o famoso mtodo on0ptionsItemSeiected(item) que
trata os eventos das aes da action bar. O mtodo onDestroyActionMode() chama
do quando a CAB encerrada, e neste momento devemos limpar os recursos,
pois a action bar deve voltar ao seu estado padro. Para encerrar a CAB, deve-se
chamar o mtodo actionMode.nish(). Um bom momento para fazer isso depois
de o usurio escolher alguma das opes.
Neste momento, execute o projeto para conferir o resultado. Ao selecionar um
carro por mais de dois segundos, a CAB ser ativada. No entanto, voc deve ter
percebido um bug ao ativar a CAB, pois somente o primeiro carro selecionado.
Se voc tocar no segundo carro, feita a navegao para a activity de detalhes,
em vez de selecionar a lin_ha.

Isso acontece porque o mtodo onC1ickCarro(view,idx) est programado para sempre


fazer a navegao de telas.
public void onClickCarro(View view, int idx) {
Carro c = carros.get(id);
Intent intent = new Intent(getContet(), CarroActivity.c1ass);
intent.putEtra("carro", c);
startActivity(intent);
}

Para resolver esse problema, altere o mtodo onC1ickCarro(viewI idx) para [estar se 3
CAB est ativada; neste caso, o correto apenas selecionar o carro e redesenhar a lista
Captulo 19 n Action bar de contexto e compartilhamento 551
CarrosFragment.java
public void onClickCarro(View view, int idx) {
Carro c = carros.get(id);
if (actionMode == null) {
Intent intent = new Intent(getContet(), CarroActivity.class);
intent.putExtra("carro", c);
startActivity(intent);
} else { // Se a CAB est ativada
// Seleciona o carro
c.selected = !c.selected;
// Atualiza o titulo com a quantidade de carros selecionados
updateActionModeTitle();
// Redesenha a lista
recyclerview.getAdapter().notifyDataSetChanged();
}

Com essa alterao, a seleo mltipla de linhas vai funcionar quando a CAB
estiver ativada. Caso contrrio, a navegao para a tela de detalhes do carro feita
normalmente.

19.4 Removendo os carros selecionados


Uma vez que a CAB estiver ativada, vamos tratar da ao de deletar da action bar.
Como j criamos a classe CarroDB, vamos apenas recuperar a lista de carros sele
cionados e chamar o mtodo deleteCarro(c) para cada objeto.

CarrosFragment.java
@Override
public boolean onActionItenClicked(ActionMode node, Menulten item) {
List selectedCarros = getSelectedCarros();
if (item.getItemId() == R.id.action_remove) {
CarroDB db = new CarroDB(getContext());
try {
for (Carro c: selectedCarros) {
db.delete(c); // Deleta o carro do banco
carros.remove(c); // Renove da lista
}

} nally {
db.close();
552 Google Android - 4 edio
}

toast("Carros excludos con sucesso.");


} else if (lten.getItemId() == R.d.actlon_share) {
toast("Conpartilhar: " + selectedCarros);
}

node.nsh();// Encerra o action node


return true;
}

Concludas essas alteraes, voc podera ativar a CAB para eXClUlf V21I'l05 CHTYOS
de uma nica vez. Depois de brincar um pouco com isso, recomendo fazer o gesto
de Pull to Refresh para buscar os carros do web service, e restaurar a lista original.
Um padro de interface bem comum no Material Design uma view quadrada
com uma mensagem que aparece na parte inferior da tela. Essa view chamada
de Snackbar, e um exemplo de onde ela utilizada o aplicativo do Gmail. No
Gmail, depois de selecionar e excluir um email, uma mensagem aparece com o
texto ldeleted e a ao UNDO para desfazer a excluso. A boa notcia que a Snaclzbar
faz parte da Android Design Support Library, que pode ser configurada no projeto
por meio desta dependncia:

app/bui|d.gradIe

comple 'com.android.support:desgn:22.2.0'

O cdigo para mostrar uma Snaclebar similar ao utilizado para mostrar um Toast,
conforme demonstrado a seguir. Observe que o primeiro parmetro a view que
serve como ncora para mostrar a mensagem; neste caso utilizamos o Recylerview.
Snackbar.make(recyclerVew, "Carros excludos com sucesso.", Snackbar.LENGTH_LONG)
.setActon("0K", new View.0nClckLstener() {
@0verride
public void onClck(Vew v) {
toast("OK");
}

}).show();

19.5 Compartilhando os carros selecionados

Para compartilhar os carros selecionados, altere o arquivo de menu que inflado


para criar a LAB e congure a classe androd.support.v7.wdget.ShareActionProvder.
Captulo 19 I Action bar de contexto e compartilhamento 553
/res/menu/menu_frag_carros_context.xmI
<menu . . >
<item android:id="@+id/action_share" ...
app:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
<item android:id="@+id/action_remove" . . . />

Ao compartilhar as informaes, precisamos criar um objeto do tipo


android . content.Intent e preencher com os dados necessrios. Essa intent congu
rada no ShareActionProvider. Com isso, o Android poder disparar uma mensagem
de broadcast para todas as aplicaes instaladas no dispositivo, com a inteno
de que cada uma delas responda se capaz de tratar o compartilhamento ou
no. Por isso, ao compartilhar fotos, voc pode escolher entre envi-las por Gmail,
I-Iangouts, WhatsApp etc. Tendo em vista que nosso objetivo compartilhar os
carros, vamos atualizar o cdigo conforme demonstrado a seguir.

CarrosFragment.java

import android.support.v7.widget.ShareActionProvider;
public class CarrosFragment extends BaseFragment {
public Intent shareIntent;

@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Ina o menu especico da action bar de contexto (CAB)
Menulnater inater = getActivity().getMenuInater();
inater.inate(R.menu.menu_frag_carros_contet, menu);
Menultem shareItem = menu.ndItem(R.id.action_share);
ShareActionProvider share = (ShareActionProvider) M
enuItemCompat.getActionProvider(shareltem);
shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/*");
share.setShareIntent(shareIntent);
return true;
}

private void updateActionModeTitle() {


if (actionMode != null) {
// Atualiza o titulo da CAB aqui
554 Google Android - 4 edio
updateShareIntent(selectedCarros);
}

// Atualiza a share intent com os carros selecionados


private void updateShareIntent(ListCarro> selectedCarros) {
if(shareIntent != null) {
// Texto com os carros
sharelntent.putExtra(Intent.EXTRA_TEXT, "Carros: " + selectedCarroS)3
}

Nota: no cdigo-fonte, importe a classe android.support.v7.widget.ShareActionProvider e


no a classe android.widget.ShareActionProvider, pois estamos utilizando a biblioteca
de compatibilidade.

O segredo criar a intent de compartilhamento no mtodo onCreateActionMode()


logo aps inflar o menu com as aes da CAB. Observe que o objeto da intent
salvo no atributo sharelntent. Depois, sempre que o usurio selecionar algum carro,
o mtodo updateActionModeTitle() chamado para atualizar o ttulo da CAB, com o
objetivo de mostrar o texto 1 carro selecionado ou x carros selecionados? Neste
momento, vamos chamar o mtodo updateShareIntent() para congurar e atualizar
o contedo da intent com uma string que tem o nome de todos os carros.

Dica: a classe android.content.Intent o corao do Android e tudo gira em torno


dela. At o momento, tnhamos visto que uma intent utilizada para fazer a
navegao de telas com o mtodo startActivity(intent). Agora vimos que uma
intent tambm utilizada para compartilhar informaes. Na verdade, uma
intent representa uma mensagem da aplicao para o sistema operacional,
solicitando que algo seja realizado. Cada aplicativo pode tratar essa ao se for
de seu interesse. Para mais detalhes, verique o captulo 20 sobre a classe Intent.

Para testar a teoria, recomendo instalar a aplicao em um dispositivo real, pois


ele tem mais aplicaes para tratar o compartilhamento. No caso do emulador 1

conforme a gura 193, podemos enviar os carros selecionados por SMS. Veja que
ate o momento a intent de compartilhamento foi configurada para enviar apenas
_ . ~ utura melhoria seria compartilhar
o nome dos carros em modo texto, mas uma f
mais informaoes, como as fotos.
Captulo 19 I Action bar de contexto e compartilhamento 555
19.6 Compartilhando as fotos dos carros selecionados
Para compartilhar as fotos dos carros, precisamos salv-las em arquivos, para na
sequncia criar uma intent com o contedo desses arquivos. Uma maneira de fazer
isso salvar a foto do carro em arquivo no mesmo instante em que mostramos
a foto no adapter, mas desse jeito podemos gastar recursos demais, uma vez que
todas as fotos seriam salvas sem necessidade.
Outra forma , no momento em que o usurio clicar em compartilhar, disparar
uma tarefa (Thread ou AsyncTasl<) para fazer o download das fotos para arquivo,
para somente depois enviar a intent com o compartilhamento. Para o aplicativo
dos carros, vou escolher essa segunda opo. No tpico anterior, pedi para voc
congurar a classe androd . support.v7.wdget.ShareActonProvder no arquivo de menu,
mas desta vez vou pedir para voc remov-la. Portanto, remova essa linha indicada
do arquivo de menu.

/res/menu/menu_frag_carros_context.xmI
<menu . . >
<item androd:id="@+d/acton_share" ...
app:actonProvderC1ass="androd.support.v7.wdget.ShareActonProvider" />
<iten androd:d="@+d/acton_remove" . . . />

O motivo por no utilizar a classe ShareActonProvder porque, ao tocar no boto de


compartilhar, vamos disparar uma tarefa para fazer o download das fotos. E caso
a classe ShareActonProvder seja utilizada, ela toma conta de tudo, e no nos deixa
tratar o evento. Portanto, apague tudo o que zemos no tpico anterior, para deixar
o cdigo exatamente como estava antes de iniciar a parte de compartilhamento.
Peo desculpas por isso, mas que eu precisava explicar a classe ShareActonProvder,
porm, para este exemplo mais avanado, eu prefiro no utiliz-la.
Voc deve remover a parte do cdigo dentro do mtodo onCreateActonMode() que
congura o ShareActonProvder, remova o mtodo updateShareIntent() e tambm o
atributo da classe Intent sharelntent. Quando o cdigo estiver sem a funao de
compartilhamento, vamos continuar.
Na ao R.d . action share no mtodo onAct nItenC1tcked(), vamos chamar uma tarefa
para fazer o download das fotos para arquivo. Logo em seguida, vamos disparar
uma intent que vai conter a lista dos arquivos para serem compartilhados. Para
isso, deixe o cdigo conforme demonstrado a seguir:
556 Google Android - 4' edio

CarrosFragment.java
public class CarrosFragment extends BaseFragment {

@0verride
public boolean onActionItemClicked(Actionode mode, Menultem item) {
List<Carro selectedarros = getSelectedCarros();
if (item.getItemId() == R.id.action_remove) {

} else if (item.getItemId() == R.id.action_share) {


// Dispara a tarefa para fazer download das fotos
startTask("compartilhar",new CompartilharTask(selectedCarros));
}
// Encerra o action mode
mode.nish();
return true;
}

// Task para fazer o download


// Faa import da classe android.net.Uri;
private class CompartilharTask implements TaskListener {
// Lista de arquivos para compartilhar
ArrayList<Uri imageUris = new ArrayListUri();
private nal List selectedCarros;
public CompartilharTask(List selectedCarros) {
this.selectedCarros = selectedCarros;
}

@0verride
public Object execute() throws Exception {
if(selectedCarros != null) {
for (Carro c : selectedCarros) {
// Faz o download da foto do carro para arquivo
String url = c.urlFoto;
String leName = url.substring(url.lastInde0f("/"));
// Cria o arquivo no SD card
File le = SDCardUtils.getPrivateFile(getContet(),"carros",leName);
IOUtils.downloadToFile(c.urlFoto,le);
// Salva a Uri para compartilhar a foto
imageUris.add(Uri.fromFile(le));
}

rrurn null;
}
Captulo 19 I Action bar de contexto e compartilhamento 557
@Override
public void updateView(0bject o) {
// Cria a intent com a foto dos carros
Intent sharelntent = new Intent();
sharelntent.setAction(Intent.ACTION_SEND);
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
sharelntent.putParcelableArrayListEtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
// Cria o Intent Chooser com as opes~
startActivity(Intent.createChooser(sharelntent, "Enviar Carros"));
}

@0verride
public void onError(Eception e) { alert("0correu algum erro ao compartilhar."); }
@0verride
public void onCancelled(String s) { }
}

Ao compartilhar os carros, disparada uma tarefa/ thread, da mesma forma que


zemos ao consultar os carros do web service. O mtodo execute() vai executar em
segundo plano, sendo responsvel por fazer o download das fotos para arquivo
e tambm por salvar uma lista do tipo ArrayList com esses arquivos. A classe
android . net.Uri representa um recurso acessado por um caminho. Na prtica, a Uri
contm a localizao do arquivo. O mtodo updateView() executado na UI Thread
quando o dovvnload das fotos terminar; neste momento, estamos criando a intent,
e congurando-a com as fotos que precisam ser enviadas.
Algo interessante deste exemplo o mtodo Intent.createChooser(intent,titulo), que
cria um alerta para o usurio escolher com qual aplicao ele deseja compartilhar
as informaes. Segundo a documentao do Android, utilizar este escolhedor de
aplicativos (chooser) tem trs vantagens:
1. O alerta com as opes ser exibido sempre, mesmo se o usurio j sele
cionou algum aplicativo para ser o compartilhador padro.
2. Se nenhuma aplicao conseguir tratar a mensagem enviada pela intent,
ser mostrado um alerta com uma mensagem de erro padro. Caso voc
no utilize o chooser, ocorre um erro em tempo de execuo.
3. Podemos denir um ttulo para a janela do alerta.
A gura 19.4 mostra o resultado deste exemplo. Ao selecionar alguns carros e
clicar no boto compartilhar, aparece o alerta com as aplicaes que podem
compartilhar as fotos.
S58 Google Androitl - 4 edio

.._
_1 E. Y_ wi /'
* Q _. _ " = fat _',1J ^
$ _- qn ..z_ -_ _ , < ,.vz/ .z- ~
K
~f~._'
24
V_z` \_; l i=
.t _ _ _ - _; . ~4 _ 1 ,__f, zh.-'V ,
_fWf

.i\.!fl

M Gmail
.i;!zH,i._ Ut*="=' L z*-<w\="H\'
WhatsApp

11 _
iu.. )I At LI'
L Olive

Figura 19.4 - Compartilhando fotos.

A Figura 19.5 mostra o resultado ao escolher o aplicativo do Gmail para compar


tilhar as fotos.

Dica: para testar melhor a funcionalidade de compartilhamento, utilize um


dispositivo real.

Hom rie:hm@gma|l com v i


l

*To
l

l1

_ Ricardo Lecheta

Figura 19.5 - Compurti1lzam'l itos pelo Gmail.


Captulo 19 I Action bar de contexto e compartilhamento 559
19.7 Links teis
Neste captulo, estudamos como habilitar a action bar de contexto (CAB) para
selecionar mltiplas linhas de uma lista e tambm fizemos um exemplo para
compartilhar informaes. Para continuar seus estudos, separei alguns links da
documentao ocial.
Android API Guides - Contextual Menus

http://developer android. com/guide/topics/ui/menus. html


Android API Guides - Contextual Action Mode (CAB)

http://developer android. com/guide/topics/ui/menus.html# CAB


Android Training - Sharing Simple Data

http://developer android.com/training/sharing/index. html


` cAPnuLo 20
\ \

\
Intents

"l

Neste captulo, vamos fazer uma pausa no desenvolvimento do projeto dos carros
para aprender detalhes importantes sobre a classe androd.content.Intent, que e 0
corao do Android. Uma intent uma mensagem da aplicao para o sistema
operacional, solicitando que algo seja realizado, e representa um importante papel
na arquitetura do Android para integrar diferentes aplicaes.
Cabe ao sistema operacional interpretar essa mensagem e tomar as providncias
necessrias, que pode ser abrir um aplicativo, fazer uma ligao para determinado
nmero, tirar uma foto ou at abrir o browser em uma pgina da internet.
Demonstraremos diversos exemplos de como utilizar a classe Intent e aprendere
mos a chamar algumas aplicaes nativas do Android.

20.1 Intent- envio de uma mensagem ao Android


A classe androd.content.Intent representa uma ao que a aplicao deseja executar.
A traduo de intent para o portugus inteno, de forma que podemos dizer
que uma intent representa a inteno da aplicao de realizar determinada tarefa.
Na prtica, essa inteno enviada ao sistema operacional como uma mensagem,
chamada de broadcast. Ao receber a mensagem, o sistema operacional tomar as
decises necessrias, dependendo do contedo da mensagem.
Uma intent pode ser utilizada para:
enviar uma mensagem para o sistema operacional;
abrir uma nova tela da aplicao, utilizando o mtodo startActvty(ntent);
ligar para um nmero de celular;
abrir o browser em uma pgina da internet;
z

enviar uma mensagem por SMS ou email;

560
Captulo 20 1 |mem5 561
exibir algum endereo, localizao ou rota no Google Maps;
abrir a agenda de contatos para selecionar um contato;
abrir a galeria de fotos ou vdeos para selecionar um arquivo de multimdia;
tocar uma msica ou vdeo;
.
abrir a cmera e solicitar que uma foto ou vdeo sejam gravados;
enviar mensagens de uma tela para outra dentro do mesmo aplicativo, ou
at trocar mensagens entre diferentes aplicativos;
integrar diferentes aplicaes; por exemplo, possvel enviar uma mensagem
para aplicativos especializados em ler um cdigo de barras para obter a resposta;
abrir o Google Play para fazer a instalao de determinado aplicativo;
executar algum processamento pesado em segundo plano usando as classes
BroadcastRecever e Service, que sero explicadas em outros captulos;
e muito mais.

Nota: uma intent entre muitas coisas a forma utilizada para fazer com que
aplicaes em processos diferentes se comuniquem, utilizando uma mensagem
que, dependendo do seu contedo, pode ser interceptada por qualquer aplicao.

20.2 Intents explcitas e implcitas


Nesta altura do campeonato, voc j sabe navegar entre as telas da aplicao, e
para isso utilizamos uma intent. Neste tpico, vamos revisar alguns conceitos e
aprender a diferena entre intents explcitas e implcitas.
Uma activity representa uma tela da aplicao, e para navegar entre essas telas
utilizamos o mtodo startActivty(ntent). O parmetro intent uma instncia da
classe androtd.content.Intent que contm as informaes necessrias referentes
activity que ser executada. Por exemplo:
// Intent explcita
Intent intent = new Intent(getContet(), CarroActvity.c1ass);
tntent.putEtra("carro", carro);
startActivty(intent);
Esse trecho de cdigo na verdade criou uma mensagem para o sistema operacional,
cujo contedo : Minha inteno/vontade abrir a tela do carro Ao chamar
Q mtodo startActtvty(intent), essa mensagem enviada ao sistema operacional.
562 Google Android - 4' edio
(abe ao sistema operacional analisar o contedo da mensagem _I1C0ff1" ela
correta que precisa ser aberta. A intent deste exemplo de iizivegw PNQ tel 'd
carro e chamada de intentexpllcita, pois ela especifica exatamente llll QCUVWY deve
ser chamada. Isso feito quando voc precisa abrir uma activity que fazpatte do
seu prprio aplicativo. Mas lembre-se: uma intent e muito mais do que isso, pois
na pratica uma mensagem enviada ao sistema operacional.
O interessante de uma mensagem enviada por uma intent e que nem sempre ela
recebida pela prpria aplicao que a criou, por isso ela frequentemente utilizada para
integrar aplicaes. Para exempli car, veja este exemplo do mtodo startActvty(ntent),
que utiliza a ao ACTION_CALL e uma Ur em vez de especicar a activity:
// Intent implcita
Uri ur = Uri.parse("te1:12345678");
Intent intent = new Intent(Intent.ACTION_CALL, ur);
startActvty(ntent);
Nesse caso, o mtodo startActvty(ntent) foi utilizado novamente, mas desta vez
nenhuma activity foi especicada. Essa uma intent implcita, que na prtica repre
senta uma mensagem enviada para todas as aplicaes instaladas no sistema.
Neste momento, caber a cada aplicao interpretar a mensagem, informando
ao Android quem poder trata-la.
Especificamente para esta mensagem "teI:12345678", quem vai trat-la a aplicao
que faz ligaes, portanto o resultado que a activity da aplicao de ligao vai abrir
e ligar para esse nmero. Esse exemplo inicial foi apenas para voc entender que uma
intent e utilizada para tudo, no apenas para navegar entre as activities do aplicativo.

Nota: uma intent explcita determina exatamente qual activity deve executar.
Esse tipo de intent utilizado quando a activity ou mensagem deve ser enviada
para alguma classe que faz parte do seu aplicativo. Uma intent implcita uma
mensagem genrica enviada ao sistema peracional e pode ser tratada por
qualquer aplicao instalada.

20.3 Exemplos de intents nativas

Acredito que a melhor maneira de entender o que e uma intent e partirmos para
21 PFIICH; POFIZIHIO, abra o prjeto de exemplo deste captulo no /\ndroid Studio
Seguindo mesmozum
'elo contem template de outros projetos
Lstview ` J Vdoilivro,
~ a--activity
2 . .finicial
'. . do
pm
J I com varias opoes. (sida opao t um exemplo de Ifelll
que pode ser enviada ao sistema operacional.
Captulo 20 n Intents

MainActivity.java
package br.com.livroandroid.intents;
public class MainActivity extends AppCompatActivity implements
Adapterview.0nItemClickListener {
private static nal String TAG = "livroandroid";
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] items = new String[]{
"Ligar para telefone", "Discar para telefone",
"Enviar Email", "Enviar SMS",
"Abrir Browser", "Mapa - Lat/Lng",
"Mapa - Endereco", "Mapa - Rota",
"Compartilhar", "Camera Foto",
"Camera Video", "Visualizar Todos Contatos",
"Visualizar Contato 1", "Selecionar Contato",
"Intent customizada", "Intent customizada / schema",
"Sair"
};
ListView listView = new ListView(this);
setContentView(listView);
listview.set0nItemClickListener(this);
listView.setAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1, items));
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
switch (position) {
// Ligar para um nmero de telefone
case 0:
Uri uri = Uri.parse("tel:12345678");
Intent intent = new Intent(Intent.ACTION_CALL, uri);
startActivity(intent);
break;
// Discar para um nmero de telefone
case 1:
Uri z Uri.parse("tel:12345678");
intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
break;
// Enviar um email
Google Android - 4 edio

case 2:
// Email
Intent emaillntent = new Intent(Intent.ACTION_SEND)J _ u
emaillntent.putExtra(Intent.EXTRA_SUBJECT, "Titulo do ell );
emaillntent . putEtra(Intent . EXTRA_TEXT , "Ol" );
emaillntent.putEtra(Intent.EXTRA_EMAIL, "rlecheta@93\-C")3
emaillntent.setType("message/rfc822");
startActivity(emaillntent);
break;
// Enviar um SMS
case 3:
// SMS
uri = Uri.parse("sms:12345678");
Intent smslntent = new Intent(Intent.ACTION_SENDTO,uri);
smslntent.putEtra("sms_body", "0l");
startActivity(smsIntent);
break;
// Abrir o browser em uma pgina
case 4:
// Browser
uri = Uri.parse("http://google.com");
intent = new Intent(Intent.ACTION_VIEH, uri);
startActivity(intent);
break;
// Abrir o mapa na coordenada: Latitude/Longitude
case S:
// Mapa
String GEO_URI = "ge0:-25.4@89185,-49.3222402";
intent = new Intent(Intent.ACTION_VIEH, Uri.parse(GEO_URI));
startActivity(intent);
break;
// Abrir o mapa em um endereo
case 6:
// Mapa
GEO_URI = "geo:G,0?q=Av. Manoel Ribas - Santa Felicidade,
Curitiba - Paran, Brasil";
intent = new Intent(Intent.ACTION_VIEw, Uri.parse(GEO URI));
startActivity(intent);
break;
// Abrir o mapa e mostrar a rota entre dois endereos
case 7:
String rota =
Captulo 20 1 Intents

"httpz//maps.google.com/maps?saddr=-25.4089185,-49.3222402&dad
dF=-2S.428781, -49.30925";
intent = new Intent(Intent.ACTION_VIEw, Uri.parse(rota));
startActivity(intent);
break;
// Compartilhar informaes
case 8:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
sharelntent.putEtra(Intent.EXTRA_SUBJECT, "Compartilhar");
shareIntent.putEtra(Intent.EXTRA_TEXT, "Bla bla bla");
startActivity(sharelntent);
break;
// Abrir a cmera para tirar uma foto
case 9:
Intent fotolntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(fotoIntent, 9);
break;
// Abrir a cmera para gravar um video
case 10:
Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE)
startActivityForResult(videolntent, O);
break;
// Abrir a agenda para visualizar todos os contatos
case 11:
uri = Uri.parse("content://com.android.contacts/contacts");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
// Abrir a agenda para visualizar um contato
case 12:
uri = Uri.parse("content://com.android.contacts/contacts/1");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
// Abrir a agenda para escolher um contato
case 13:
uri = Uri.parse("content://com.android.contacts/contacts");
intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);
break;
// Intent com ao customizada
case 14:
566 Google Android - 4 edio
intent = new Intent("br.com.iivroandroid.intents.TESTE");
startActivity(intent);
break;
// Intent com ao customizada especicando um 'schema'
case 15:
// INTENT_FILTER
uri = Uri.parse("livroandroid://carros/ferrari");
intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);
break;
default:
nish();
break;
}

} catch (Exception e) {
Toast.makeTet(this, "Erro 1" + e.getMessage(), Toast.LENGTH_SHORT).show();
}

@0verride
protected void onActivityResult(int codigo, int resultado, Intent it) {
Log.i(TAG, "Menu.onActivityResult: " + codigo + ", resultado: " + resultado + " > " + it);
if (codigo == 9 && resultado == Activity.RESULT_0K) {
// Tirou a foto, aqui podemos ler o Bitmap
}

else if (codigo == 13 && resultado == Activity.RESULT_0K) {


// Selecionou o contato
Uri uri = it.getData();
Log.d(TAG,"Contato Uri: " + uri);
}

Neste cdigo, temos vrios exemplos de intents implcitas que sero enviadas como
mensagem para o sistema operacional. Cada intent tem como objetivo realizar algu
ma ao, como abrir o browser, abrir o mapa, ligar para um telefone etc. Recomendo
que voc execute este projeto no emulador ou em um dispositivo para conferir o
resultado. Veja que o cdigo-fonte est comentado, portanto leia com calma.

Nota: algumas destas intents vo acessar a agenda de contatos, adicione alguns


contatos no emulador para testar. Note que existe uma intent no cdigo que
tem como objetivo mostrar o Ccontato d `d=l - ` z CSSC
l , LDIEIO 8l`l.lI`.llTT1[t , C()I'l[2llO
precisa existir.
Captulo 20 n Intents 567
Veja que existem muitas maneiras de criar uma intent. Um dos construtores da classe
android.content.Intent aceita apenas uma string que pode ser qualquer coisa, como
abacaxi ou bingo Essa string a ao que a mensagem vai executar. Por conven
o, a ao composta do nome do pacote, mais um cdigo, como por exemplo:
// Intent implcita: Que vai trat-la?
Intent intent = new Intent("br.com.livroandroid.intents.TESTE");
startActivity(intent);

Como essa uma intent implcita que tem uma ao genrica, alguma aplicao
deve entender o que signica essa ao. Depois vamos estudar mais detalhes
sobre isso, mas, apenas para adiantar o assunto, essa intent vai abrir a activity
TesteActivity. Isso acontece porque no manifesto da aplicao foi congurado um
ltro de inteno, conforme mostra o trecho de cdigo a seguir:
// AndroidManifest.xml
<activity android:name=".TesteActivity" . . >
<intent-lter>
<action android:name="br.con.Iivroandroid.intents.TESTE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-1ter>

Outro construtor disponvel na classe android.content.Intent aceita um parmetro


que a ao e uma Uri com o contedo; por exemplo:
Uri uri = Uri.parse("te1:12345678");
Intent intent = new Intent(Intent.ACTION_CALL, uri);
startActivity(intent);
Existem muitas aes padres da plataforma, e cada uma delas apresenta constan
tes na classe Intent. Na prtica, cada ao uma String, mas utilizam-se as constan
tes para facilitar o cdigo. Algumas das aes mais comuns so Intent.ACTION_VIEw,
Intent.ACTION_CALL,Intent.ACTION_DIAL,Intent.ACTION_SEND,Intent.ACTION_IMAGE_CAPTUREEIC.
No cdigo anterior, a ao Intent.ACTION_CALL; por isso, quando a aplicao abrir,
ela vai entender que deve efetuar a ligao telefnica. s vezes a mesma aplica
o pode receber diferentes tipos de mensagens, portanto se trocar a ao para
Intent.ACTION_DIAL a aplicao do telefone vai abrir novamente, mas desta vez vai
apenas discar o nmero, sem efetuar a ligao. Na prtica, cada aplicao nativa
tambm uma activity e cada activity pode decidir o que fazer com base na ao
e parmetros recebidos pela intent. A figura 20.1 mostra o projeto de exemplo
deste captulo executando no emulador, e o primeiro exemplo que faz a ligao
para um nmero de telefone.
568 Google Android - 4 edio

~ Ligar para telefone

Dlscar para telefone

Enviar E-mall

Enviar SMS

Abrir Browser

Mapa - Lat/Lng

Mapa - Endereco

Mapa - Rota

Figura 20.1 - Projeto exemplo com intents.

20.4 Permisses
Algumas intents precisam de permisses para funcionar, como nos casos de fazer
ligao, listar os contatos da agenda e abrir o browser. Portanto, este breve tpico
serve apenas para alert-lo de que essas permisses foram configuradas no projeto
de exemplo deste captulo.
<uses-permission androd:name="androd.permsson.CALL_PHONE" />
<uses-permission androd:name="androd.permission.READ_CONTACTS" />
<uses-permission androd:nane="androd.pernsson.INTERNET" />

20.5 Retornando resultados de uma intent - startActivityForResuIt


Se voc observar nos exemplos, as intents da cmera e da agenda de contatos fo
ram disparadas com o mtodo startActvtyForResu1t(intent, codigo), pois estamos
interessados na resposta. No caso da aplicao da cmera, a resposta ser a foto;
e no caso da aplicao da agenda, a resposta o contato selecionado.
Fique tranquilo se voc achou os exemplos sobre cmera e contatos simples demais 3

pois este captulo apenas uma breve introduo sobre as intents. Posteriormente.
teremos captulos dedicados aos recursos de multimdia e agenda de contatos/
Sobre o exemplo que tein como objetivo selecionar um contato, utilizada uma Uri
Captulo 20 1 Intents 569
customizada do provedor de contedo dos contatos e a ao Intent.ACTION_PICK, que
por conveno indica que a intent deve retornar algum resultado.
// Abre a agenda para selecionar um contato
Uri uri = Uri.parse("content://com.android.contacts/contacts");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);

O mtodo startActivityForResult(intent,codigo) recebe dois parmetros:


Parmetro Descrio
intent Intent que representa a mensagem.
int codigo Cdigo numrico utilizado para identicar a activity que ser iniciada,
chamado de requestCode no Android. O mesmo valor ser passado ao
mtodo onActivityResult(codigo, resultado, intent) quando a activity
chamada nalizar, portanto este cdigo pode ajudar a identicar o
retorno de cada chamada.

Neste exemplo, a agenda de contatos vai abrir, logo cadastre alguns contatos no
emulador para testar. Depois que voc selecionar algum contato, ele ser enviado
como resultado para a activity de origem, ou seja, a MainActivity no nosso caso. O
resultado enviado para o mtodo onActivityResult( . . .) que deve ser sobrescrito na
activity que fez a chamada da intent. No trecho de cdigo citado, o cdigo=13 foi
o utilizado para chamar a activity dos contatos. Nesse contexto estamos testando
se o retorno dessa activity e se o resultado RESULT_0K.
protected void onActivityResult(int codigo, int resultado, Intent it) {

if (codigo == 13 && resultado == Activity.RESULT_0K) {


// A Uri representa o endereo do contato selecionado
Uri uri = it.getData();
Log.d(TAG,"Contato Uri: " + uri);
}

Cada aplicao retorna o resultado de uma maneira diferente, mas no caso da


agenda o retorno enviado pela Uri que representa o contato selecionado. Por meio
dessa Uri, podemos ler as informaes do contato, como nome, email, telefone,
foto etc. Mas isso vamos deixar para outro captulo.
O mtodo onActivityResult(codigo,resultado,intent) recebe os parmetros.

Parmetro Descrio ____v _g __*


int codigo Mesmo cdigo que originou a chamada com o mtodo
startActivitiyForResult(intent, codigo).
570 Google Android - 4 edio

. . . ~ ' ' erar o


Parmetro Descrio (cont)

int resultado Constante que indica se o resultado foi bem sucedigq ou flizam se
ser qualquer constante denida na apl1caaO. POV Pa mo* U l
as constantes Actvty.RESULT_OK e RESULT_CANCELED

intent Intent que originou o retorno. Com essa mtent e possivel recup
Bundle para ler os parmetros retornados. O metodo tntet-9fD()
retorna uma Uri, que algumas aplicaes utilizam para enviar a resposta.

20.6 IntentFiIter
Quando uma mensagem enviada ao sistema operacional utilizando a classe
Intent, necessrio congurar a classe IntentF1ter para interceptar essa mensagem,
com base em seu contedo. Por exemplo, uma das intents que fazem parte do
projeto de exemplo deste captulo esta:
// Este cdigo abre a TesteActivty, mas por qu?
Intent intent = new Intent("br.com.livroandrod.intents.TESTE");
startActvity(ntent);

Ao enviar essa mensagem, o Android abrir a activity que foi congurada para in
terceptar a ao. Essa congurao deve ser feita com a tag <ntent-fi1ter> no arquivo
de manifesto. A seguir podemos visualizar o cdigo do arquivo AndroidManiest.xml
do projeto deste captulo. Veja que a activity TesteActvty foi congurada para
interceptar exatamente a ao br.con.1vroandroid.intents.TESTE.

AndroidManifest.xmI
<?m1 verson="1.0" encoding="utf-8"?>
<manfest _ . .>
<app1cation . . . >
<activty androd:nane=".ManActvty" . . >
<activty androtd:nane=".TesteActvity" . . .>
ntent-1ter>
action androd:nane="br.con.1vroandrod.intents.TESTE" /
<category android:nane="android.intent.category.DEFAULT" ]
</ntent-1ter>
<intent-1ter>
<action android:nane="android.ntent.action.VIEw" />
<category androd:name="android.ntent.category.DEFAULT" />
<data androd:scheme="1vroandrod" />
</ntent-1ter>
</activty>
</applcation>
Captulo 20 1 Intent; 571
Por isso, ao disparar a intent com a ao "br.con.ltvroandroid.intents.TESTE",
a classe TesteActtvtty executada. A tag <tntent-1ter> corresponde classe
android.content.IntentFt1ter, que como voc j deve ter entendido utilizada para
mapear uma ao para determinada classe. Seguindo o mesmo raciocnio, se a
mensagem "bingo" for enviada, tal como no exemplo a seguir:
Intent intent = new Intent("bingo");
startActivity(intent);

Basta declarar o seguinte ltro em qualquer activity para receber essa mensagem.
<intent-1ter>
<action android:name="bingo" />
<category android:name="android.intent.category.DEFAULT" />
</intent-1ter>

Nota: apenas para reforar o conceito, note que nunca declaramos um ltro
para intents explcitas, pois elas especicam exatamente qual activity deve
abrir. Filtros so utilizados apenas para mapear aes genricas, no caso de
intents implcitas.

A grande mgica e o pulo do gato desta arquitetura de mensagens que a dupla


Intent e IntentFi1ter est presente em todos os lugares. Por exemplo, quando o
sistema operacional recebe uma mensagem SMS, ele dispara uma intent com a
ao and rod . provider.Te1ephony . SMS_RECEIVED, que pode ser congurada por qualquer
aplicao que deseja interceptar essa intent, para, por exemplo, ler a mensagem
SMS. No captulo 33, sobre SMS, vamos aprender a fazer isso.
O sistema operacional do Android dispara essas intents para tudo. Por exemplo,
se o Wi-Fi foi ligado ou desligado, podemos interceptar essas mensagens a m de
executar determinada lgica na aplicao. Aos poucos voc vai entendendo esses
conceitos e aprendendo a ltrar essas mensagens; ento que tranquilo, pois o
objetivo principal deste captulo foi ensin-lo sobre essa arquitetura e a grande
importncia de uma intent.

Nota: se traduzirmos literalmente a expresso IntentF1ter, teramos a traduo


ltro de intenes. Nesse caso, vamos dizer que o IntentFi1ter nada mais do
que um ltro de uma intent (inteno). Na prtica, esse ltro quem decide
se a mensagem daquela intent lhe interessa ou no, e, em caso armativo, a
activity executada.
S72 Google Android - 4 edio
20.7 Por que a MainActivity declara um <intent-lter>?

Sempre que criamos um novo projeto, automaticamente o wizard cria a acti gy


principal, que por conveno tem o nome ManActvity. Essa activity e congura a
automaticamente com um <intent-1ter>, conforme demonstrado a seguir:
<activty android:name=".ManActivty" >
<ntent-1ter>
<action android:name="android.ntent.action.MAIN" />
<category android:name="androd.intent.category.LAUNCHER" />
</intent-1ter>
</actvty>

No caso da ManActvty, so declaradas na maioria das vezes a ao androidintent.


acton.MAIN e a categoria androdintent.category.LAUNCHER. Isso feito para adicionar
o cone da aplicao na tela inicial do Android, para o usurio clicar no cone e
abrir a aplicao.
Para revisar o conceito, temos a seguir a definio da ao MAIN e categoria LAUNCHER:
androd.ntent.acton.HAIN - Ao-padro do Android, usada para abrir uma
activity como o ponto de partida, sem depender de outra activity Se uma
activity est congurada com a ao MAIN, ela pode ser iniciada diretamente
pelo usurio ou Android Studio; caso contrrio, ser executada somente
a partir do mtodo startActvty(intent). como denir o famoso mtodo
public static void man(Strng args[]) em uma aplicao Java.

.__,
androd.ntent.category.LAUNCHER - Categoria que faz com que esta activity
que presente na tela inicial do Android, ou seja, esta activity o ponto de
partida da aplicao e pode ser iniciada pelo usurio.

20.8 Exemplo completo com intent customizada

Agora que j estudamos o bsico sobre o que uma mensagem enviada pela
intent e aprendemos a interceptar essas mensagens com um intent filter vamgg
criar um exemplo customizado de intent para abrir a lista de carros

.1.
O exemplo que vamos criar parecido com a intent que mostra roda 3 lista dg
contatos e permite se ecionar um contato, de forma que o contato selecionado
retornado para a aplicao. Apenas para lembrar, essa intent dos contatos pode
ser disparada com este cdigo:
Captulo 20 1 Intents 573
// Abre a agenda para selecionar um contato
Uri uri = Uri.parse("contentz//com.android.contacts/contacts");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 13);

O resultado j sabemos que ser entregue no mtodo onActivityResult(. . . ). O nosso


objetivo ser fazer a mesma coisa, mas vamos disparar uma intent que vai mostrar
a lista de carros, e ao selecionar um carro ele ser retornado para a activity que
fez a chamada. A seguir podemos ver um exemplo desta intent.
// Abre a lista de carros para selecionar um carro
Uri uri = Uri.parse("carros://br.com.livroandroid.carros/carros");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 99);

Da mesma forma que podemos selecionar apenas um contato com a ao


content: / /con.android.contacts/contacts/ 1, podemos criar uma activity que mostre o
carro pela ao carros: / /br.con.livroandroid.carros/carros/ 1, na qual o nmero 1 o
identicador do registro. Porm para este exemplo vou preferir utilizar uma ao
que tem o nome do carro, assim no corremos risco de buscar um carro que no
existe, caso o id no seja encontrado.
Vamos ento criar uma intent que possa abrir um carro pelo nome.
// Abre o carro pelo none
Uri uri = Uri.parse("carrosz//br.com.livroandroid.carros/carros/Ferrari FF");
Intent intent = new Intent(Intent.ACTION_VIEw, uri);
startActivity(intent);

Antes de comearmos, vale ressaltar que esse tipo de intent muito especca, e
dificilmente voc vai precisar fazer este tipo de coisa no seu dia a dia. Ao desen
volver aplicativos, geralmente voc navega entre as activities utilizando intent ex
plcitas. Estamos estudando isso porque quero que voc entenda como o Android
funciona por debaixo dos panos.
Outro ponto que voc precisa entender que estas mensagens podem ser dispara
das de qualquer aplicao instalada. No projeto dos carros, vamos configurar um
ltro de inteno (intent-filter) para interceptar essas mensagens. Portanto vamos
abrir uma porta na aplicao dos carros, para que ela possa fornecer contedo,
de modo a comunicar-se com outras aplicaes. Para continuar, faa uma cpia
do projeto dos carros e crie a seguinte activity:
574 Google Android - 4 ed
1 CarroslntentActivity.java
package br.com.livroandroid.carros.activity;

public class CarrosIntentActivity extends BaseActivity {


private List carros;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_carros_intent);
// (*1*) L as informaes da intent
Intent intent = getIntent();
Uri uri = intent.getData();
Log.d("livroandroid", "Action: " + intent.getAction());
Log.d("livroandroid", "Schemez " + uri.getSchene());
Log.d("livroandroid","Host: " + uri.getHost());
Log.d("livroandroid","Pathz " + uri.getPath());
Log.d("livroandroid","PathSegments: " + uri.getPathSegnents());
// Recyclerview
Recyclerview recyclerview = (Recyclerview) ndViewById(R.id.recyclerview);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
recyclerview.setLayoutManager(nLayoutManager);
// Congura a Toolbar como a action bar
setUpToolbar();
CarroDB db = new CarroDB(this);
try {
if("/carros".equals(uri.getPath())) {
// (*2*)Lista todos os carros
this.carros = db.ndAll();
recyclerview.setAdapter(new CarroAdapter(this, carros, onClickCarro()));
} else {
// (*3*) Busca o carro pelo nome: /carros/Ferrari FF
ListString> paths = uri.getPathSegnents();
String none = paths.get(1);
Carro carro = db.ndByNome(nome);
if(carro != null) {
// Se encontrou o carro, abre a activity para mostr-lo
Intent carrolntent = new Intent(this, CarroActivity.class);
carroIntent.putEtra("carro", carro);
startActivity(carroIntent);
I nish();
}

}
Captulo 20 I Intents 575
} nally {
db.close();
}

private CarroAdapter.Carro0nClickListener onClickCarro() {


return new CarroAdapter.Carro0nClickListener() {
@0verride
public void onClickCarro(View view, int idx) {
Carro c = carros.get(id);
// (*4*) Retorna o carro selecionado para quem chamou
Intent intent = new Intent();
intent.putEtra("nome",c.nome);
intent.putEtra("url_foto",c.urlFoto);
setResult(Activity.RESULT_0K,intent);
nish();
}

@0verride
public void onLongClickCarro(View view, int idx) {
// nada aqui
}

};
}

Essa activity busca todos os carros que esto salvos no banco de dados, e para
simplificar o cdigo note que no utilizei nenhuma thread (AsyncTasl<), mas na
prtica isso seria necessrio. Veja que adicionei algumas anotaes no cdigo,
explicadas logo a seguir.
Na parte (*1*) estamos extraindo as informaes da intent e de sua Uri. Com base
nessas informaes, podemos decidir o que fazer.
Log.d("livroandroid", "Actionz " + intent.getAction()); // Imprime ACTION_VIEw ou ACTION_PICK
Log.d("livroandroid", "Schemez " + uri.getScheme()); // Imprime carro://
Log d("livroandroid","Host: " + uri.getHost()); // Imprime br.com.livroandroid
Log.d("livroandroid","Path: " + uri.getPath()); // Imprime /carros ou /carros/nome

Isso feito para extrair as partes da Uri utilizada pela intent, que carros://br.com.
livroandroid.carros/carros oiicarros://br.com.livroandroid.carros/carros/nome_carro.

A parte (*2*) o cdigo que mostra todos os carros em uma lista com o Recyclerview,
e isso feito se o mtodo uri.getPath() igual a /carros. A parte (*3*) o cdigo
que l o nome do carro, extraindo do path da Uri. Neste caso, o carro buscado
no banco de dados pelo nome, e se for encontrado a activity de detalhes do carro
576 Google Android - 4' edio
chamada. A parte ('4') o cdigo que trata o evento de seleo da ISIQ, TICSC
caso criada uma intent de resultado, com os parmetros que so o I'iOm 6 8
foto do carro. O metodo setResu1t(codigo,ntent) e utilizado para enviar a ltt de
retorno. Sendo assim, quando o metodo finsh() for chamado, a activity que fez a
chamada da intent receber o resultado no mtodo onActvtyResu1t(. - - )

listas explicaes so sucintas, pis espero que voc d uma boa estudada no
cdigo para melhorar seu aprendizado. Vamos continuar com a conguraao.
(lomo essa activity precisa abrir ao receber determinada mensagem, congure o
arquivo de manifesto conforme demonstrado a seguir:

ii AndroidManifest.xmI

<?m1 verson="1.0" encoding="utf-8"?>


<manfest . . .>
<app1ication . . .

<activty android:name=".activity.CarrosIntentActvity" . . . >


</span></span> <span class='ocr_word' id='word_1_141' title="bbox 895 1938 1051 1987"><span class='xocr_word' id='xword_1_141' title="x_wconf -4">Filtro</span></span> <span class='ocr_word' id='word_1_142' title="bbox 1083 1954 1185 1997"><span class='xocr_word' id='xword_1_142' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_143' title="bbox 1217 1941 2414 2003"><span class='xocr_word' id='xword_1_143' title="x_wconf -4">carros://br.com.lvroandrod.carros/carros/*</span></span> <span class='ocr_word' id='word_1_144' title="bbox 2453 1962 2527 1988"><span class='xocr_word' id='xword_1_144' title="x_wconf -1">--</span></span></span>
<span class='ocr_line' id='line_1_17' title="bbox 759 2029 1132 2078"><span class='ocr_word' id='word_1_145' title="bbox 759 2029 1132 2078"><span class='xocr_word' id='xword_1_145' title="x_wconf -4"><intent-1ter></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_18' title="bbox 854 2122 2262 2187"><span class='ocr_word' id='word_1_146' title="bbox 854 2122 1037 2168"><span class='xocr_word' id='xword_1_146' title="x_wconf -3"><acton</span></span> <span class='ocr_word' id='word_1_147' title="bbox 1070 2122 2176 2176"><span class='xocr_word' id='xword_1_147' title="x_wconf -5">androd:name="android.intent.acton.VIEH"</span></span> <span class='ocr_word' id='word_1_148' title="bbox 2213 2126 2262 2187"><span class='xocr_word' id='xword_1_148' title="x_wconf 0">/></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 854 2213 2456 2279"><span class='ocr_word' id='word_1_149' title="bbox 854 2216 1092 2270"><span class='xocr_word' id='xword_1_149' title="x_wconf -1"><category</span></span> <span class='ocr_word' id='word_1_150' title="bbox 1124 2213 2368 2278"><span class='xocr_word' id='xword_1_150' title="x_wconf -4">android:nane="android.intent.category.DEFAULT"</span></span> <span class='ocr_word' id='word_1_151' title="bbox 2407 2220 2456 2279"><span class='xocr_word' id='xword_1_151' title="x_wconf 0">/></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 853 2301 1714 2366"><span class='ocr_word' id='word_1_152' title="bbox 853 2301 983 2349"><span class='xocr_word' id='xword_1_152' title="x_wconf -1"><data</span></span> <span class='ocr_word' id='word_1_153' title="bbox 1015 2302 1630 2355"><span class='xocr_word' id='xword_1_153' title="x_wconf -5">android:schene="carros"</span></span> <span class='ocr_word' id='word_1_154' title="bbox 1664 2307 1714 2366"><span class='xocr_word' id='xword_1_154' title="x_wconf -2">/</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_21' title="bbox 758 2390 1157 2448"><span class='ocr_word' id='word_1_155' title="bbox 758 2390 1157 2448"><span class='xocr_word' id='xword_1_155' title="x_wconf -4"></intent-1ter></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_22' title="bbox 758 2482 2522 2555"><span class='ocr_word' id='word_1_156' title="bbox 758 2484 855 2527"><span class='xocr_word' id='xword_1_156' title="x_wconf -1"><!--</span></span> <span class='ocr_word' id='word_1_157' title="bbox 894 2482 1050 2530"><span class='xocr_word' id='xword_1_157' title="x_wconf -3">Filtro</span></span> <span class='ocr_word' id='word_1_158' title="bbox 1082 2497 1183 2541"><span class='xocr_word' id='xword_1_158' title="x_wconf -1">para</span></span> <span class='ocr_word' id='word_1_159' title="bbox 1215 2485 2410 2555"><span class='xocr_word' id='xword_1_159' title="x_wconf -4">carros://br.con.lvroandroid.carros/carros/*</span></span> <span class='ocr_word' id='word_1_160' title="bbox 2449 2515 2522 2541"><span class='xocr_word' id='xword_1_160' title="x_wconf -1">--</span></span></span>
<span class='ocr_line' id='line_1_23' title="bbox 758 2572 1130 2621"><span class='ocr_word' id='word_1_161' title="bbox 758 2572 1130 2621"><span class='xocr_word' id='xword_1_161' title="x_wconf -4"><ntent-1ter></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_24' title="bbox 853 2664 2259 2736"><span class='ocr_word' id='word_1_162' title="bbox 853 2664 1036 2711"><span class='xocr_word' id='xword_1_162' title="x_wconf -4"><acton</span></span> <span class='ocr_word' id='word_1_163' title="bbox 1068 2664 2171 2725"><span class='xocr_word' id='xword_1_163' title="x_wconf -5">androd:name="android.ntent.action.PICK"</span></span> <span class='ocr_word' id='word_1_164' title="bbox 2209 2678 2259 2736"><span class='xocr_word' id='xword_1_164' title="x_wconf -1">/</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_25' title="bbox 853 2756 2452 2831"><span class='ocr_word' id='word_1_165' title="bbox 853 2759 1090 2812"><span class='xocr_word' id='xword_1_165' title="x_wconf -2"><category</span></span> <span class='ocr_word' id='word_1_166' title="bbox 1122 2756 2364 2826"><span class='xocr_word' id='xword_1_166' title="x_wconf -4">android:name="android.ntent.category.DEFAULT"</span></span> <span class='ocr_word' id='word_1_167' title="bbox 2403 2772 2452 2831"><span class='xocr_word' id='xword_1_167' title="x_wconf -1">/></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_26' title="bbox 852 2842 1710 2910"><span class='ocr_word' id='word_1_168' title="bbox 852 2842 982 2891"><span class='xocr_word' id='xword_1_168' title="x_wconf -2"><data</span></span> <span class='ocr_word' id='word_1_169' title="bbox 1013 2844 1627 2898"><span class='xocr_word' id='xword_1_169' title="x_wconf -4">android:schene="carros"</span></span> <span class='ocr_word' id='word_1_170' title="bbox 1661 2851 1710 2910"><span class='xocr_word' id='xword_1_170' title="x_wconf -1">/</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_6' title="bbox 470 2930 1155 3256">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_27' title="bbox 757 2930 1155 2988"><span class='ocr_word' id='word_1_171' title="bbox 757 2930 1155 2988"><span class='xocr_word' id='xword_1_171' title="x_wconf -4"></intent-1ter</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_28' title="bbox 662 3019 952 3079"><span class='ocr_word' id='word_1_172' title="bbox 662 3019 952 3079"><span class='xocr_word' id='xword_1_172' title="x_wconf -4"></actvity</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 565 3108 937 3166"><span class='ocr_word' id='word_1_173' title="bbox 565 3108 937 3166"><span class='xocr_word' id='xword_1_173' title="x_wconf -4"></applcation></span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 470 3197 760 3255"><span class='ocr_word' id='word_1_174' title="bbox 470 3197 760 3255"><span class='xocr_word' id='xword_1_174' title="x_wconf -4">/manfest</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_7' title="bbox 471 3325 2969 3439">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_31' title="bbox 471 3325 2969 3438"><span class='ocr_word' id='word_1_175' title="bbox 471 3326 523 3379"><span class='xocr_word' id='xword_1_175' title="x_wconf -2">O</span></span> <span class='ocr_word' id='word_1_176' title="bbox 551 3328 820 3397"><span class='xocr_word' id='xword_1_176' title="x_wconf -2">primeiro</span></span> <span class='ocr_word' id='word_1_177' title="bbox 847 3325 989 3384"><span class='xocr_word' id='xword_1_177' title="x_wconf -3">filtro</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1013 3333 1041 3386"><span class='xocr_word' id='xword_1_178' title="x_wconf -3">e</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1066 3350 1194 3403"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1223 3348 1390 3390"><span class='xocr_word' id='xword_1_180' title="x_wconf -2">tratar</span></span> <span class='ocr_word' id='word_1_181' title="bbox 1413 3356 1440 3392"><span class='xocr_word' id='xword_1_181' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_182' title="bbox 1466 3346 1602 3410"><span class='xocr_word' id='xword_1_182' title="x_wconf -3">ao</span></span> <span class='ocr_word' id='word_1_183' title="bbox 1628 3351 2154 3417"><span class='xocr_word' id='xword_1_183' title="x_wconf -3">Intent.ACTION_VIEw,</span></span> <span class='ocr_word' id='word_1_184' title="bbox 2177 3375 2205 3410"><span class='xocr_word' id='xword_1_184' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2230 3377 2266 3411"><span class='xocr_word' id='xword_1_185' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_186' title="bbox 2290 3361 2557 3432"><span class='xocr_word' id='xword_1_186' title="x_wconf -4">segundo</span></span> <span class='ocr_word' id='word_1_187' title="bbox 2583 3385 2718 3438"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_188' title="bbox 2746 3383 2919 3425"><span class='xocr_word' id='xword_1_188' title="x_wconf -2">tratar</span></span> <span class='ocr_word' id='word_1_189' title="bbox 2941 3390 2969 3426"><span class='xocr_word' id='xword_1_189' title="x_wconf -1">a</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_8' title="bbox 469 3428 2971 3641">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 470 3428 2971 3536"><span class='ocr_word' id='word_1_190' title="bbox 470 3428 604 3492"><span class='xocr_word' id='xword_1_190' title="x_wconf -3">ao</span></span> <span class='ocr_word' id='word_1_191' title="bbox 625 3432 1126 3492"><span class='xocr_word' id='xword_1_191' title="x_wconf -2">Intent.ACTION_PICK.</span></span> <span class='ocr_word' id='word_1_192' title="bbox 1141 3429 1360 3487"><span class='xocr_word' id='xword_1_192' title="x_wconf -3">Ambos</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1379 3453 1442 3488"><span class='xocr_word' id='xword_1_193' title="x_wconf -2">os</span></span> <span class='ocr_word' id='word_1_194' title="bbox 1465 3433 1633 3494"><span class='xocr_word' id='xword_1_194' title="x_wconf -4">filtros</span></span> <span class='ocr_word' id='word_1_195' title="bbox 1652 3438 1931 3501"><span class='xocr_word' id='xword_1_195' title="x_wconf -2">declaram</span></span> <span class='ocr_word' id='word_1_196' title="bbox 1951 3467 1978 3506"><span class='xocr_word' id='xword_1_196' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_197' title="bbox 2001 3463 2092 3523"><span class='xocr_word' id='xword_1_197' title="x_wconf -2">tag</span></span> <span class='ocr_word' id='word_1_198' title="bbox 2108 3456 2243 3509"><span class='xocr_word' id='xword_1_198' title="x_wconf -1"><data</span></span> <span class='ocr_word' id='word_1_199' title="bbox 2269 3444 2971 3536"><span class='xocr_word' id='xword_1_199' title="x_wconf -5">android:scheme="carros"/></span></span></span>
<span class='ocr_line' id='line_1_33' title="bbox 469 3520 2969 3641"><span class='ocr_word' id='word_1_200' title="bbox 469 3538 581 3590"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_201' title="bbox 600 3520 981 3592"><span class='xocr_word' id='xword_1_201' title="x_wconf -2">corresponde</span></span> <span class='ocr_word' id='word_1_202' title="bbox 1001 3525 1028 3578"><span class='xocr_word' id='xword_1_202' title="x_wconf -3"></span></span> <span class='ocr_word' id='word_1_203' title="bbox 1052 3541 1206 3596"><span class='xocr_word' id='xword_1_203' title="x_wconf -2">parte</span></span> <span class='ocr_word' id='word_1_204' title="bbox 1226 3536 1473 3597"><span class='xocr_word' id='xword_1_204' title="x_wconf -2">carros://</span></span> <span class='ocr_word' id='word_1_205' title="bbox 1495 3531 1564 3590"><span class='xocr_word' id='xword_1_205' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1586 3543 1685 3603"><span class='xocr_word' id='xword_1_206' title="x_wconf -4">Ur,</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1703 3537 1981 3600"><span class='xocr_word' id='xword_1_207' title="x_wconf -1">chamada</span></span> <span class='ocr_word' id='word_1_208' title="bbox 2004 3544 2075 3603"><span class='xocr_word' id='xword_1_208' title="x_wconf -2">de</span></span> <span class='ocr_word' id='word_1_209' title="bbox 2093 3551 2276 3608"><span class='xocr_word' id='xword_1_209' title="x_wconf -3">Schema.</span></span> <span class='ocr_word' id='word_1_210' title="bbox 2300 3556 2383 3611"><span class='xocr_word' id='xword_1_210' title="x_wconf -2">Da</span></span> <span class='ocr_word' id='word_1_211' title="bbox 2407 3576 2626 3618"><span class='xocr_word' id='xword_1_211' title="x_wconf -1">mesma</span></span> <span class='ocr_word' id='word_1_212' title="bbox 2652 3561 2832 3623"><span class='xocr_word' id='xword_1_212' title="x_wconf -2">forma</span></span> <span class='ocr_word' id='word_1_213' title="bbox 2855 3587 2969 3641"><span class='xocr_word' id='xword_1_213' title="x_wconf -2">que</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 469 3615 2971 3723">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 469 3615 2971 3723"><span class='ocr_word' id='word_1_214' title="bbox 469 3618 601 3671"><span class='xocr_word' id='xword_1_214' title="x_wconf -2">voc</span></span> <span class='ocr_word' id='word_1_215' title="bbox 632 3615 862 3688"><span class='xocr_word' id='xword_1_215' title="x_wconf -2">poderia</span></span> <span class='ocr_word' id='word_1_216' title="bbox 895 3617 1113 3676"><span class='xocr_word' id='xword_1_216' title="x_wconf -2">utilizar</span></span> <span class='ocr_word' id='word_1_217' title="bbox 1139 3628 1320 3690"><span class='xocr_word' id='xword_1_217' title="x_wconf -3">sms://,</span></span> <span class='ocr_word' id='word_1_218' title="bbox 1347 3632 1528 3695"><span class='xocr_word' id='xword_1_218' title="x_wconf -3">tel://,</span></span> <span class='ocr_word' id='word_1_219' title="bbox 1555 3637 1745 3702"><span class='xocr_word' id='xword_1_219' title="x_wconf -2">http://</span></span> <span class='ocr_word' id='word_1_220' title="bbox 1775 3654 1890 3707"><span class='xocr_word' id='xword_1_220' title="x_wconf -2">etc.,</span></span> <span class='ocr_word' id='word_1_221' title="bbox 1915 3661 1974 3698"><span class='xocr_word' id='xword_1_221' title="x_wconf -2">se</span></span> <span class='ocr_word' id='word_1_222' title="bbox 2002 3648 2261 3705"><span class='xocr_word' id='xword_1_222' title="x_wconf -2">estivesse</span></span> <span class='ocr_word' id='word_1_223' title="bbox 2291 3654 2646 3717"><span class='xocr_word' id='xword_1_223' title="x_wconf -2">interessado</span></span> <span class='ocr_word' id='word_1_224' title="bbox 2673 3683 2765 3720"><span class='xocr_word' id='xword_1_224' title="x_wconf -2">em</span></span> <span class='ocr_word' id='word_1_225' title="bbox 2798 3682 2971 3723"><span class='xocr_word' id='xword_1_225' title="x_wconf -2">tratar</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 467 3713 2967 3938">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 468 3713 2966 3837"><span class='ocr_word' id='word_1_226' title="bbox 468 3728 664 3768"><span class='xocr_word' id='xword_1_226' title="x_wconf -3">outros</span></span> <span class='ocr_word' id='word_1_227' title="bbox 684 3716 831 3785"><span class='xocr_word' id='xword_1_227' title="x_wconf -2">tips</span></span> <span class='ocr_word' id='word_1_228' title="bbox 851 3713 920 3770"><span class='xocr_word' id='xword_1_228' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_229' title="bbox 941 3735 1285 3791"><span class='xocr_word' id='xword_1_229' title="x_wconf -3">mensagens.</span></span> <span class='ocr_word' id='word_1_230' title="bbox 1309 3725 1583 3795"><span class='xocr_word' id='xword_1_230' title="x_wconf -2">Prtanto,</span></span> <span class='ocr_word' id='word_1_231' title="bbox 1603 3750 1790 3790"><span class='xocr_word' id='xword_1_231' title="x_wconf -2">nesses</span></span> <span class='ocr_word' id='word_1_232' title="bbox 1811 3735 1986 3795"><span class='xocr_word' id='xword_1_232' title="x_wconf -3">ltros</span></span> <span class='ocr_word' id='word_1_233' title="bbox 2006 3747 2141 3800"><span class='xocr_word' id='xword_1_233' title="x_wconf -3">voc</span></span> <span class='ocr_word' id='word_1_234' title="bbox 2161 3744 2384 3808"><span class='xocr_word' id='xword_1_234' title="x_wconf -1">declara</span></span> <span class='ocr_word' id='word_1_235' title="bbox 2405 3773 2434 3810"><span class='xocr_word' id='xword_1_235' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_236' title="bbox 2456 3764 2595 3829"><span class='xocr_word' id='xword_1_236' title="x_wconf -2">ao</span></span> <span class='ocr_word' id='word_1_237' title="bbox 2615 3781 2731 3835"><span class='xocr_word' id='xword_1_237' title="x_wconf -2">que</span></span> <span class='ocr_word' id='word_1_238' title="bbox 2753 3769 2966 3837"><span class='xocr_word' id='xword_1_238' title="x_wconf -1">precisa</span></span></span>
<span class='ocr_line' id='line_1_36' title="bbox 468 3809 2966 3938"><span class='ocr_word' id='word_1_239' title="bbox 468 3828 553 3864"><span class='xocr_word' id='xword_1_239' title="x_wconf -2">ser</span></span> <span class='ocr_word' id='word_1_240' title="bbox 583 3809 958 3883"><span class='xocr_word' id='xword_1_240' title="x_wconf -3">interceptada</span></span> <span class='ocr_word' id='word_1_241' title="bbox 989 3834 1016 3868"><span class='xocr_word' id='xword_1_241' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_242' title="bbox 1045 3835 1080 3869"><span class='xocr_word' id='xword_1_242' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_243' title="bbox 1107 3818 1269 3872"><span class='xocr_word' id='xword_1_243' title="x_wconf -4">Schema</span></span> <span class='ocr_word' id='word_1_244' title="bbox 1298 3839 1408 3891"><span class='xocr_word' id='xword_1_244' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_245' title="bbox 1439 3828 1571 3881"><span class='xocr_word' id='xword_1_245' title="x_wconf -2">voc</span></span> <span class='ocr_word' id='word_1_246' title="bbox 1599 3833 1714 3886"><span class='xocr_word' id='xword_1_246' title="x_wconf -4">esta</span></span> <span class='ocr_word' id='word_1_247' title="bbox 1747 3834 2103 3896"><span class='xocr_word' id='xword_1_247' title="x_wconf -2">interessado.</span></span> <span class='ocr_word' id='word_1_248' title="bbox 2134 3845 2188 3899"><span class='xocr_word' id='xword_1_248' title="x_wconf -2">O</span></span> <span class='ocr_word' id='word_1_249' title="bbox 2219 3866 2332 3919"><span class='xocr_word' id='xword_1_249' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_250' title="bbox 2362 3857 2499 3910"><span class='xocr_word' id='xword_1_250' title="x_wconf -1">voc</span></span> <span class='ocr_word' id='word_1_251' title="bbox 2529 3855 2728 3918"><span class='xocr_word' id='xword_1_251' title="x_wconf -1">coloca</span></span> <span class='ocr_word' id='word_1_252' title="bbox 2760 3861 2966 3938"><span class='xocr_word' id='xword_1_252' title="x_wconf -4">depois</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 468 3903 2965 4037">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_37' title="bbox 468 3903 2965 4037"><span class='ocr_word' id='word_1_253' title="bbox 468 3903 544 3961"><span class='xocr_word' id='xword_1_253' title="x_wconf -2">do</span></span> <span class='ocr_word' id='word_1_254' title="bbox 569 3908 731 3961"><span class='xocr_word' id='xword_1_254' title="x_wconf -5">sthema</span></span> <span class='ocr_word' id='word_1_255' title="bbox 759 3915 868 3963"><span class='xocr_word' id='xword_1_255' title="x_wconf -3">no</span></span> <span class='ocr_word' id='word_1_256' title="bbox 897 3911 1176 3974"><span class='xocr_word' id='xword_1_256' title="x_wconf -3">interessa,</span></span> <span class='ocr_word' id='word_1_257' title="bbox 1203 3916 1327 3984"><span class='xocr_word' id='xword_1_257' title="x_wconf -2">pois</span></span> <span class='ocr_word' id='word_1_258' title="bbox 1353 3938 1388 3970"><span class='xocr_word' id='xword_1_258' title="x_wconf -1"></span></span> <span class='ocr_word' id='word_1_259' title="bbox 1417 3937 1653 3980"><span class='xocr_word' id='xword_1_259' title="x_wconf -2">restante</span></span> <span class='ocr_word' id='word_1_260' title="bbox 1680 3925 1749 3984"><span class='xocr_word' id='xword_1_260' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_261' title="bbox 1778 3938 1858 3987"><span class='xocr_word' id='xword_1_261' title="x_wconf -4">Uri</span></span> <span class='ocr_word' id='word_1_262' title="bbox 1885 3937 1913 3989"><span class='xocr_word' id='xword_1_262' title="x_wconf -4"></span></span> <span class='ocr_word' id='word_1_263' title="bbox 1940 3955 2112 3994"><span class='xocr_word' id='xword_1_263' title="x_wconf -1">com</span></span> <span class='ocr_word' id='word_1_264' title="bbox 2139 3960 2197 3997"><span class='xocr_word' id='xword_1_264' title="x_wconf -2">se</span></span> <span class='ocr_word' id='word_1_265' title="bbox 2227 3941 2436 4006"><span class='xocr_word' id='xword_1_265' title="x_wconf -2">fssem</span></span> <span class='ocr_word' id='word_1_266' title="bbox 2467 3958 2825 4025"><span class='xocr_word' id='xword_1_266' title="x_wconf -3">parmetros</span></span> <span class='ocr_word' id='word_1_267' title="bbox 2851 3983 2965 4037"><span class='xocr_word' id='xword_1_267' title="x_wconf -2">que</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 468 4002 1210 4078">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_38' title="bbox 468 4002 1210 4078"><span class='ocr_word' id='word_1_268' title="bbox 468 4005 600 4058"><span class='xocr_word' id='xword_1_268' title="x_wconf -4">voc</span></span> <span class='ocr_word' id='word_1_269' title="bbox 627 4002 774 4075"><span class='xocr_word' id='xword_1_269' title="x_wconf -3">pode</span></span> <span class='ocr_word' id='word_1_270' title="bbox 801 4002 876 4061"><span class='xocr_word' id='xword_1_270' title="x_wconf -1">ler</span></span> <span class='ocr_word' id='word_1_271' title="bbox 900 4025 974 4060"><span class='xocr_word' id='xword_1_271' title="x_wconf -2">n</span></span> <span class='ocr_word' id='word_1_272' title="bbox 998 4005 1210 4078"><span class='xocr_word' id='xword_1_272' title="x_wconf -4">codigo.</span></span></span>
</p>
</div>
</div>
</body>
</html>
Captulo 20 1 Intents 577
Feito isso, execute novamente o projeto dos carros no emulador do Android, e o
resultado que esta nova activity ser instalada na aplicao. Na sequncia, feche
o aplicativo dos carros.
Para testar a intent, vamos precisar criar um novo projeto que dispare estas duas
intents. Assim, crie o projeto Teste-Intent-Carros. Este projeto contm apenas a
ManActvty com um layout que tem dois botes para disparar as intents. No layout
tambm temos um campo de texto para mostrar o nome do carro selecionado e
uma imagem para mostrar a foto.

/res/layout/activity_main.xmI
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
androd:padding="16dp" androd:orientation="vertica1">
<TetView
android:tet="@string/he11o_wor1d"
androd:1ayout_width="wrap_content" android:1ayout_height="wrap_content" />
<Button
android:onC1ck="onC1ickMostrarCarroPe1oNome"
android:layout_width="wrap_content" android:layout_heght="wrap_content"
android:text="Mostrar carro pelo nome" />
<Button
androd:onC1ck="onC1ckSe1econarCarro"
android:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
android:tet="Se1econar um carro" />

<EdtText
androd:d="@+d/tNomeCarro"
android:1ayout_width="match_parent"
android:1ayout_heght="wrap_content"
androd:hint="@string/nome_carro_selecionado"/>

<ImageVew
androd:d="@+d/mgFotoCarro"
android:1ayout_wdth="match_parent"
androd:1ayout_height="0dp"
android:1ayout_weight="1"
android:layout_margin="10dp"/>

A seguir, podemos ver o cdigo da ManActivity:


578 Google Android - 4 edio

MainActivity.java

import con.squareup.picasso.Picasso;
public class MainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}

public void onClickMostrarCarroPeloNone(View view) {


Uri uri = Uri.parse("carros://br.con.livroandroid.carros/carros/Ferrari FF");
Intent intent = new Intent(Intent.ACTION_VIEN, uri);
startActivity(intent);
}

public void onClickSelecionarCarro(View view) {


Uri uri = Uri.parse("carros://br.con.livroandroid.carros/carros");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
startActivityForResult(intent, 99);
}

@0verride
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Activity.RESULT_0K && requestCode == 99) {
// L as informaes do carro selecionado
String none = data.getStringEtra("nome");
String url_foto = data.getStringEtra("url_foto");
Log.d("livroandroid","Fotoz " + url_foto);
// Mostra os dados do carro selecionado
EditText text = (EditText) ndViewById(R.id.tNomeCarro);
Inageview img = (lmageview) ndViewById(R.id.imgFotoCarro);
tet.setText(none);
Picasso.with(this).load(url_foto).into(ing);
}

Para mostrar a foto do carro, a activity utiliza a biblioteca Picasso' logo adicione
essa dependncia no arquivo app/build.gradle conforme zemos no projeto dos
carros. Como preciso acessar a internet ar af oto
p a mostrar f do carro, configure
a permisso no arquivo AndroidManifest.xml.
Captulo 20 I Intents 579
AndrodManifest.ml
<manifest . . . />
<uses-permission androd:name="androd.permssion.INTERNET" />
<applcaton ... />
</manfest>

Ao executar esse projeto no emulador, voc ver uma tela com dois botes. Ao
clicar no boto Mostrar(arro pelo Nome, a ao ACTION_VIEN com a Ur "carros://br.com.Iivroandroid.
carros/carros/Ferrari FF" ser disparada. O resultado ser como a gura 20.2. Veja que
interessante! Uma aplicao enviou uma mensagem para outra!

i Hello world!

MOSTRAR CARRO PELO NOME


l

l sEi_ec|oNAR um cARRo

Nome do carro selecionado 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 V1 2. Se trata de um modelo
GT de quatro lugares que no s substitui a
612 rnastambm
ar um tnoxo
ai i ipo
t' de
cliente. daquele que gosta de percorrer
caminhos mais dificeis que exigem trao
integral Este modelo revolucionrio (dentro
da marca) tem um novo chassi com entre
eixos maior, alem de suspenso i
independente que incorpora a ultima
gerao de amortecedores ajustaveis, alm
de freios de ceramica da Brembo.

Figura 20.2 - Intent customizada para abrir um carro.

E mais interessante ainda o segundo exemplo, que dispara a intent com a ao


ACTION_PICK com a Ur "carros://br.com.livroandroid.carros/carros". Neste caso, a aplicao dos
carros mostra a lista para o usurio selecionar um carro. O carro selecionado
enviado como retorno para a activity conforme mostra o fluxo da gura 20.3.
Esse exemplo demonstrou como integrar diferentes aplicaes, e zemos a mesma
coisa que alguns aplicativos nativos fazem; portanto, agora voc sabe como tudo
funciona internamente.

Nota: qual mtodo utilizar: startActvty ou startActvityForResult? Na maioria


das vezes vamos utilizar o mtodo startActvity para navegar entre as telas da
aplicao, pois geralmente no estamos interessados em ler um retorno. Porm,
ein casos especficos nos quais preciso obter um retorno da activity, devemos
utilizar o mtodo startActvtyForResult.
580 Google Android - 4 edio

-,l\;i'i'^
*-
'
{u._w.\."' _, ,
i
i . . srrun cnno Pato Nous
Mozsrnm carmo esto nous ^ i "
3 oww: um mano
,ser, .rscroiw
. _ ' l um cnuano
Ford <: . sensor
Mustang197
't.:fi'~...i,..;=v1~tz`.\\it\ 1 i M _ V, ,___ _ .- -.-_

<: .
Figura 20.3 - Intent customizacla para selecionar um carro.

20.9 Vericando se uma intent ser encontrada


Sempre que voc disparar uma intent implcita, que geralmente corresponde a
uma ao eustomizada, recomendado vericar antes de enviar a mensagem se
existe alguma aplicao que consegue trat-la. Isso preciso porque, caso seja
disparada uma intent e nenhuma aplicao consiga trat-la, vai acontecer um erro
em tempo de execuo com a seguinte mensagem no LogCat:
No Activity found to handle Intent {descrio da intent aqui}

Faa o teste! Dispare uma intent como essa e veja o erro que vai acontecer.
startActivity(new Intent("NAO EXISTE"));

Para vericar se a intent pode ser tratada por alguma aplicao, podemos utilizar
a seguinte classe:

IntentUtiIs.java
package livroandroid.lib.utils;
public class IntentUtils {
public static boolean isAvailable(Context ctx, Intent intent) {
List list =
nal PackageManager mgr = ct.getPackageManager();

return list.size() > 0; _


ngr.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT ONLY)~

l
}
Captulo 20 I Intents 531
Assim, antes de disparar uma intent, o recomendado sempre vericar se ela
pode ser tratada, conforme mostra este cdigo:
public void onCiickSe1ecionarCarro(View view) {
Uri uri = Uri.parse("carros://br.con.1ivroandroid.carros/carros");
Intent intent = new Intent(Intent.ACTION_PICK, uri);
if(IntentUti1s.isAvai1ab1e(this,intent)) {
startActivityForResu1t(intent, 99);
}

20.10 Interceptando aplicaes nativas


O Android apresenta uma arquitetura orientada a mensagens, e podemos intercep~
tar vrias mensagens nativas. Por exemplo, podemos interceptar uma mensagem
quando um SMS entregue, o Wi-Fi ligado ou desligado, um novo dispositivo
Bluetooth encontrado etc. Ao longo do livro, vamos estudar mais desses exem
plos, e voc vai acostumando-se com a ideia.
Mas por ora gostaria apenas de explicar mais um recurso interessante do Android.
possvel substituir uma aplicao nativa do sistema operacional. Para isso, basta criar
uma activity que declare exatamente a mesma ao que ela precisa para ser disparada.
Por exemplo, no aplicativo dos carros tratamos a seguinte mensagem:
Uri uri = Uri.parse("carros://br.conlivroandroid.carros/carros");
Intent intent = new Intent(Intent.ACTION_PICK, uri);

Foi denido o atributo <data android:scheme="carros" /> no arquivo de manifesto,


assim essa intent vai entender as mensagens que comearem por carros://. Mas
como podemos tratar uma mensagem que enviada para o browser?
Intent intent = new Intent(Intent.ACTION_VIEw, Uri.parse("http://goog1e.com"));
startActivity(intent);

Se voc j entendeu, sabe que basta declarar um ltro com o <data android : schene="http" />,
conforme demonstrado a seguir.
<intent-1ter>
<action android:nane="android.intent.action.VIEw" />
<category android:name="android.intent.category.DEFAULT" />
<data android:schene="http" />
</intent-1ter>
582 Google Android - 4 edio
__ .--
,~_.
, . a,
z ~sia
ualaodis arara
a licato de
laaa o teste! be voce declarar esse filtro em qualquer activity L , P
. - , ,I ` . i ~ z resultado.
llltllt para abrir o browser. o Android vai lbe perguntar com Cl P S*
browser voce descia abrir o conte udo. /\ figura 20.4 mostrt o

i Ligar para telefone

Discar para telefone

Enviar Email

Enviar SMS

Abrir Browser

Mapa - Lat/Lngl l Teste


Mapa - Endeieco
. Browser

Mapa - Rota

Figura 20.-l - Duas aplicaes que podem ser 0 browser

Bem, antes que voc pergunte, no e possvel dar prioridade para nenhuma apli
cacao. Sendo assim, sempre que existirem duas ou mais aplicaes que podem
tratar a mesma mensagem, o Android vai perguntar ao usuario para decidir qual
aplicacao ele deseja abrir. Lembre-se: e o usuario que manda no celular dele, e
nao voc. Para dar outros exemplos, possivel interceptar o botao que cbama a
activity que faz a ligao.
<ntent-lter>
<acton androd:name="androd.ntent.acton.CALL_BUTTON" />
<category androd:name="android.ntent.category.DEFAULT" />
</ntent-1ter>

(u ptxlenios interceptar a aricaela CHlCF,&llll(dC Cfblflll aricaca


turbinada de ca mera.
<intent-1ter>
<acton androd:name="androd.meda.action.IMAGE_CAPTURE" />
<category androd:name="androd.ntent.category.DEFAULT" />
</ntent-lter>

'iztmizm podemos interceptar o toque no boto lrlome, e criar uma tela inicial
ttaliiieiite (llS()llll71Ll.
Captulo 20 1 Intents 533
<ntent-1ter>
<acton androd:nane="androd.ntent.acton.MAIN" />
<category androd:nane="android.ntent.category.HOME" />
<category androd:nane="android.ntent.category.DEFAULT" />
</intent-1ter>

Insira esses ltros em alguma activity para testar e veja que interessante. Natu
ralmente, isso serve apenas para voc entender o conceito da plataforma, pois
necessrio um motivo muito bom para substituir uma aplicao nativa.

20.11 Lendo cdigo de barras


Para brincar um pouco mais com intents e a integrao com outra aplicao, vamos
criar um exemplo para ler um cdigo de barras. Como a implementao desse tipo
de aplicao no trivial, podemos simplesmente chamar um aplicativo especialista
no assunto a m de apenas obter o resultado da leitura do cdigo de barras.
Essa aplicao de leitura de cdigo de barras pode ser encontrada no Google Play
com o nome Barcode Scanner; depois de instalada, j podemos enviar mensagens
para ela. Ela construda com a biblioteca ZXing (https://github.com/zxing/zxing),
que contm cdigo aberto e utilizada por quase 100% de todos os aplicativos
que precisam fazer leitura de cdigo de barras. Muitos desenvolvedores pegam o
cdigo-fonte dessa biblioteca e o customizam e utilizam dentro do prprio aplicativo.
Para continuar o exemplo a seguir, certique-se de instalar o aplicativo Barcode
Scanner em seu celular. Segue link do Google Play:
https://play. google. com/store/apps/details ?id=com. google.zxing.client. android
A m de brincar com a leitura de cdigo de barras, crie um novo projeto que vai
conter apenas um simples boto na tela, que vai chamar a aplicao do Barcode
Scanner para fazer a leitura. Se preferir, abra o projeto HeIIoBar(odeReader disponvel
nos exemplos do livro.
A seguir temos um exemplo do mtodo que dispara a intent:
public void onC1ck(Vew v) {
Intent intent = new Intent("con.goog1e.zxng.c1ent.androd.SCAN");
startActvityForResu1t(intent, 0);
}

Um jeito melhor ainda de disparar essa intent verificar se o aplicativo est ins
talado. Caso no esteja, vamos abrir o Google Play para o usurio instalar. Veja
que tudo feito por intents.
534 Google Android - 4 edio
public void onClick(View v) {
Intent intent = new Intent("con.google.zing.client.android.SCAN");
if(IntentUtils.isAvailable(contet,intent)) {
startActivityForResult(intent, O);
} else {
Toast.nakeTet(contet,"Instale o app Barcode Scanner",Toast.LENGTH_SHORT).show();
intent = new Intent(Intent.ACTION_VIEw);
intent.setData(Uri.parse("narket://details?id=com.google.zing.client.android ));
startActivity(intent);
}

Para essa intent funcionar, declare a permisso do ZXing no manifesto:


<uses-permission android:nane="con.google.zxing.client.android.SCAN" />

Dessa forma a aplicao de leitura de cdigo de barras ser executada e, ao apontar


a cmera para um cdigo de barras vlido, ele ser lido e identicado. Recomendo
voc testar com QR Code, pois cdigos de barra 2D (exemplo: boleto) requerem
algumas customizaes para serem lidos. Para gerar um cdigo de barras a m de
testar este exemplo, voc pode utilizar este link http://zxing.appspot.com/generator/_
Feito isso, o nmero ser retornado para a aplicao de origem por meio do m
todo onActivityResult(codigo, resultado,intent).

public void onActivityResult(int requestCode, int resultCode, Intent intent) {


if (requestCode == 0) {
if (resultCode == RESULT_0K) {
String numero = intent.getStringEtra("SCAN_RESULT");
// Este o nmero do cdigo de barras
}

Deixo como exerccio para voc construir esse aplicativo, ou caso prera abra o
projeto de exemplo deste captulo. O nome do projeto HeIIoBarCodeReader.

Nota: o recurso de enviar mensagens para todas as aplicaes instaladas e ainda


ler o resultado torna o desenvolvimento de aplicaes para o Android muito
flexvel, facilitando muito a integrao entre diferentes aplicaes, uma vez que
uma pode chamar a outra com facilidade.
Captulo 20 . Intents 535
20.12 Nomenclatura das intents
Repare que uma das intents que utilizamos anteriormente tinha a seguinte ao:
br.con.1ivroandroid.intents.TESTE

E no exerccio sobre cdigo de barras vimos que a intent do ZXing :


com.goog1e.zxing.client.android.SCAN

Este breve tpico serve apenas para alert-lo da importncia do padro de nomes de
uma intent. Por conveno, o nome do pacote da aplicao sempre colocado antes
da ao, com o objetivo de diferenciar as intents de diferentes aplicaes. No apli
cativo dos carros mostramos como abrir uma activity com base na seguinte intent:
// Abre o carro pelo none
Ur uri = Uri.parse("carros://br.com.1ivroandrod.carros/carros/Ferrari FF");
Intent intent = new Intent(Intent.ACTION_VIEw, ur);
startActivity(ntent);
Neste caso estamos utilizando a constante Intent.ACTION_VIEII de uma ao nativa
do Android, mas essa ao tambm segue a mesma conveno, pois sua string
android.ntent.acton.VIEw.

20.13 Links teis


Para continuar seus estudos sobre a classe Intent, separei alguns links da docu
mentao ocial.
API Guides - Intents and Intent Filters

http://developer android.com/guide/components/intents-lters.html
API Guides - Common Intents

http://developer android.com/guide/components/intents-common.html
Android Training - Sending the User to Another App

http://developer android.com/training/basics/intents/sending. html


Android Training - Allowing Other Apps to Start Your Activity

http://developer android.com/training/basics/intents/lters. html


cAPiruLo 21

Multimdia - udio, vdeo e


`*_` cmera

Neste captulo, vamos estudar os recursos de multimdia disponveis no Android, como


mostrar vdeos, reproduzir udio e utilizar a cmera para tirar fotos e gravar videos.

21.1 Formatos de udio e vdeo suportados

,8
Segundo a documentao ocial do Android, os seguintes formatos so suportados
ao desenvolver as aplicaes.
udio - mp3, midi, 3gp, ogg, m4a, wav e ac.
Vdeo - mp4 e 3gp.
Os protocolos de comunicao suportados so HTTR HTTPS e RTSR O Real Time
Streaming Protocol (RTSP) extremamente recomendado no caso de streaming
de vdeos. Para mais informaes consulte o site ocial:
http://developerandroid.com/guide/appendix/media-formats. html

21.2 Classe Media Player


Vamos comear este captulo brincando um pouco e criandoium player de mp3.
Para isso, vamos utilizar a classe androd.meda.MedaP1ayer, que responsvel por
reproduzir qualquer recurso de mdia, como udio e vdeo.

- no e so
Tocar uma msica simples, basta congurar a origem do arquivo com al uma
verso do metodo setDataSource(...), chamar o mtodo prepare() a m de prepa
rar a mdia e na sequncia o mtodo start() O mtodo prepare() sncro '
retorna quando o arquivo estiver pronto para reproduo. Depois que a mdia

586
Captulo 21 I Multimdia - udio, vdeo e cmera 587
estiver pronta para ser reproduzida, basta chamar o mtodo start() para iniciar
a msica ou vdeo.

O exemplo a seguir mostra como tocar uma msica a partir de um caminho de


arquivo conhecido.
MediaPlayer player = new MediaPlayer();
player.setDataSource(Uri.parse("/storage/sdcard/Music/lnkin_park1.mp3"));
player.prepare();
player.start();

Veja que sempre necessrio chamar a sequncia de mtodos setDataSource( . . .) >


prepare() > start(). Para simplicar esse uxo, existe o mtodo MedaPlayer.create(. . .),
que um atalho que automaticamente congura a dataSource e chama o prepare().
Portanto, basta voc chamar o mtodo start().
MediaPlayer player = MediaPlayer.create(ths, Uri.parse("httpz//site.com.br/musica.
I1D3"));
player.start();

O mtodo MedaPlayer.create( . . .) til no caso de voc precisar tocar uma msica


que est na /res/raw do projeto, pois internamente esse mtodo encapsula o cdi
go necessrio para congurar a dataSource. O exemplo de cdigo a seguir mostra
como tocar a msica /res/raw/musica.mp3.
MediaPlayer player = MediaPlayer.create(ths, R.raw.musca);
player.start();
Depois que a msica estiver tocando, possvel chamar o mtodo stop() para parar a
reproduo ou o pause() para fazer apenas uma breve pausa. Se o mtodo pause() for
chamado, a msica interrompida, mas pode ser reiniciada normalmente mais tarde.
Para reinici-la, basta chamar o mtodo start() novamente. O mtodo stop() deve ser
chamado para parar completamente a reproduo da msica. Se depois da chamada
do mtodo stop() voc quiser voltar a reproduzir a msica, necessrio reiniciar o
MedaPlayer. Para isso, o mtodo reset() deve ser chamado, fazendo com que o MedaPlayer
volte a seu estado original. Consequentemente, os mtodos setDataSource(arquvo),
prepare() e start() precisam ser chamados novamente para reproduzir o arquivo.
O ltimo mtodo que deve ser explicado o release(), o qual deve ser chamado
para liberar a memria e os recursos do MediaPlayer quando no for mais neces
srio usar essa classe. Se voc tem certeza de que depois do stop() no voltar a
reproduzir a msica, pode chamar o release().
Para assegurar-se de que o mtodo release() ser chamado, possvel cham-lo no
mtodo onDestroy() da activity para garantir que a reproduo da msica ou vdeo
tenha sido encerrada e os recursos liberados, conforme demonstrado a seguir:
588 Google Android - 4 edio
@0verrlde
protected void onDestroy() {
super.onDestroy();
if (player != null) {
player.stop();
player.release();
}

A gura 21.1 mostra o diagrama de estado da classe MediaPlayer. Observe atenta


mente a ordem em que os mtodos devem ser chamados:
Fonte: http://developexandroid.com/reerence/android/media/MediaPlaye1:html

/' relcaseO 'N d _ `


rc|etO ~,\____v _J_,,-..-~~' .._....,.. ft
utvatasourceo OnEn'orListenor. onE1rorO ' l
_ .e \ Error
~\_"_".`-M., J m.~4 ,_,z'/
, .ff "W '""""`*\
\ pr epreA|ync0 z~-'"
\ Pfwmm e Izmzzza
OnPreparodI.istencr.onPreparedO prpo

dl/f mw
K Prepared N)
Stern zzmo
prop|reA:yncO V. A
.` ' r /-f"""` "`~\ __ _ \
Q }~F>.n~h. v_r (L Started Iioyl/1giTu,ue&&
} AePk,[.OOm`nt.\_"M_ " r PIY ac completo:
r- ,J,slopg
-` lldx
igzmf1
_lll.Q
1 * `
.S1StPPd
` 1.xI E,/"
EQ " ,, _
_;_`:cckTo _\\
O/p
df' as s e Pmucd
aus:O
I-Oping = false &&
`\ _ onCompleonO invoked on
:topO \. \ 1 strtO
\ OnComplet1onL\sten?r 1
\\\ (note. lonx begrmnm)

l s' " """'~- |eokToO


Q>;bzz1zc:z~zzz1zQ ` \

Figura 21.1 - Diagrama com os mtodos do MediaPlayen


(aPtuIo 21 n Multimdia - udio, vdeo e cmera 539
21.3 Criando um player de mp3
Para o prximo exemplo, criaremos o projeto PIayerMp3.
Antes de escrever o cdigo da activity que exibir a tela para o usurio, criaremos
uma classe chamada PlayerMp3, que encapsular o acesso classe MediaPlayer para
controlar a reproduo das msicas e facilitar o exemplo.

PIayerMp3

public class PlayerMp3 implements OnCompletionListener {


private static nal String CATEGORIA = "livroandroid";
private static nal int NOVO = O;
private static nal int TOCANDO = 1;
private static nal int PAUSADO = 2;
private static nal int PARADO = 3;
// Comea o status zerado
private int status = NOVO;
private MediaPlayer player;
// Caminho da msica
private String mp3;
public PlayerMp3() {
// Cria o MediaPlayer
player = new MediaPlayer();
// Executa o listener quando terminar a msica
player.setOnCompletionListener(this);
}

public void start(String mp3) {


this.mp3 = mp3;
try {
switch (status) {
case TOCANDO:
player.stop();
case PARADO:
player.reset();
case NOVO:
player.setDataSource(mp3);
player.prepare();
case PAUSADO:
player.start();
break;
}
status = TOCANDO;
590 Google Android - 4 edio
} catch (Exception e) {
Log.e(CATEGORIA,e.getMessage(),e);
}

public void pause() {


player.pause();
status = PAUSADO;
}

public void stop() {


player.stop();
status = PARADO;
}

// Encerra o MediaPlayer e libera a memria


public void close() {
stop();
player.release();
player = null;
}

/~k*

* @see android.media.MediaPlayer.0nCompletionListener#onCompletion(android.media.MediaPlayer)
*/
public void onCompletion(MediaPlayer mp) {
Log.d(CATEGORIA, "Fim da msica: " + mp3);
}

public boolean isPlaying() {


// Retorna true se a msica est tocando ou se est em PAUSE
return status == TOCANDO || status == PAUSADO;
}

Observe que essa classe controla o estado de reproduo da mdia. Para isso, o
atributo status da classe utilizado, de modo que a sequncia correta dos mtodos
seja chamada, respeitando o diagrama de estados da classe MediaPlayer. Para tal, o
mtodo start(mp3) verica o estado atual antes de iniciar a reproduo.
O prximo passo criar o arquivo de layout, que contm um campo de texto
com o caminho do arquivo mp3 e os botes start, pause e stop para controlar o udio.

[] /res/layout/activity_main.mI
<?xml version="1.G" encoding="utf-8"?>
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match parent"
android:orientation="vertical" android:padding="16dp">
Captulo 21 I Multimdia - udio, vdeo e cmera 591
<TetView

android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Player MP3" />
<EditText
android:id="@+id/arquivo"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:text:"/storage/sdcard/Music/linkin_park1.np3" />
<LinearLayout
android:layout_width="match_parent" android:layout_height="wrap_content" >
<ImageButton
android:id="@+id/start"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/img_start" />
ImageButton
android:id="@+id/pause"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/img_pause" />
ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content" android:layout_height="wrap_content
android:src="@drawable/ing_stop" />

A seguir, podemos visualizar a activity que usar esse arquivo de layout e tratar
os eventos dos botes para tocar a msica. Todo o trabalho, na verdade, delegado
para a classe PlayerMp3 que criamos anteriormente.

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.0nClickListener {


private static nal String TAG = "livro";
private PlayerMp3 player = new PlayerMp3();
private EditText text;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
text = (EditText) ndViewById(R.id.arquivo);
ndViewById(R.id.start).set0nClickListener(this);
ndViewById(R.id.pause).setOnClickListener(this);
ndViewById(R.id.stop).setOnClickListener(this);
}
592 Google Android - 4 edio
public void onClick(View view) {
try {
if (view.getId() == R.id.start) {
String mp3 = tet.getText().toString();
p1ayer.start(np3);
} else if (view.getId() == R.id.pause) {
p1ayer.pause();
} else if (view.getId() == R.id.stoD) l
p1ayer.stop();
}

} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Toast.makeTet(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}

@Override
protected void onDestroy() {
super.onDestroy();
// Libera recursos do MediaPlayer
p1ayer.c1ose();
}

Neste exemplo, o mtodo onClick(view) chamado para tratar o evento de quando


o usurio clica em algum boto da tela. Nesse mtodo, o caminho do arquivo mp3
digitado no formulrio da tela recuperado para tocar a msica. Ao executar a
aplicao, a tela ser visualizada conforme a gura 21.2, e voc pode usar os botes
de play, pause e stop para tocar o mp3. A figura ao lado do player mp3 mostra um
print da janela File Explorer do Android Studio, pois este exemplo l o arquivo do
SD card. Certique-se de colocar os arquivos de mdia no SD card do emulador
connmindkado

Nota: neste exemplo, se voc tocar a msica e fechar a tela da activity, o P1ayerMp3
ser encerrado, pois ele est atrelado ao ciclo de vida da activ-ity. No captulo

`dd
27, sobre a classe Service, vamos aprender a executar processos em segundo
plano que podem car vivos no sistema operacional do Android. Utilizando
esses servios e segun o plano, a msica continuar executando mesmo ao
fecharaaphcao.
Captulo 21 I Multimdia - udio, vdeo e cmera 593

Pizzyzzf MP3

||nkn__park1 .mp3
lfstorge/sdcard/Musc/ z
b M D * Q_=:sdc:ard
' P IB Alarms
* E Android
Hocim
i > EB Download
1 V IB LOSIDIR
l

z ' Qi? Movies


I last__mohicans.3gp
5 ' (55 Music
- lnkin_park1.mp3

Figura 21.2 - Exemplo completo para tocar a msica.

21.4 Reproduzindo vdeo com a classe VideoView


A classe MedaP1ayer consegue reproduzir qualquer arquivo de mdia, seja udio
ou vdeo. O vdeo, porm, precisa ser renderizado em alguma view da tela, e para
simplicar esse trabalho foi criada a classe Vdeovew, que uma subclasse de View,
portanto, basta inseri~la no layout para mostrar o vdeo. A classe Vdeovew encap
sula o acesso ao MedtaP1ayer e de resto ela igual.
Para congurar o contedo do vdeo, ou seja, qual arquivo ou URL que ser
mostrado, voc pode utilizar os mtodos setVdeoPath(strng) ou o mtodo
setVideoURI(ur). Para demonstrar a utilizao do Vdeovew, vamos criar um exemplo
idntico ao PIayerMp3, mas desta vez vamos chamar de PIayerVideo. Para continuar o
exerccio, crie um projeto chamado PIayerVideo com o seguinte arquivo de layout:

/res/Iayout/actvity_main.xml
<?ml verson="1.G" encodng="utf-8"?>
<LnearLayout androd:orentaton="vertca1" . . .>
// EdtText para digitar o caminho do vdeo aqui
// Botes de play, pause e stop aqui
// Por ltimo o Vdeovew
<VdeoVew android:id="@+d/VdVeW"
androtd:1ayout_width="match_Df"t"
androdjayout_heght="0dp" androd:layout_weght="1" />
</LnearLayout>
594
_ . _ , ' ' ' a dife Google Android - 4 edio

. . . . - ' strar o vdeo.


Estou simplicando o layout, pois ele e o mesmo do exemplo Uf1'10f-A mc
rena que o Videoview foi inserido abaixo dos botes de play, P3U5@ $P- N0 Cd1g0 da
activity vamos recuperar o Videoview pelo id e chamar os metodos para ITIO

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.0nClickListener {


private static nal String TAG = "livroandroid";
// Classe que encapsula o MediaPlayer
private Videoview videoview;
private Editlext text;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
text = (EditText) ndViewById(R.id.arquivo);
videoview = (VideoView) ndViewById(R.id.videoView);
videoview.setHediaController(new MediaController(this))
ndViewById(R.id.start).set0nClickListener(this);
ndViewById(R.id.pause).setOnClickListener(this);
ndViewById(R.id.stop).set0nClickListener(this);
}

public void onClick(View view) {


try {
if (view.getId() == R.id.start) {
String path = text.getTet().toString();
videoview.setVideoURI(Uri.parse(path));
videoView.start();
} else if (view.getId() == R.id.pause) {
videoView.pause();
} else if (view.getId() == R.id.stop) {
videoview.stopPlayback();
}

} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Toast.nakeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}

@0verride
protected void onDestroy() {
super.onDestroy();
Captulo 21 1 Multimdia - udio, vdeo e cmera 595
// Libera recursos do MedaP1ayer
vdeoview.stopP1ayback();
}

A gura 21.3 mostra o resultado com o vdeo executando, mas lembre-se de


coloc-lo no SD card do emulador. O interessante do cdigo o mtodo
setMediaContro1er(controller), o qual adiciona os controles da reproduo do vdeo,
que podem ser vistos sobre o Videovew na parte inferior da tela.

Player MP3 do sdcard


/storage/sdcard/Movies/'
Iast_mohicans.3gp V , Sdcard
V
HH U >zAlarms
V U5 Android

U* IE DCIM
U* EE: Download
l

P E5 LQST.DlR
. ' E.= Movies
l

last_mohicans.3gp
"E3hdusic
linkin_park1.mp3

Figura 21.3 - VideoView.

21.5 Utilizando uma intent e o player de vdeo nativo


j sabemos que para mostrar um vdeo podemos utilizar a classe Videovew, mas
para isso necessrio criar um layout, criar uma nova activity e fragment etc. Mas
por que no mostramos o vdeo disparando uma simples intent?
Mais uma vez, as intents vo dar o ar de sua graa, pois a maneira mais simples
de mostrar um vdeo no Android disparar uma intent para o player de vdeo
nativo. Por exemplo, se voc conhecer o caminho do arquivo, URL ou URI do v
deo, basta disparar a seguinte intent, que ser tratada pelo player de vdeo nativo.
Uri ur = Uri.parse(ur1);
Intent intent = new Intent(Intent.ACTION_VIEw);
intent.setDataAndType(ur, "video/*");
context.startActvty(ntent);
596 Google Android - 4 edio
, , . . - -z z _ ando o
Pronto! Tem algo melhor do que mostrar o vdeo no playf HHVQ d0 A<1fi<1?^
vantagem e que o usuario inclusive Ja esta acostumado com 0 player Qu
vdeo terminar, sua aplicao volta a executar e pronto. Simples 8551m
No projeto dos carros, vamos brincar um pouco e por motivos de aprend1Z2lCl0
ofereceremos ao usurio trs opes para tocar o vdeo:
1. Mostrar o vdeo no browser.
2. Mostrar o vdeo no player nativo do Android.
3. Abrir uma nova activity com 0 Vdeovew.
O fragment que mostra os detalhes do carro apresenta os botes Vdeo e Mapa na
action bar. Ao tocar no boto Vdeo, vamos mostrar uma alerta com essas trs
opes. Para isso, vamos utilizar a classe androd.wdget.PopupMenu. A vantagem da
classe PopupMenu que ela mostra um alerta na posio de determinada vievsg sendo
que essa view chamada de ncora. Neste caso 0 alerta ser mostrado ao lado do
boto Vdeo da action bar. As opes do alerta podem ser definidas em um arquivo
de menu, pois a classe PopuMenu vai inar esse arquivo para criar a lista.
Agora volte ao projeto dos carros e faa estas alteraes:

/res/menu/menu_popup_video.xmI
<menu m1ns:androd="http://schemas.android.com/apk/res/android"
m1ns:app="http://schemas.androd.com/apk/res-auto">
<tem android:id:"@+id/action_vdeo_browser"
android:title:"@strng/acton_video_browser" app:showAsActon="a1ways" />
<item androd:id:"@+id/acton_vdeo_p1ayer" android:title:"@string/action_vdeo_p1ayer"
app:showAsAction="a1ways" />
<tem androd:id:"@+d/acton_vdeo_videoview"
android:title:"@string/acton_vdeo_vdeoview" app:showAsActon="a1ways" />

/res/values/strings.xmI
<?xm1 verson="1.0" encoding="utf-8"?>

<string name:"acton_video_browser">Browser</strng>
<strng name="action_video_p1ayer">Vdeo P1ayer
<string name="acton_vdeo_videovew">vd@Qvew</5tring>
Captulo 21 I Multimdia - udio, vdeo e cmera 597
Agora que criamos o arquivo de menu, vamos alterar o cdigo que trata o
evento da action bar para, ao selecionar o item Vdeo, mostrar as opes para o
usurio com a ajuda da classe PopupMenu. Observe que o cdigo utiliza a classe
android.support.v7.widget.PopupMenu da biblioteca de compatibilidade.

(arroFragment.java

import android.support.v7.widget.PopupMenu;
import livroandroid.lib.utils.IntentUtils;
public class CarroFragment extends BaseFragment {

public boolean onOptionsItemSelected(Menultem item) {

} else if (item.getItemId() == R.id.action_video) {


// URL do video
nal String url = carro.urlVideo;
// L a view que a ncora do popup ( a view do boto da action bar)
View menuItemView = getActivity().ndViewById(item.getItemId());
if (menuItemView != null && url != null) {
// Cria o PopupMenu posicionado na ncora
PopupMenu popupMenu = new PopupMenu(getActionBar().getThemedContext(),
menuItemView);
popupMenu.inate(R.menu.menu_popup_video);
popupMenu.set0nMenuItemClickListener(new PopupMenu.0nMenuItemClickListener() {
@0verride
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.action_video_browser) {
// Abre o video no browser
IntentUtils.openBrowser(getContet(), url);
} else if (item.getItemId() == R.id.action_video_player) {
// Abre o video no Player de Video Nativo
IntentUtils.showVideo(getContet(), url);
} else if (item.getItemId() == R.id.action_video_videoview) {
// Abre outra activity com Videoview
}
return true;
}

});
popupMenu.show();
}

}
return super.onOptionsItemSelected(item);
}

}
598 Google Android - 4 edio
, . . . - ' ' ndroid-utils
.-_oe
Para o codigo compilar, importe a classe Intenttils da biblioteca a ,
strar o vdeo no
pois ela contm os mtodos para disparar as intents, a m de mo I d
browser ou no player nativo. Para sua consulta, o codigo fonte ClSS3 C 355 P
ser visualizado a seguir:

IntentUtiIs.java
package livroandroid.lib.utils;
public class IntentUtils {
private static nal String TAG = "IntentUtils";

public static void openBrowser(Contet context, String url) {


try {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEN, uri);
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "openBrowser() - ActivityNotFoundException [\"+url+\"]: " + e.getMessage());
}

public static void showVideo(Contet context, String url) {


try {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEH);
intent.setDataAndType(uri, "video/*");
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "showVideo() - ActivityNotFoundException ["+url+"]: " + e.getMessage());
}

Com essas alteraes, ao executar o projeto e selecionar a ao Vdeo na action bar,


o alerta com as opes ser mostrado conforme a figura 21.4.
Ao selecionar a opo Browser, uma intent disparada para abriro browser. Assim,
o vdeo ser aberto da mesma forma co mo se voc tivesse aberto o browser e
digitado uma URL de um arquivo vdeo no endereo. Na prtica, voc s precisa
utilizar essa opo se no souber ao certoqual
ser o tipo do contedo, ento
voc simplesmente dispara a intent.
Capitulo 21 I Multimdia - udio, vdeo e cmera

Preo de USS 1,5 milho, Ferrari Enzo tem Preo de US S 1,5 milho, Ferrari Enzo tem
fascinado muitos amantes do desporto fascinado muitos amantes do desporto
automvel em todo o mundo. O carro possui automvel em todo o mundo. O carro possui
um motor de 12 cilindros. Apenas 399 um motor de 12 cilindros. Apenas 399
carros foram produzidos sempre e Michael carros foram produzidos sempre e Michael
Schumacher no se esquea de comprar Schumacher no se esquea de comprar
um. A tecnologia de Frmula 1 fez este lindo um. A tecnologia de Frmula 1 fez este lindo
carro de luxo uma escolha favorita entre os carro de luxo uma escolha favorita entre os
amantes de carros Ferrari. Caracteristicas amantes de carros Ferrari. Caracteristicas
como a aerodlnmlca poderosa, o corpo de como a aerodlnmlca poderosa, o corpo de
bra de carbono e os discos de freio de fibra de carbono e os discos de frelo de
cermica de carbono torna-lo especial entre cermica de carbono torn-lo especial entre
todos os carros de luxo. todos os carros de luxo.

Figura 214 - Alerta com opes do vdeo.

Mas caso saiba que se trata de uma URL de vdeo, a opo do player de vdeo
nativo mais interessante, pois dispara uma intent que ser tratada pela aplicao
nativa de vdeo. Vale relembrar que, se mais de uma aplicao conseguir tratar a
mensagem, o Android vai perguntar ao usurio qual aplicao deve executar. A
gura 21.5 mostra um vdeo sendo tocado no player nativo do emulador.

Figura 21.5 - Player de vdeo nativo.


600 Google Android - 4 edio

. . - _z .' f^ .'cisar
" ' ` arar uma intent e
21.6 Utilizando o VideoView no projeto dos carros

- , zmostrar o vdeo
A forma mais simples de mostrar um video na apllC21a0 6 d1SP
- -- ' w.
utilizar o player nativo. Se, porem, por algum motivo voce Pre
dentro do layout da apl1caao,e necessario utilizar a classe V1d@0V1
Como j estudamos a classe Videoview, vamos s exercitar o conceito mais uma
vez e criar uma nova activity e fragment para tocar o vdeo do carro. Crlc uma
activity conforme demonstrado a seguir. Note que ela recebe o ob]eto carro por
parmetro e delega o trabalho para um fragment.

VideoActivty.java
package br.com.livroandroid.carros.activity;
import android.support.v4.app.NavUtils;

import br.com.livroandroid.carros.fragments.VideoFragment;
public class VideoActivity extends BaseActivity {
private Carro carro;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video);
// Congura a Toolbar como a action bar
setUpToolbar();
carro = (Carro) getIntent().getSerializableEtra("carro");
getSupportActionBar().setTitle(carro.nome);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (savedInstanceState == null) {
// Adiciona o fragment ao layout da activity
VideoFragment videoFragment = new VideoFragment();
videoFragment.setArguments(getIntent().getEtras());
getSupportFragmentManager().beginTransaction().replace(R.id.fragLayout,
videoFragment).commit();
}

@0verride

public boolean on0ptionsItemSelected(Menultem item) {


switch (item.getItemId()) {
case android.R.id.home:

Intent intent = NavUtils.getParentActivityIntent(getActivity());


'intent.putExtra("carro", carro);
Navtils.navigateUpTo(getActivity(), intent);
return true;
Captulo 21 I Multimdia - udio, vdeo e cmera 601
}

return super.on0ptonsItemSe1ected(tem);
}

Lembre-se de que todas as activities precisam ser conguradas no manifesto. Re


pare que, no caso desta activity do vdeo, estou congurando a activity pai como
CarroActvty, e no ManActivty.

AndroidManifest.xmI
<app1icaton . . .>

<actvty androd:name=".actvity.VideoActivty"
androd:label:"@string/tt1e_actvty_vdeo"
androd:parentActvtyName=".activity.CarroActvty" >
<meta-data androd:name="androd.support.PARENT_ACTIVITY"
android:va1ue=".activty.CarroActivity" /
</actvty>
</applcaton>

No cdigo da activity perceba que estou interceptando a ao do boto voltar do


up navigation (androd.R.d.home) e utilizando a classe NavUt1s para voltar para a
activity anterior, que est acima na hierarquia. Lembre-se de que a activity pai da
VdeoActvty a CarroActvity e no a ManActvty. Por padro, o funcionamento do
up navigation no passar nenhum parmetro para a activity pai, e como a classe
CarroActvity recebe o carro por parmetro, precisamos customizar esse retorno.
Entenda que na prtica todo esse cdigo poderia ser substitudo por um simples
nsh(), pois dessa forma, quando a activity do vdeo terminar, a activity do carro
passaria a ocupar o topo da pilha. Propositalmente, mostrei esse cdigo que utiliza
a classe NavUt1s para demonstrar casos mais complexos. Digamos que voc tivesse
a seguinte pilha de activities:
> Main Activity > A > B > C > D > E.

Se voc estiver na activity E, como fazemos para voltar para a activity B? Neste caso, se
utilizarmos o mtodo nsh(), apenas a activity E ser nalizada e a activity D passar
a ocupar o topo da pilha. Mas o que queremos derrubar todas as activities da
pilha, at chegar na B. Utilizamos ento o ag FLAG_ACTIVITY_CLEAR_TOP na intent, mas
a classe NavUt1s contm mtodos justamente para facilitar esse tipo de navegao.
Vamos continuar com o cdigo para mostrar o vdeo do carro. A seguir, podemos
ver o arquivo de layout da activity VdeoActvty, que contm apenas a Toolbar e
um FrameLayout para adicionar o fragment.
602 Google Android - 4 edio
/res/Iayout/attivity_vdeo.xmI
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="mtCh_Pfe"t" >
<include layout:"@layout/include_toolbar" />
<FrameLayout
android:id="@+id/fragLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/fragment_video" />

Nota: lembre-se de que todas as activities precisam incluir a Toolbar no layout.

Mais uma vez, estamos utilizando a activity para controlar a navegao das telas,
mas internamente todo o contedo e lgica cam no fragment. A classe VideoFragment
precisa ler o objeto carro que enviado pelos argumentos e congurar o Videoview
com a URL do vdeo.

VideoFragment.java
package br.com.livroandroid.carros.fragments;
import android.widget.MediaController;

public class VideoFragment extends BaseFragment {


public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_video, container, false);
Videoview videoview = (VideoView) view.ndViewById(R.id.videoView);
Carro c = (Carro) getArguments().getSerializable("carro");
if (c != null) { i
videoview.setVideoURI(Uri.parse(c.urlVideo));

videoView.start(); '
videoview.setHediaController(new MediaController(getContext()));

toast("start: " + c.urlVideo);


}

return view;
}

O arquivo de layout do fragment contm apenas um V_de0Vew_


Captulo 21 n Multimdia - udio, vdeo e cmera 603
/res/layout/fragment_video.xmI

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" >
<VideoView android:id="@+id/videoview"
android:layout_width="match_parent" android:layout_height="match_parent" />

Agora que j criamos a activity e o fragment que vai mostrar o vdeo, volte classe
CarroFragment e altere o trecho de cdigo do PopupMenu cujo alerta inflamos com as
opoes. Na opo do Videoview, vamos navegar para a activity que acabamos de criar.

CarroFragment.java
public class CarroFragment extends BaseFragment {

popupMenu.set0nMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.action_video_browser) { . . .}
else if (item.getItemId() == R.id.action_video_player) { ... }
else if (item.getItenId() == R.id.action_video_videoview) {
// Abre outra activity com Videoview
Intent intent = new Intent(getContext(), VideoActivity.class);
intent.putExtra("carro", carro);
startActivity(intent);

I'`lU` true;
}});
}

Feito isso est pronto. Desta vez, ao selecionar a opo Vdeo e depois o item VideoView,
7

vamos navegar para a activity do vdeo que acabamos de criar. A gura 21.6 mostra
o resultado com o vdeo sendo mostrado no Videoview.

Com relao ao projeto dos carros, isso tudo, pois j mostrei trs maneiras para
mostrar o vdeo do carro. Particularmente, sempre tento convencer meus clientes
de que a melhor opo disparar uma intent e utilizar o prprio player nativo.
motivo simples, pois mais fcil de fazer e o resultado emuito melhor. Mas e
claro que tudo depende do caso. Por exemplo, em uma aplicao para tabletem
que tgmog um grande espao disponvel na tela, muitas vezes opta-se por utilizar
um Videoview, a m de aproveitar melhor o espao das views dentro do layout.
604 Google AndroId 4" !d|

|'|\~.z ,Q | na Q 1 ' \\v'lq_ 1 M1111 I n 'n mm


|v\-nm.I- mn!!-W m\vu\|m In \1"=1*'\\\ _: _
|`|1\m\\pI pm bah' \ |\\|I|\n|\ l\ :IN -\ |'-\*I'~I'\
||\\|\|.u .lal '\I|h\JI\\ ^|'|\\IT PW
-,|.\q hu nm m.|\|;|Inn Ill\|\\|\I| n M|'|\ WI
'~hu~\\4\.~lm nu cf n|\\:\ -iv ^\H 'I'
um A hn~|\\\\|n dl! \|I\\lI|l 1 in* \-z1\lm.|
.~m.\ Ja tmn uma n\W\-a humuin num* -~\
nmmvlvn .In vnlmn I vml 1`mm|mln\I- f
nwnn n |Iw\\|Im'|\\\u fl |\-'-|v*\I\ I"`I"|'1|l'
lun :Iv rmImm\ 0 vv uh-nww min hrk\ ll
.wllm n1m\u|\z~n.\h\|n!\ L~ \\o~In|v\!I
hhlfl U! 1 Il|'1\l Un Imu

ov

\.
!':_\;:n. .Tl. \ |J'1..unm \`:.I.\: u

21 .7 Tirando fotos com uma intent

Sc \`u\ .u h.| qm' |IU*'il 11\'n'f`za ~.l|v ;\- IIIFIII Hmh.: lu ;',.||\ .nn Inn, '~.|.\ *n;'_.1
Ihltl. |\U|H. UHHH \|lf~r' HU \`;I|\Illl|U .IIHt`l'I\I`, h IIIIQ IIIH '~.I\ H s \I`.l\.i \| .'\|HIHI\|
f ~.\ 1|1i||/.u|n uu lmlw. IlI'_.|I'(`?.

I`;n'.| urzn" uma | mn .'\|u||~ml. t`\IF|\`III h|.|~. um


uno uno mn
~ |n' \1.\|z-
l| lum
~
* /z . .
/\|WI`III\L`IIl ~||l|||,n| .n'|z1' .-v .m1|oid.h.uIw.n~.( nml
l l,| | .\ |
,\
l,\'. \'11\ ., n\
N'~ln:\=.x*|\*u'~~s;'|||||n||*1m'|\l.n .||;z\||\~.|m~l|w|\| nun II n \ u|\'| In uniu
\`.III\\`I';I. IICIH JC '\IHI`|II F.\`\I ".l;H|n` |I.lg.I | M | ph I-|\I||\| r '\|,' .|\I` \'\I\''
HH! |H\llu) \\\ \|I}'__ Ml II /\IH|II\| II\lIIl;|*. \'\`/l" II H |\|\\ |'. \|\|" \|'- ~|\\'n|\''|

|IIII
llI\| (lu 7t`IH, |HH|`lI\\'- \|N`H.IF IIII`)',I;II .\||I\' Igwz |\I' |.| \'\| I; m |'n|i |||| |

1\' \\7-
| \ '\\ ,~"\'.
. ` \\ .
-';;n|u|.| |_.\ u||-.|=.l\ mu ||f.|.n;n num IIIIFHI |n|| 1 \||. K '|. H mv; .I |. 1

'`|1| v\ \|1`
|m|.|, qm \.| = m.|||;_.|| | || u;||;|I| \~-.,|.|., .|. .W `|. ,.. MH
n.\ | .1 IN |\|\um.| .I|)||\.I\`.I\ nu llnn ,||, m || ||. |u~ \|\||n|| II mm In
/\m||'||. .m 11-'.u|||.\|,;\ ;| vlu. mm|\1||u~.\|~\~<I\-~
| I\HH|I ... |(,|.~.,`|.,
lI|H H|l|'|.||_ |III| !`||z^|H.I HIILI IIII.I)'t`III Hu /\I\|||\|

NJ |\I;/IIH ;I.t|I'|);II.lI lIlII;| IH|l`IlI H IU u|||~|||_|,_I HJ \\ z |l|\' U ||-.||,\ U; \-| |||_


I ||;|||v|~;im*
;\ I||n.| ' ` H'(|'I|* IN I\
.u \lH\'l\IIl\|l|H|n'\\|||\\
\|l|||/.n'~-.-.;1.|u_.|m"|\||\|_
_ . _ L.. '.
Captulo 21 I Multimdia - udio, vdeo e cmera 605
Para continuar, crie um novo projeto chamado HeIIo(amera ou abra o projeto dis
ponivel nos exemplos do livro.
A seguir, podemos ver o arquivo de layout da activity Teremos um boto que vai
disparar a camera com uma intent e um Imageview que vai receber o resultado da foto.

/res/layout/activity_main.xml
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="ll_parent" android:layout_height="ll_parent"
android:orientation="vertical" android:padding="16dp">
<ImageButton android:id="@+id/btAbrirCamera"
android:layout_width="60dp" android:layout_height="60dp"
android:src="@android:drawable/ic_menu_camera" android:tet="Abrir a Camera" />
<ImageView android:id="@+id/imagem" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_weight="1"
android:layout_gravity="center" />

Veja que o boto que vai disparar a intent est 'congurado com a imagem
android:src="@android:drawable/ic_menu_camera", nativa do Android. A seguir, temos o
cdigo da activity que dispara a intent para abrir a cmera:

MainActivity.java

public class MainActivity extends AppCompatActivity {


private Imageview imgview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mgvew = (lmageview) ndViewById(R.id.imagem);
Imagegutton b = (ImageButton) ndViewById(R.id.btAbrirCamera);
b.set0nClickListener(new View.0nClickListener() {
@0verride
public void onClick(View v) {
Intent 1 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityF0rRSU1t( 0);
l
});
}
606 Google Android - 4 edio
@Override
protected void onActivityResult(int requestCode, int resulttode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null) {
Bundle bundle = data.getEtras();
if (bundle != null) {
// Recupera o Bitmap retornado pela cmera
Bitmap bitmap = (Bitmap) bundle.get("data")5
// Atualiza a imagem na tela
imgview.setImageBitmap(bitmap);
}

Uma ltima congurao necessria declarar a permisso para utilizar a camera


no arquivo de manifesto.

AndroidManifest.xmI
<manifest . . .>
<uses-permission android:nane="android.permission.CAMERA" />
<application . . . />

Como j estudamos o que uma intent, acredito que este cdigo seja tranquilo para
voc. Ao disparar a intent, a cmera vai abrir, e depois de tirar a foto o resultado
ser entregue no mtodo onActivityResult(. . .). Como eu disse antes, cada aplicao
retorna s informaes de uma forma diferente. No caso da aplicao da cmera, o
retorno um objeto do tipo Bitmap. Para obter o Bitmap, devemos ler o parmetro
"data" do Bundle. Esse Bitmap mostrado no Imageview que est no layout.
A figura 21.7 mostra o resultado da foto. A primeira parte o layout antes de
tirar a foto, a segunda parte a aplicao da cmera executando no emulador e
a terceira parte a foto retornada pela cmera do emulador, que retorna sempre
a mesma gurinha apenas para simular.

Importante: para tirar fotos com o emulador, preciso configura-lo no momento


de criar o AVD. Nas configuraes da cmera do emulador, selecione o item Back
(cmera traseira) e escolha a opo Emulated para simular uma cmera. Em meus
testes, a cmera do emulador com o webcam do computador no funciona bem
Outra opo tirar fotos com um dispositivo real.
Captulo 21 n Multimdia - udio, vdeo e cmera 607

?
1

Figura 21.7 - Wdeo do carro no Video Wew

21.8 Tirando fotos - como obter o arquivo da foto


No exemplo anterior, vimos como simples tirar uma foto utilizando uma intent
e delegar o trabalho ao aplicativo nativo da cmera. No emulador, retornada
sempre a mesma gura, mas ao tirar a foto em um dispositivo real voc ver que
o retorno da cmera um pequeno thumb da foto, ou seja, uma imagem reduzida
da original. O Android faz isso para economizar memria.
Para obter a foto original com toda a resoluo, necessrio passar um parme
tro para a intent da cmera, que deve ser o caminho para salvar o arquivo. Neste
caso a aplicao da cmera no vai retornar o thumb, pois ela vai simplesmente
salvar a foto no caminho de arquivo que foi informado pela intent. E no mtodo
onActivityResult( . . .) da activity s preciso testar se o retorno da cmera foi RESULT_0K
e ler o arquivo da foto no caminho que ns mesmos informamos.
O cdigo-fonte a seguir utiliza o mesmo arquivo de layout do exemplo anterior,
porm mostra como obter a foto com toda a resoluo da cmera.

MainActivity.java

import livroandroid.lib.utils.ImageResizeUtils;
import livroandroid.lib.utils.SDCardUtils;
public class MainActivity extends AppCompatActivity {
// Caminho para salvar o arquivo
private File le;
Google Android - 4' edio

private Imageview imgview;


@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imgview = (lmageview) ndViewById(R.id.imagem);
ImageButton b = (lmageutton) ndViewById(R.id.btAbrirCamera);
b.set0nClickListener(new View.0nClickListener() {
@0verride
public void onClick(View v) {
// (*1*) Cria o caminho do arquivo no SD card
le = SDCardUtils.getPrivateFile(getBaseContet(), "foto.jpg",
Environment.DIRECTORY_PICTURES);
// Chama a intent informando o arquivo para salvar a foto
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(HediaStore.EXTRA_0UTPUT, Uri.fromFile(le));
startActivityForResult(i, 0);
}

});
if (savedInstanceState != null) {
// (*2) Se girou a tela, recupera o estado
le = (File) savedInstanceState.getSerializable("le")
showImage(le);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// (*3*) Salvar o estado caso gire a tela
outState.putSerializable("le", le);
}

@0verride

protected void onActivityResult(int requestCode, int resultCode, Intent data) {


super onActivityResult(requestCode, resultCode, data);
if (resulttode == RESULT_0K && le != null) {
/I (*4*) Se a cmera retornou, vamos mostrar o arquivo da foto
showImage(le);
}

// Atualiza a imagem na tela


private void showImage(File le) {
if(le != null && le.eists()) {
Log.d("foto",le.getAbsolutePath());
(aPU|0 21 I Multimdia - udio, vdeo e cmera 609
t w = mgView.getNdth();
It h = mgVew.getHeght();
// (*5*) Redimensona a imagem para o tamanho do Imagevew
BNHP bitmap = ImageReszeUt1s.getReszedImage(Uri.fronF1e(1e), w, h, false);
Ta5t-Hk@T@t(ths, "w/h:" + imgVew.getwdth() + "/" + mgVew.getHeght()
+ " > " + "w/h=" + btmp.9etwdth() + "/" + btmap.getHeght(),
Tost.LENuTH_sHoRT).show();
imgvew.setImageBtmap(btmap);
l
}

Como esse exemplo vai salvar um arquivo no SD card, preciso declarar a per
misso no arquivo de manifesto.

AndroidManifest.xmI
<manfest . . .>
<uses-permission androd:name="androd.permssion.CAMERA" />
<uses-permission androd:name="android.permission.NRITE_EXTERNAL_STORAGE" /
<app1caton . . . />
</manfest>

Tambm estou utilizando algumas classes da biblioteca androd-utls, portanto


declare essa dependncia no arquivo app/buildgmdle.
Ao executar essa activity o resultado ser o mesmo do anterior. A diferena que
estamos salvando a foto em um arquivo do SD card, assim podemos obter a foto
com toda a resoluo. Agora vamos explicar algumas partes importantes que
destaquei no cdigo-fonte.
No item (*1*) foi criado um arquivo foto. jpg no SD card, e esse arquivo foi passado
por intent para a cmera. No emulador o arquivo foi criado na seguinte localizao:
/storage/sdcard/Android/data/br com.li1/roandrod. multimidia/les/Pictures/foto. jpg
O5 tens (*2*) e (*3*) so referentes ao ciclo de vida da activity Como o usurio
pode girar o celular entre vertical e horizontal, precisamos salvar o estado da foto,
pois conforme j explicado no livro o Android vai matar e recriar a activity nesses
Ca505_ O item (*3*) salva o arquivo no Bundle antes de destruir a activity e o item
(*2*) recupera O arquivo logo depois de recriar a activity Dessa forma, podemos
manter o estado da tela mesmo se o usurio girar o dispositivo.
O item (*4*) o trecho de cdigo que recebe o resultado da intent da camera, e
nesse caso basta abrir a foto do arquivo. O item (*5*) mostra o cdigo que faz o
redimensionamento da foto antes de mostr-la no Imagevew.
610 Google Android - 4 edio
Caso o aplicativo no faa o redimensionamento da foto e o.b1tm3P fwfdo PCL:
cmera seja mostrado diretamente no Imageview, o Android vai aPf5en_tar erro
de memria, conhecidos como 0ut0fMemory. Por isso, antes de mostrar O blfmap 20
Inagevew, feito esse redimensionamento, que consiste em diminuir o tamanhida
imagem, para que ela que somente com o tamanho necessario para Sf Xl 1 21
no Imagevew. Com isso, possvel economizar recursos e memOf1a~

Importante: entenda que o problema de memria com as fotos nao esta


relacionado com a cmera, pois ela consegue tirar quantas foi0S SCU aphcanvo
precisar. O problema acontece caso voc mostre a foto original fcom toda a
resoluo) no Imagevew. Portanto, nunca se esquea de reCllml1Sl0af 3 fofo
antes de mostra-la na tela.

21 .9 Trabalhando com bitmaps de forma eciente


Existem muitas maneiras de carregar um bitmap em memria, mas tudo consiste
em utilizar algum mtodo da classe BtmapFactory. Por exemplo, o cdigo a seguir
carrega um bitmap em memria, a partir da Ur de um arquivo.
Bitmap bitmap = BitnapFactory.decodeFi1e(urF1e.getPath(), null);

Mas as fotos tiradas com a cmera resultam em arquivos muito grandes, devido
alta resoluo das cmeras. Por exemplo, a foto de uma cmera pode ultrapassar
3.000 pixels de largura, mas muitas vezes o Imagevew que est no layout tem apenas
300 ou 500 pixels. Nesse caso, recomenda-se redimensionar a imagem original
para que ela que com um tamanho prximo do Imagevew.
justamente para facilitar o redimensionamento das fotos, utilizamos a classe
ImageReszeUti1s no exemplo anterior. Essa classe mostra como redimensionar a
foto da maneira correta, otimizando a memria do aplicativo. As explicaes deste
tpico visam fornecer a base terica, para que voc consiga entender o cdigo
-fonte dessa classe, que est disponvel no GitHub.
Para redimensionar uma gura, precisamos fazer quatro passos:
1. Obter as dimenses originais de largura e altura da figura.
2. Conhecer as novas dimenses para as quais a gura deve ser redimensionada.
3. Com base nestas dimenses , preciso calcular o fator de converso do
redimensionamento. Por exe
I mplo, se podemos diminuir a foto na metade,
ou em 1/4 do tamanho original.
4. Conhecendo o fator de converso, basta redimensionar a foto
de c ' ' - .
C3PtuIo 21 u Multimdia - udio, vdeo e cmera 611
NO P21550 1, podemos escolher qual o tamanho que a foto precisa ter. Mas o que
muitos aplicativos fazem ler o ta
manho que o Imageview utiliza no layout. O trecho
od1go a seguir mostra como implementar o passo 2, a m de extrair a largura
e altura da foto, sem carregar o arquivo inteiro em memria.
Uri uriFile = esta a Uri do arquivo da foto...
// Congura o BitmapFactor
y para apenas ler o tamanho da imagem (sem carreg-la em memria)
BitmapFactory.0ptions opts = new BitmapFactory.0ptions();
opts.inJustDecodeBounds = true;
// Faz o decode da imagem
BitmapFactory.decodeFile(uriFile.getPath(), opts);
// L a largura e altura do arquivo
int w = opts.outwidth; // largura original
int h = opts.outHeight // altura original

O segredo utilizar o parmetro inJustDecodeBounds = true. Esse parmetro faz com


que a classe BitmapFactory leia apenas o tamanho do arquivo, sem carregar a ima
em inteira em memria. No nal as variveis w e h contm a lar ura e a altura
3

da imagem, respectivamente.

Agora vamos para o passo 3. Uma vez que o tamanho da gura original foi ob
tido, podemos calcular o atributo que vai denir a escala da foto, chamado de
inSampleSize. Esse clculo consiste em fazer uma simples diviso do tamanho total
da foto pelo tamanho desejado.
// Fator de escala
int scaleFactor = Math.min(w / width, h / height);
opts.inSampleSize = scaleFactor;

Nota: recomendvel que o atributo inSampleSize seja mltiplo de 2. O fator de


escala funciona da seguinte forma: se o valor for igual a 1, signica que uma
imagem com o tamanho original ser retornada; se for igual a 2,s1gnica metade
do tamanho; se for igual a 4, signica um quarto; e assim sucessivamente.

por ltimg vamos para o passo 4, que consiste em carregar o Bitmap em memria,
7

mas como o fator de escala foi denido, o tamanho da foto ser menor que a
origina] Ngre que para carregar a foto em memria o parmetro inJustDecodeBounds
deve ser false.
// Decode bitmap with inSampleSize Set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
612 Google Android - 4 edio
Entender esse exemplo fundamental para trabalhar com imagens no Android,
mente o tamanho e mem
pois mostramos uma foto em um Imagevew, utilizando so
ria necessrios. Note que a classe BtmapFactory contm v
rios mtodos para carregar
Btmaps em memria, como decodeByteArray(...), decodeF1e(-~-), d@Cd@R@5,U"C(. ~)
etc. Qual mtodo voc vai utilizar no importa. O que voc precisafazer e utlllzar
corretamente os truques com o atributo nJustDecodeBounds para primeiro obter O
tamanho original da gura, na sequncia calcular o fator de escala, e por ultimo
carregar a foto reduzida em memria.

Dica: para continuar seus estudos, recomendo uma leitura nos links adicionais
que esto no final do captulo. Dentre eles, importante ler 0 topico Loading Large
Btmaps Efcently da documentao ocial.

21.10 Enviando a imagem para o servidor


No mesmo exemplo em que tiramos uma foto, observe que o arquivo da foto foi
salvo no SD card. Portanto, podemos transferi-lo para o servidor, caso seja ne
cessrio. Para isso, basta converter 0 arquivo para um array de bytes. Voc pode
enviar esse byte[] ao servidor fazendo um post HTTP, de forma que esse array
seja enviado no corpo da requisio, ou fazendo um post multipart em um web
service que aceite upload de arquivos.
Enfim, mostrar como implementar 0 servidor que aceite upload de arquivos no
o foco do livro, mas este breve tpico serve apenas para lembr-lo que o arquivo
da foto est nas suas mos, e agora voc pode fazer 0 que quiser com ele.
Caso precise obter mais informaes a esse respeito, pesquise no Google por
Android HTTP Multipart POST

21.11 Gravando udio e vdeo


A maneira mais simples de gravar udio e vdeo no A ndroid utilizar uma intent,
da mesma forma que tiramos uma foto. Essas intents so mesmo boas parceiras,
no so? Aprendemos que para tirar uma foto podemos utilizar uma intent com
a ao ACTION_IMAGE_CAPTURE, e o retorno o arquivo da foto. Podemos utilizar 0
mesmo conceito para gravar vdeos, basta alterar a ao para ACTION_VIDEO_CAPTURE.
O cdigo a seguir mostra como disparar a intent para gravar um vdeo'
Captulo 21 I Multimdia - udio, vdeo e cmera 613
Fll fl = ' ' ' ||
1 e 'le SDCHFUUIS.9@tPUb11F11e( v1deo.np4", Environment.o1REcroRY_Mov1Es) ;
// Chama a intent informando o arquivo para salvar a foto

Como f z .
Intent i = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
i.putEtra(MediaStore.EXTRA_0UTPUT, Uri.fronFi1e(1e))'
startActivityForResu1t(i, 0);

resultado, o arquivo de video sera gravado e salvo no local informado


portanto no mtodo onActivityResu1t() podemos mostrar o vdeo com o Videoview.

protected void onActivityResu1t(int requestCode, int resuittode, Intent data) {


super.onActivityResu1t(requestCode, resu1tCode, data);
if (resultCode == RESULT_OK) {
videoview.setVideoPath(1e.getAbso1utePath());
videoview.setMediaContro1ier(new MediaControi1er(this));
videoView.start();
}

Deixarei para voc concluir este exerccio. Caso ache necessrio, veja o projeto
VideoRecorder disponvel nos exemplos do livro.
Para gravar udio, tambm podemos utilizar uma intent, porm no caso do udio
o arquivo sempre salvo no diretrio interno do Android, portanto no preci
samos definir o arquivo com a localizao. O trecho de cdigo a seguir mostra
como disparar a intent para gravar um udio:
Intent i = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
startActivityForResu1t(i, G);
O resultado mais uma vez entregue no mtodo onActivityResuIt(); basta ler
o mtodo getData() da intent de retorno. O objeto Uri contm a localizao do
arquivo de udio, e conforme j aprendemos esse udio pode ser tocado com a
classe MediaPiayer.

protected void onActivityResu1t(int requestCode, int resu1tCode, Intent data) {


super.onActivityResuit(requestCode, resu1tCode, data);
if (resu1tCode == RESULT_OK) {
// Uri, exemplo: content://media/external/audio/media/1
Uri uri = data.getDat()
pjayer z MediaPiayer.create(this, UF1);
p1ayer.start();
}

exerccio, mas caso ache necessrio


Novamente, deixarei para voce conclulr 651
veja 0 projeto AudioRecorder disponvel nos exemplos do livro.
614 Google Android - 4 edio
21.12 Links teis

Neste captulo, estudamos os recursos de multimdia disponiveis HO AHFOK


Para continuar seus estudos, separei alguns links interessantes.
Android Training - Managing Audio Playback

http://developerandroid.com/training/managing-audio/iHCCX.html

Android Training - Taking Photos Simply

http://developerandroid.com/training/camera/photobasics.html
Android Training - Recording Videos Simply

http://developer:android.com/trainmg/camera/videobasics. html
Android Training - Controlling the Camera

http://developer:android.com/training/camera/cameradirect.html
Android Training - Loading Large Bitmaps Efciently

http://developer and roid. com/training/displaying-bitmaps/load-bitmap. html


CAPTULO 22

Mapas
\,__d
\

'\

Uma das funcionalidades que mais chamam a ateno na plataforma do Android


e a grande facilidade para se construir uma aplicao integrada ao Google Maps.
Neste captulo, veremos diversos recursos sobre a construo de mapas, como
controle de zoom, tipo de visualizao por rua e satlite, desenho de imagens
(marcadores) no mapa e muito mais.

22.1 Introduo
Existem duas verses da API de mapas, que chamaremos de verses 1 e 2, ou apenas
V1 e V2. Em dezembro de 2012, o Google descontinuou a Vl. Portanto, neste livro,
vamos estudar a V2. Contudo, caso seja a primeira vez que voc esteja estudando
a API de mapas, explicaremos alguns conceitos bsicos da API antiga para voc
saber identicar o cdigo deprecated (obsoleto) quando o vir.
A primeira verso da API de mapas do Android era representada pela classe
Mapview, uma subclasse de View.

<LnearLayout . . . >
<com.googIe.androd.maps.Mapvew . . . />
</LnearLayout>

Contudo a API V1 e a classe Mapvew foram descontinuadas pelo Google. Dito isso,
1

o objetivo dessa breve explicao foi apenas informar o que voc no deve fazer,
pois normal que, alm deste livro, voc procure algum material auxiliar para
complementar os seus estudos. Ento, caso voc encontre um artigo na internet
ber
sobre a classe Mapvew, sa que ela foi descontinuada.

615
616 Google Android - 4= edio
22.2 Google Maps Android API - Verso 2
a nova API de mapas, chamada Google
Em dezembro de 2012, 0 Google lanou 0 0 V1 T d
Maps Android API V2, a qual apresenta muito mais funcionalidades que 21 - O O 0
framework de mapas foi criado utilizando vetores para suportar 35 VlSU3l1Z305
2D e 3D, o que tambm possibilita um ganho significativo no deserriP1h0 tor:
nando todas as interaes e animaes muito mais fluidas. A v1sual1zaao.3D e
feita automaticamente se o nvel de zoom estiver perto O b2lSE21Hf C Se na Cldade
em questo houver imagens 3D.
A API tambm ganhou diversas melhorias, mas a principal foi a utilizao de
fragments. Antigamente, com a V1, s era possvel exibir um mapa de cada vez
na tela, o que, no caso dos tablets, trouxe um grande problema. Mas como a V2
implementada com fragments, essa limitao no existe mais. Na prtica, trocamos
a classe Mapvew por MapFragment. Caso esteja utilizando a biblioteca de compatibi
lidade, que o nosso caso no aplicativo dos carros, use a classe SupportMapFragrnent.

22.3 Google Play Services


O Google Play Services um aplicativo distribudo no Google Play que apresenta
funcionalidades essenciais para se comunicar com os servios do Google, alm
de vrias APIs. Por exemplo, a API de Mapas V2 faz parte do Google Play Services,
assim como diversas outras APIs, tais como: Google+, Google Drive, Google Fit,
Jogos, Localizao etc.
A vantagem do Google Play Services que ele distribudo pela loja de aplica
tivos; sendo assim, pode receber atualizaes, e qualquer melhoria ou correo
de bug das APIs do Google so atualizadas facilmente nos dispositivos. Para
utilizar o Google Play Services no projeto, basta declarar a dependncia no ar
quivo app/buildgradle. Faa isso no projeto dos carros, pois neste captulo vamos
desenvolver a tela que vai mostrar o mapa da fbrica do carro.

app/buiId.gradIe

Compile 'com.google.androd.gms:play-services:7.0.0'

Caso o Android Studio informe que existe uma verso mais nova da biblioteca
quando voc for estudar o livro, que vontade para utilizar sempre as novas verses
Captulo 22 1 Mapas 617
N:z.
Sc (081 Sfa Clrlando ou movendo algumas APIs para o Google Play
O l_ '_ Vmagm que ml1'l0f18S Ou bugs podem ser corrigidos atualizando
ap icativo o Google Play Services pela loja.

22.4 Gerando a chave de acesso dos mapas


Para o mapa funcionar, precisamos criar uma chave de autenticao no servio
do Google. A criao das chaves feita no Google Developers Console, que pode
ser acessado na seguinte pgina:
https://console. developers. google. com/

Nota: para fazer login na pgina do Google Developers Console necessria


uma conta do Google, ou seja, do Gmail.

Se for a primeira vez que estiver abrindo essa pgina com sua conta de email,
voc ver uma tela com um boto Create project, utilizado para criar um novo pro
jeto. No formulrio de criao do projeto, basta digitar um nome qualquer para
identic-lo. Eu recomendo que voc crie um projeto chamado Livro Android, assim
todas as conguraes que zermos referente ao livro caro nele.
Ao criar o projeto, note que o campo ProjectlD preenchido automaticamente, e
voc no precisa se preocupar com ele.

Nota: um projeto no console do Google representa um conjunto de configuraes


que so feitas na sua conta para que as aplicaes utilizem determinados servios,
como o de mapas, mensagens de push, entre outros.

Logo depois de criar a congurao do projeto, voc ver uma pgina com algu
mas opes no menu lateral, como: Overview, APls&auth, Monitoring, Compute, Networking,
Storage, Big Data etc.
os de fazer na pgina do console habilitar o
A primeira congurao qL1 tm
Servio dos mapas. Para isso, acesse a pgina APls&auth > APIs e habilite o servio Google
Maps Andmd Apm Confgrme a figura 22.1. A figura mostra minha conta de testes. Veja
que 0 servio Google Cloud Messaging for Android para enviar mensagens por push tambm
est habilitado, mas vamo s estudar esse assunto em outro captulo.
618 Google Android - 4 edio
- U
"`) Google Developers V, _
(- C fi H; iu consoledevelopers.qoogletom, ,_
zW
' g., ,,-, ""` 'l' " == Y
1 ze rzgs ~ ' lirov;fi'oid@gIW'l CCV
|\`9(_`,\ \.'

Enabled APM-2

Oi.sei~.|e. '
Livro Android
. ,-~~
gt ,` , _

El? ng S. Settings u
Peri os

Apis Si null! u
._..,

Figura 22.1 - Google Maps Android API v2 habilitado.

O prximo passo criar a chave de acesso para o servio do Google Maps. Para
isso, precisamos obter o SHA-1 ngerprnt do certicado que voc utiliza para compilar
o projeto. Por padro, um certicado de debug criado automaticamente pelo
Android Studio, o qual ca na pasta do usurio do sistema operacional. A seguir,
temos a localizao deste arquivo no Windows e Linux:
Windows - C:\Users\usuario\android\debug./eeystore
Linux - /home/usuario/.android/debug.keystore

Esse o certicado de debug utilizado para testes, a m de instalar aplicativos


no emulador ou em um dispositivo. Quando voc for publicar o aplicativo no
Google Play precisar criar outro certificado, mas isso vamos estudar no captulo
sobre o Gradle.

Nota: um aplicativo sempre precisa ser assinado por um certificado antes de


ser instalado no dispositivo. O Android Studio faz isso automaticamente ao
executar o projeto, assinando-o com o certicado de debug, que o arquivo

SHA
debugleeystore localizado na pasta do usurio. z

Copie o caminho de seu certificado, abra um prompt e digite o seguinte comando


para extrair o -1 ngerprint. Note que informei o caminho d o arquivo debug. /eeystore.

C:\keytoo1 -list -v -keystore "C:\Users\usuaro\.androd\debug.keystore"


-alias androiddebugkey -storepass android -keypass androd
Alias name: androddebugkey
Captulo 22 1 Mapas
619
Certcate fingerprints;
MDS: 14:A9:36:6C:7C:50:E6:42:88:8D:94:2F:C4:CF:0395
SHA1: C1:66:56:93:DF
:DE:0B:B9:DC:ED:76:D7:65:B7:10:DC:1F:3F:9D:41
Signature algorthn name: SHA1wtthRSA
Version: 3

O resultado do
comando mostra vrias informaes sobre o certicado. Mas a
linha pela qual estamos interessados esta que exibe o SHA1ngerprint:
SHA1: C1:66:56:93:DF:DE:0B:B9:DC:ED:76:D7:65:B7:10:DC:1F:3F:0D:41

Nota: o comando keytool um executvel que est localizado na pasta bin do]DK.

Depois de copiar o valor do SHA-1ngerprint,volte para a pgina do Google Developers


Console. Entre na pgina APIs&auth > (redentials e clique no boto Create New Key. Na
sequncia, selecione o tipo Android Key. Na janela que vai aparecer (Figura 22.2),
digite no campo de texto o SHA-1 ngerprint + ponto e vrgula + o pacote do
projeto, como por exemplo:
C1:66:56:93:DF:DE:0B:B9:DC:ED:76:D7:65:B7:10:DC:1F:3F:0D:41;br.con.livroandroid.carros

A gura 22.2 mostra como digitar o ngerprint com o pacote.

Create an Android key and configure allowed Android applications


This key can be deployed in your Android application. I

API requests are sent directly to Google from your client Android device. Google verifies that each

Learn more
request originates from an Android application that matches one of the certificate SHA1 fingerprints f
using the following command: g
and package names listed below. You can discover the SHA1 fingerprint of your developer certificate ;
I

keytool -list -v -keystore mystorakeystore


i

ACCEPT REQUESTS FROM AN ANDROID APPLICATION WITH ONE OF THE CERTIFICATE


FINGERPRINTS AND PACKAGE NAMES LISTED BELOW _ A g 1 l

One SHA1 certificate fingerprint and package name pafaled by 3 5em'|) Pef line- EamPle- i
45B5:E4:6F'361AD'0A:98:94:B4:02:66r2B:12:17:F2'56:26.AOIEO;c0m.E'mPl`
C1'66'56'Q3'DF'DE'0B`B9ZDCZEDI7ID75ZB7Z1OZDCZTFZ3FIODI41;bf.COfTl.lVfO3I1dfOd.ClT0 i
i

Figura 22.2 - Criando uma chave de acesso Android Key.


620 Google Android - 4= edio
. . - ` oder utiliz
O resultado disso que foi criada uma chave de acesso ao Google Maps Android API v2 para
o seu certificado e pacote. Isso significa que somente o seu projeto p
m a chave criada. O campo API Key 21 Chave
-la. A figura 223 mostra a pgina co h S U k
de que precisamos, que neste caso o texto AIzaSyAapGvUMdKvQd05bQZbD25Y4 ClXl'l Q _Z

Key for Android applications l

API KEY AIzaSy0YCu8-ljlg-dug-VZT1SC_6fekDKnO0TQ


l

Autmoio c1zzszoa:tF:oE:oa~B9:oc:Etz7:t7:5;B7z1o~oc1Fz3F:ot:41;br.0mzlM2dfd-2ffS
APPLICATIONS

ACTIVATION DATE Mar17, 2015, 3120200 PM

ACTIVATED BY l|vroandroid@gmal.com (you) I


Edit allowed Android applications Regenerate key Delete
Figura 22.3 - Chave de acesso criada.

Nota: uma chave de acesso utilizada por uma aplicao para autenticar o
acesso nos servios do Google. Copie a API Key, pois vamos utiliz-la depois para
congurar o arquivo de manifesto do projeto.

22.5 (ongurando o projeto


Vamos continuar com o desenvolvimento do projeto dos carros, mas antes de
escrever cdigo com a API V2 precisamos congurar o projeto. Como dito antes,
a API V2 de mapas depende do Google Play Services, portanto certique-se de
que essa dependncia est congurada no app/build.gradle.

app/buiId.gradle
A1O

compile 'com.googIe.android.gms:play-services:7.0.0'

A parte mais trabalhosa congurar o arquivo de manifesto ento adicione as 3

seguintes permisses e configuraes:

AndroidManifest.xml
<?mI version="1.0" encodng="utf-8"?>
<manifest ... package="br.com.Iivroandroid.carros"
U5@S'D@FWSS0n androd:name="android.permisson.INTERNET" />
Captulo 22 I Mapas
621

<uses-permission android:nane="android.permission.ACCESS_NETwORK_STATE" />


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

uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /

uses-feature android:glEsVersion="0x0002000G" android:required="true"/>


<application . . .>

<meta-data
android:name="com.google.android.gns.version"
android:value:"@integer/google_play_services_version" /

meta-data android:name="com.google.android.naps.v2.API_KEY"
android:value:"@string/API_KEY"/>

<activity . . . />

No arquivo de manifesto, estamos usando o texto @string/API_KEY, ento vamos


criar o arquivo /res/values/strings_cong.xml com essa string de conguraao. Note
que no estou usando o arquivo stringsxml padro, pois gosto de separar essas
conguraes em um arquivo isolado. Em outros captulos, tambm vamos adi
cionar mais conguraes nesse arquivo.

/res/vaIues/strings_cong.xmI
<zm1 Vef50n:"1,O" encoding="utf-8"?>

ora _v.'_~.' .to


<I-- Gerado do SHA1' C1:66:56:93:DF:DE:GB:B9:DC:ED:76:D7:65:B7:10:DC:1F:3F:0D:41 -->
<5trng name="API KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn0oTQ

A amos entender esse arquivo Primeiramente, adicionamos algumas per


misses que so necessrias para os mapas funcionarem. Na lista a seguir, vamos
~ ' para
aproveitar cado deo Slgfll
revisar cada permissao que temos ate o momen
no aplicativo dos carros.
. u ' ' ' . RNET"
<uses-permission android:name= android.permission INTE />
Utilizada para acessar a intfflff
622 Google Android - 4 edio ,, . - TE"
. . - ~ or
<uses - permission android : name: android . permission .ACCESS_NETwORK_STA />

Utilizada para ler o estado da rede.

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

Utilizada para o Google Maps salvar informaoes no SD card, c0m0 P


exemplo: fazer cache dos mapas.

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

Utilizada para ler dados do SD card.

<uses-permission android:name="com.goog1e.android.providers.gsf.permission.READ_GSERVICES" />

Utilizada para acessar os servios do Google.

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

Utilizada para acessar o GPS por triangulao de antenas.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /

Utilizada para acessar o GPS por hardware.

Nota: mesmo que voc no utilize a API de localizao no cdigo, caso a opo my
location esteja habilitada no mapa pela API, as permisses de GPS so necessrias.

Logo depois das permisses, outra congurao importante declarar que a


OpenGL ES V2 requisito para os mapas funcionarem. Sendo assim, precisamos adi
cionar a configurao para que somente dispositivos com a OpenGL ES
V2 possam instalar essa aplicao pelo Google Play:
<!-~ Precisa de OpenGL ES verso 2 --
<uses-feature android:g1EsVersion="0@002000G" android:required="true" /
Esta pgina mostra informaes sobre a verso da
, Open GL encontrada nos
diversos dispositivos com Android:

http://de velopcn and roid. com/a bout/dush boa rds/i ndex. html # Open G L

Outra configurao importante declarar a verso da biblioteca do Google Play


Services com o qual o seu projeto foi compilado,


!-- Verso do Google Play Services --
<meta-data android:name="com.goog1e.android.gms.version"
<3dfd1V3lU@="@"t@9f/909l_D1y_services_version" /
Captulo 22
Por ltimo a con " ' '
n Mapas 623
ao Servio de ma ufldao nais importante de todas declarar a chave de acesso
p S' em ra aquela Cha*/6 (API Key) que criamos na pgina do
Google Developers Console? Coloque a sua chave aqui

<string name="API_KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn@oTQ</strng>

Importante: para testar os mapas, utilize um dispositivo real com o Google


Play Services instalado, pois o emulador no tem esse pacote. Embora existam
artifcios tcnicos para instal-lo no emulador, no abordarei isso aqui. Lembre
se tambm de criar a sua chave API Key na pgina de console do Google, pois ela
ser gerada para o certicado que voc est utilizando no seu computador. Se
voc no inserir a chave correta, o mapa no vai funcionar.

Lembre-se de que cada chave diferente e voc precisa utilizar a sua, caso contrrio
o mapa no vai funcionar. Isso porque cada chave gerada baseada no certicado
digital utilizado no desenvolvimento, e naturalmente o arquivo debug. keystore que
est na sua mquina diferente do meu.
Uma dica interessante no caso de empresas nas quais existem vrios desenvolvedo
res no time utilizar o mesmo arquivo debuglzeystore em todos os computadores.
Assim a chave para todos a mesma, ento voc poder instalaro aplicativoide seu
. ' ` ' brando ue todas as
computador, ou do computador de um colega de trabalho, pois todos utilizam o
mfzgmg arquivo debugkeystore para assinar o aplicativo. Lem q U '
. ~ - ' ' ' e uma a lica ao assinada com o
apligagg, no Android precisam ser assinadas por um certicado e, caso Ja exista
uma aphcaao instalada no dispositivo, soment p
mesmo certificado pode atualiz-la.
z4

_ .--ra a p
22.6 Adicionando o mapa no projeto d0S Cff$
t s todo o ro eto vamosarte pa
f ~ ro. . que
mais fcil
Depois que conguramo P J d arms existem Os
mostrar a fbrica do carro no mapa. No XML OU JSN 05 C '
Google Android - 4= edio

campos com a latitude e longitude da fabrica de cada Cal'


{

"none": "Carro 1",

"latitude": "latitude aqui",


"longitude": "longitude aqui"
}

Por exemplo, o carro Ferrari FF est com a localizao denida para a fbrica da
Ferrari, em algum lugar da Itlia. Nosso objetivo mostrar uma activity com um
mapa posicionado na localizao da fbrica do carro. Faremos isso ao clicar no
boto Mapa que j existe na tela de detalhes do carro.
O passo a passo para mostrar um mapa ser parecido com o que zemos para
mostrar um vdeo. No caso do vdeo, criamos as classes VideoActivity para controlar
a navegao, e criamos a classe VideoFragment para gerenciar 0 contedo e O layout.
Dentro do VideoFragnent utilizamos o Videoview. Desta vez, vamos criar as classes
MapaActivity e MapaFragnent. Dentro da classe MapaFragment, vamos inserir 0 componente
de mapas da API V2, que o fragment SupportMapFragment. Ento mos obra! Abra o
projeto dos carros novamente e crie uma activity conforme demonstrado a seguir.
Ela recebe o objeto carro por parmetro e delega o trabalho para um fragment.

MapaActivity.java
package br.com.livroandroid.carros.activity;

import br.con.livroandroid.carros.fragments.HapaFragment;
public class MapaActivity extends BaseActivity {
private Carro carro;
@0verride

protected void onCreate(Bundle savedInstanceState) {/


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mapa);
// Congura a Toolbar como a action bar
setUpToolbar();

carro = (Carro) getIntent().getSerializableEtra("carro").


getSupportActionBar().setTitl e(carro.nome);

getSupportActionBar().setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null) { ,
Captulo 22 : Mapas
625

// Adiciona o fragment no layout da activity


MDFFagment mapaFragment z n
QW MDFragment();
9etSupportFr X raso);
f'1DaFragment.setArguments(getIntent() .ge-tg t

mapaFragment).commit(); . id 'fragLayout'
agmentManager()'be91"Tra"5aCt0() FDlace(R
}

@0verride

public boolean onOptionsItemSelected(MenuItem item) {


// Volta para a activity CarroActivity
// Mesmo cdigo que colocamos na classe VideoActivity
}

No arquivo de layout, vamos colocar a Toolbar e um FrameLayout de marcao para


adicionar o fragment.

/res/layout/activity_mapa.mI
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent" android:layout_height="match_parent">
<include layout:"@layout/include_toolbar" />
<FrameLayout android:id="@+id/fragLayout"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:layout:"@layout/fragment_mapa" />

Por ltimo, congure a activity no arquivo de manifesto.

AndroidManifest.xmI
<application . . .>

<activity and roid : name=" . activity .MPBAC'CVfY "


android;parentActivityName=".activity.CarroActivity" >
<meta-data android:name="android.support.PARENT_ACTIVITY"
android Va1ue=" . activity. CarroActivity" />

ortanto crie a classe


O contedo da tela ser grHC3d0 Pl0 ffa8mnt,I _ _
MapaFragment. O cdigo a seguir m0 stra um template basico de como utilizar o
fragment Support!-1apFragme'C, P015 estamos posicionando o mapa na coordenada
da fbrica, mostrando um marcador e congurando o tipo do mapa (normal,
satlite, terreno ou hbrido)
626 Google Android - 4 edio

MapaFragment.java
package br.com.livroandroid.carros.fragments;

import com.google.android.gms.maps.SupportMapFragment;
public class MapaFragment extends BaseFragment implements 0nMapReadyCallback {
// Objeto que controla o Google Maps
private Googleap map;
private Carro carro;
public View onCreateView(Layoutlnater inater, ViewGroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_mapa, container, false);
// Recupera o fragment que est no layout
// Utiliza o getChildFragmentManager() pois um fragment dentro do outro
SupportMapFragment mapFragment = (SupportMapFragment)
getChildFragmentManager().ndFragmentById(R.id.mapFragment);
// Inicia o Google Maps dentro do fragment
mapFragment.getMapAsync(this);
this.carro = (Carro) getArguments().getSerializable("carro")
return view;
}

@Override
public void onMapReady(GoogleMap map) {
// O mtodo onMapReady(map) chamado quando a inicializao do mapa estiver Ok.
this.map = map;
if(carro != null) {
// Ativa o boto para mostrar minha localizao
map.setMyLocationEnabled(true);
// Cria o objeto LatLng com a coordenada da fbrica
LatLng location = new LatLng(Double.parseDouble(carro.latitude),
Double.parseDouble(carro.longitude));
// Posiciona o mapa na coordenada da fbrica (zoom = 13)
CameraUpdate update =~CameraUpdateFactory.newLatLngZoom(location, 13);
map.moveCamera(update);
// Marcador no local da fbrica
map.addMarker(new MarkerOptions()
.title(carro.nome)
.snippet(carro.desc)
.position(location));
// Tipo do mapa:
// (normal, satlite, terreno ou hibrido)
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}

}
Itnhamos
. . .``'.
Captulo 22 - Mapas 627
entro
dmP0I't1nte. como o fragment da biblioteca de mapas SupportMapFragnent est
O nosso fragment MapaFragnent, temos uma situaao que ainda nao
f visto no livro, que e um fragment dentro de outro. Por isso, para obter
o ragment SupportMapFragnent foi utilizado o mtodo getChildFragmentManager() em
Vez de 9etFragnentManager().

O arquivo de layout do fragment contm apenas o fragment SupportMapFragnent.

/res/layout/fragment_vdeo.xmI
<FraneLayout . . . >
<fragment android:id="@+id/mapFragment"
class="con.google.android.gns.maps.SupportMapFragnent"
android:layout_width="match_parent" android:layout_height="natch_parent"
/>

Para nalizar o exemplo, volte classe CarroFragment, que a tela com os detalhes
do carro que tem os botes Vdeo e Mapa na action bar. Altere o trecho de cdigo
do PopupMenu cujo alerta inamos com as opes. Na opo que mostra o mapa,
vamos navegar para a activity que acabamos de criar.

CarroFragment.java
public class CarroFragnent extends BaseFragnent {

popupMenu.set0nMenuItemClickListener(new PopupMenu.OnMenuItenClickListener() {
@Override
public boolean onMenuItemClick(MenuIten item) {

else if (iten.getItenId() == R.id.action_mapa) {


// Abre outra activity para mostrar o mapa
Intent intent = new Intent(getContext(), MapaActivity.class);
intent.putExtra("carro", carro);
sta rtActivity(intent) ;
}
return true;
}});
}

Pronto! A navegao deexecute


telas est concluda, ento _ ~o projeto no emulador
para testar. Na tela de detalhes do carro, ao selecionar a opao Mapa, vamos nave
gar para a activity do mapa, conforme a gura 22.4. O resultado a tela do mapa
'T nf
Google Android - 4' @d5
628
, _ - - - um marcador no localtemda ofbrica,
_ ~ ` ` _ Observ que O mapa
posicionado na fabrica do carro. Foi adicionado
. .o
- na localizao dO
boto

,. -, -. `-aiais
osicionar -mapa
API.
que mostra informaoes sobre essa localizaao
detalhes da
my location habilitado, que ao ser clicado v P
usurio. Nos topicos a segu1f,V2m05 estudar m
Qjzfii
* r _ _ . J (_
. W; .. `V ' j.;\, _ ,
@

I

._FGT
, -r-- '` Ff
QITO ' . O"`
1
vmar 5" 'F _ ffnzwtil

|Ansrs ' -19"


OUV'

PPI"
klnnlori-
www
T
U.ll'Q-ID

_
vb (W nz o~ OT'

~'.ruv.5 .
r1O\lr

Figura 22.4 - Mapa posicionado na fbrica do carro.

22.7 Classe GoogIeMap


Depois de colocar o fragment SupportMapFragment no layout, podemos recuper-lo
normalmente com o mtodo fndFragmentById(id). Precisamos fazer isso para confi
gurar a visualizao do mapa.
Na verdade, o fragment SupportMapFragment no faz nada, ele apenas cria a view neces
sria para o mapa funcionar. Mas dentro desse fragment existe o objeto com.goog1e.
androd.gms.maps.Goog1eMap. Agora, falando em portugus bem claro, esse o cara! A
classe Goog1eMap representa o mapa do Google e, por meio dela, podemos controlar
visualizao do mapa, nvel de zoom, aparncia, localizao, inserir marcadores
etc. Essa classe faz um trabalho importante e encapsula toda a complexidade de
acesso aos mapas do Google, tornando o trabalho do desenvolvedor mais simples.
Para obter o objeto GoogleMap, necessrio se conectar ao s servios do Google.
Isso feito com o mtodo getMapAsync(1stener). Esse mto do assncrono, e o
resultado ser entregue no mtodo onMapReady(map) da interfa ce 0nMapReadyCaU.back
Captulo 22 . Mapas 629
possvel e v1s . . .
brincar co . ~ . .
que VCC PWCS8 implementar. Uma v

alterar o t1 o d ' ~
ez que temos o objeto GoogleMap em mos,
_ m O mapa, como alterar sua posiao, adicionar marcadores,
Cdi f P d ualizaao, fazer desenhos etc. Para revisar os conceitos, veja o
go- onte a classe MapaFragment que zemos_

22.8 Localizao do mapa - latitude e longitude


Na AlI de. mapas, uma coordenada representada pela classe LatLng, que pode
ser criada informando a latitude e longitude:
LatL9 latLng = new LatLng(-23.564224,-46.653156);

Uma vez que a localizao conhecida, podemos criar um objeto do tipo


CameraUpdate para posicionar o mapa no local desejado, conforme demonstrado
neste trecho de cdigo:
GoogleMap map = ...;
// Localizao do mapa (latitude e longitude)
LatLng latLng = new LatLng(-23.564224,-46.653156);
CameraUpdate position = CameraUpdateFactory.newLatLngZoom(latLng, 15); // O nmero 15
// o zoom
map.moveCamera(position);

Nesse cdigo, utilizei o mtodo moveCamera(update) para alterar a localizao do


mapa, porm ele faz isso de forma brusca (sem animao). Caso seja necessrio
fazer essa atualizao com uma animao, utilize o mtodo animateCamera(update),
0 que torna a experincia do usurio bem mais amigvel e uida. O mto
do animateCamera(update,time,callback) contm uma variao com trs argumentos
que basicamente denem o tempo em milissegundos que a animao deve durar
e um listener de callback caso a animao seja cancelada. Apenas para demonstrar
a utilizao da API, o trecho de cdigo a seguir mostra como centralizar o mapa
em determinada localizao de forma animada, com durao de dez segundos.
O efeito criado semelhante abertura do Google Earth, no qual voc entra no
globo terrestre de forma animada.
// Centraliza o mapa com animao de dez se9Ud5 I
ameraUpdate updatg z CameraUpdateFactory.newLatLngZoom(location, 13);
map animateCamera(update, 10000, new CancelableCallback() {
@Override
public void onFinish() f
) "Ma
Toast.makeText(getContext( , Pa centralizado.", Toast.LENGTH_SHORT).show();
}

@Override
630 Google Android - 4 edio
public void onCancel() {
Toast.makeTet(getContet(), "Animao cancelada.", Toast.LENGTH_SHORT).show();
}

});

22.9 (ameraPosition - zoom


Como voc pde perceber nos exemplos anteriores, foi congurada a localizao
do mapa, assim como o zoom. Fizemos isso com 0 mtodo CameraUpdateFactory.
newLatLngZoom(1atLng, zoom), que tambm recebe o zoom como parmetro:
GoogleMap map = ...;
LatLng 1atLng = new LatLng(-23.564224,-46.6S3156);
int zoom = 15;
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(latLng, zoom);
map . moveCamera(update);

Outra forma de congurar os valores do mapa, como localizao e zoom, utilizar


a classe CameraPosition.Bui1der:

Goog1eMap map = ...;


LatLng latLng = new LatLng(-23.564224,-46.653156);
CameraPosition position = new CameraPosition.Bui1der().target(1atLng).zoom(15).bui1d();
CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);
map.moveCamera(update);

Note que, para alterar a posio e o zoom da cmera, necessrio criar primeiramente
um objeto CameraPosition, o qual pode facilmente ser criado pelo CameraPosition .Bui1der,
que, como o prprio nome indica, uma classe de criao de objetos que segue o
padro Builder do GoF. Depois que o objeto CameraPosition for criado, necessrio
criar o CameraUpdate baseado nele, e somente depois disso atualizam-se essas infor
maes no mapa com os mtodos moveCamera(update) ou animateCamera( update).
Resumindo, este trecho de cdigo que utilizamos anteriormente:
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(1atLng, 15); ]/ 15 o zoom

um atalho para este cdigo:


CameraPosition position = new CameraPosition.Bui1der().target(1atLng).zoom(15).bui1d();
CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);

Mas a classe CameraUpdateFactory tem vrios atalhos. Por exemplo, se voc precisa
configurar apenas o zoom, basta utilizar o seguinte cdigo:
CameraUpdate update = CameraUpdateFactory.zoomTo(10);
Captulo 22 1 Mapas 631
A vantagem de utiliza
r a classe CameraUpdateFactory para atualizar o zoom que ela
vai manter
I _a localizao
_ apenas atual e o zoom. Outra forma de controlar
alterar
o zoom e utilizar os seguintes mtodos'

CameraUpdateFactory.zoomIn()

Aumenta o zoom do mapa em 1.

CameraUpdateFactory.zoom0ut()

Diminui o zoom do mapa em 1.

CameraUpdateFactory.zoomTo(zoom)

Congura o valor do zoom utilizado pelo mapa.

CameraUpdateFactory.zoomBy(zoom)

Aumenta ou diminui o zoom do mapa, conforme o valor informado.

Nota: os valores vlidos para o zoom vo de 2 a 22.

22.10 Congurando o tipo do mapa


Usurios dos mapas do Google na internet j esto acostumados com os modos
de visualizao dos mapas. No Android, essas opes tambm esto presentes.
O tipo do mapa configurado pelo mtodo setMapType(tpo) da classe Goog1eMap, o
qual recebe uma das seguintes constantes:

GO0glM3p . MAP_TYPE_NONE

Modo de Visualizao mais simples do mapa, de forma que nenhuma


informao extra exibida.

Goog1eMap.MAP_TYPE_NORMAL

Modo de visualizao-padro dos mapas, no qual podemos visualizar as


ruas, estradas e riOS

GoogleMap.MAP_TYPE_SATELLITE

M odo de ViSualizaO com os dados do satlite.


632 Google Android - 4' edio

GoogleMap.MAP_TYPE_HYBRID

Modo de visualizao com os dados fotogrcos do satlite, com os mapas


das ruas. o modo de satlite mais detalhado.

Googleap.MAP_TYPE_TERRAIN

Modo de visualizao que exibe os dados topogrcos do mapa.

22.11 Colocando os conceitos em prticas


Depois de aprendermos mais detalhes sobre a API de mapas, como controlar o
zoom c alterar o tipo de visualizao, vamos escrever um pouco de cdigo para
deixar a tela de mapa mais interessante.
Crie o seguinte arquivo de menu para o fragment dos mapas:

Gi /res/menu/menu_frag_mapa.xmI
<menu mlns:androd="http://schemas.androd.com/apk/res/androd"
mlns:app="http://schemas.android.com/apk/res-auto">

<tem
android:id:"@+id/action_locaton_carro"
android:ttle="@strng/mapa_locaton_carro"
androd:icon:"@drawable/c_acton_place"
app:showAsActon="always" />

<tem
androd:id:"@+d/acton_locaton_directions"
androld:ttle="@string/mapa_locaton_directions"
android:icon:"@drawable/lc_acton_drectons"
app:showAsActon="always" />

<tEm
androd:id:"@+id/action_mapa_normal"
androd:title:"@strng/mapa_normal"
app:showAsActon="never" /

t9
androd;id:"@+td/actlon_mapa_satelite"
androld:title="@strng/mapa_satelte"
app:showAsActon="never" /
Captulo 22 1 Mapas
633

<item

a"drd3d="@+d/action_mapa_terreno"
androld:title:"@5tf"9/WD_terreno"
app:showAsAction="never" />

<tem

a"dfd 3 d= " @+id/acton_mapa_hibrido"


android:title:"@string/mapa_hibrido"
app:showAsAction="never" />

<item

android:id="@+id/action_zoom_in"
android:title:"@string/mapa_zoom_in"
app:showAsAction="never" />

<item
android:id="@+id/action_zoom_out"
android:title:"@string/mapa_zoom_out"
app:showAsActon="never" />

Esse arquivo de menu contm opes para alterar o modo de visualizao do mapa
(normal, satlite, terreno e hbrido) e aes para aumentar ou diminuir o zoom.
Tambm adicionei uma ao para mostrar a localizao da fbrica do carro e outra
para mostrar uma rota da localizao atual at a localizao da fbrica do carro.
Para o cdigo compilar, crie estas strings:

/res/values/strings.xmI
<?m1 version="1.0" encoding="utf-8"?>

<strng name mapa _norma1">Modo Norma1


<string name mapa
satelite">Modo _
Sat1ite</StF9>
<string name mapa_terreno">Modo Terreno</5tfl9>
<string name mapa hibrido">Modo Hibrido</5tf}9> .
<strng name mapa 1ocaton_directions">Direoes I
<strin9 name mapa location_carro">Loca1izaao do Carro
<string name mapa_zoom_in">Z00W (+)</5tr19>
<string name mapa_zoom_out">Z00I'1 (' )</5t"9>
634 Google Android - 4 edio

Feito isso, basta inflarmos esse menu para mostrar os itens na action bar. O cdigo
est comentado, portanto leia com ateno. Lembre-se de que para um fragment
adicionar aes na action bar ele precisa chamar o mtodo setHas0ptionsMenu( true).

MapaFragment.java

public class MapaFragment extends BaseFragment {

public View onCreateView(Layoutlnater inater, ViewGroup container,


Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_mapa, container, false);
setHas0ptionsHenu(true);

return view;
}

@0verride
public void onCreate0ptionsHenu(Henu menu, Henulnater inater) {
super.onCreate0ptionsMenu(menu, inater);
inater.inate(R.menu.menu_frag_mapa, menu);
}
@Override
public boolean on0ptionsItemSelected(HenuItem item) {
if(map != null && carro != null) {
if (item.getItemId() == R.id.action_location_carro) {
// Posiciona mapa na localizao da fbrica
LatLng location = new LatLng(Double parSeDOUble(carr0.latitude),
Double.parseDouble(carro.longitude));
map.animateCamera(CameraUpdateFactory.newLatLngZoom(loc ation, 13));
} else if (item.getItemId() == R.id.action_location_directions) {
// Posiciona mapa no usurio
toast("Mostrar rota/direes at a fbrica.");
} else if (item.getItemId() == R.id.action_zoom_in) {
toast("zoom +");
map.animateCamera(CameraUpdateFactory.zoomIn());
} else if (item.getItemId() == R.id.action_zoom_out) {
toast("zoom -");
map.animateamera(CameraUpdateFactory.zoomOut());
} else if (item.getItemId() == R.id.action_mapa_normal) {
// Modo Normal
'map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
} else if (item.getItemId() == R.id.action_mapa_satelite) {
Captulo 22 n Mapas 635
// Modo Satlite
map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
} else if (tem.getItemId() == R.id.acton_mapa_terreno) {
// Modo Terreno
map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
} else if (item.getItemId() == R.td.action_mapa_htbrtdo) {
// Modo Hibrido
map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
}

return super . onOptonsItemSelected(tem);


}

Desta vez, ao executar o projeto no emulador, teremos as opes para trocar o


modo de visualizao do mapa. A gura 22.5 mostra o mapa nos modos Normal 7

Satlite, Terreno e Hbrido. Tambm adicionei os botes Zoom (+) e Zoom (-), que
brincam com o zoom do mapa, basta voc testar. Todas essas opes ficam no
menu overflow da action bar. Como action buttons deixei apenas o boto que
vai posicionar o mapa na coordenada do carro e o boto para traar uma rota
(falaremos da rota posteriormente).
Q 4 Q 205
un um.V ) .,
"-;7=I' L 43..?7"* J ";5*'7mz-
.M _;/1
~ za ' '
-.v-~`.----.ae-z-z~ ?*'=?'i' - .* '5~.~ `3, 333

,,z-__,~;,,,:~;._,@z.=:z;z,:z

9` ' ;1.',W:-
zu
_= V
m.z:.*~
1:1 2
CEI rw,
vlr
Yzcaat.
i
fz>.~z=

Em
wa . fr
>'A z

I _ mv mf ms ...uns
,~z _
'gzuzeams
. V t __ msvv 2 V
az - * . `

Figura 22.5 - Modos de visualizao do mapa.

22.12 CameraPosition
Conforme j estudamos, 21 classe CameraPoston representa a posio do mapa e,
' " bm o zoom. Igualmen
nela, podemos configurar tanto a localizaao como tam
te vinnos q_ ue a classe utilitria CameraUpdateFactory pode facilitar o trabalho, pois
636 Google Android - 4' edio
realiza dois passos ein um s. Contudo, a classe CameraPosition faz muito mais do
que apenas informar a localizao e o zoom, sendo assim, vamos explorar alguns
mtodos importantes como o bearing(graus) e tilt(graus).
Vamos continuar os estudos sobre a API de mapas, mas faremos isso fora do pro
jeto dos carros. Para acompanhar as prximas explicaes, abra o projeto deste
captulo Demo-MapsV2 no Android Studio. O trecho de cdigo a seguir demonstra os
mtodos bearing(graus) e tilt(graus), que tambm podemos informar para turbinar
a visualizao do mapa:
Googleap map = ...;
// Localizao do mapa (Av. Paulista - SP)
LatLng latLng = new LatLng(-23.564224,-46.653156);
nal CameraPosition position = new CameraPosition.Builder()
.target(latLng) // Localizao
.bearing(G) // Rotao da cmera em graus
.tilt(0) // ngulo em que a cmera est posicionada em graus
.zoom(15) // Zoom
.build();
Cameraupdate update = CameraUpdateFactory.newCameraPosition(position);

Note que a linguagem java permite quebrar a linha depois do ponto, para chamar
mltiplos metodos em sequncia, mas isso tambm pode ser escrito em uma
nica linha de cdigo, conforme a sua preferncia:
nal CameraPosition position =
new CameraPosition.Builder().target(latLng).bearing(0).tilt(0).zoom(15).build();
Camerapdate update = CameraUpdateFactory.newCameraPosition(position);

Nota: a palavra cmera pode gerar alguma confuso caso voc a associe com
uma cmera que tira fotos. Na verdade, o termo cmera utilizado porque a
visualizao do mapa funciona exatamente como se estivssemos enxergando
o mundo com uma cmera em um plano linear. Por pad ro, a cmera exibe os
mapas de cima para baixo, mas podemos alterar esse ngulo de visualizao,
rotao da cmera etc.

22.13 CameraPositon - bearing rotao


Um dos parmetros mais interessantes da classe CameraPosition e o bearing, que
configura a ,orientao que a cmera est apontando.
Captulo 22 1 Mapas
637
Para entendermos
cionado nomelhf,
Brasile coma gura
Vamos vericar = .226, que exibe o mapa posi
mente o - . um zoom O. Note ue as uras do ce tro d d' `
foram rotacionadas em 45 90 ' q g n e a, .lrelta
ff , respectivamente. Esse comportamento e Justa

. .I.,, ~I.
que faz a propriedade bearing da classe CameraPosti.on.

`x
` Map Map
7_

4+A+ . . /
'

fE
fe

Figura 22.6 - Parmetro bearing (rotao) com os valores 0, 45 e 90.

Nota: para rotacionar o mapa manualmente pelo touch, utilize dois dedos e faa
um crculo como se estivesse girando uma moeda. Na prtica, isso o mesmo
que alterar a propriedade bearing.

22.14 CameraPosition - tiItincIinao"


Outro parmetro muito interessante da classe CameraPoston o tlt, que congura
a inclinao da cmera. A gura 22.7 exibe o mapa na Av Paulista - SR estando as
guras do centro e d a direita com uma inclinao (tilt) de 45 e 90 respectivamente.
O efeito de inclinao fica ainda mais claro quando o mapa est com um zoom
. =Por
maior. umas cidades
exemplo, ' exibem
a parnr do zoom visualiza
12 318 J es

em 3D. A gura 22.8 mostra o mesm _maior.
o endereo com um zoom . Nela, pos
Sfvel
1 Visualizar 05 mapas em 3D. Nesse exemplo, podemos visualizar claramente
como a inclinao afeta a visualizao do mapa. Avisualizao 3D e a inclinao
da Cmera so melhorias importantes que foram feitas na API V2.
`xi"
c ~Q
_ ' ,_

\I $\
:kar
z .. '7Q

`^
,,S\ e'
G*
,
_`.,

z-. [I]
_\<_.+.-f`
,ktKM
'V\:\ xx ,._.
.f
\`N,
fr
\_
638 Google Android - 4 edio
Mapa vz E M,,,a-V :___ Mapa \/,_
.f_ _ A,
_ ir .
L 3
N

X,
vn
.
r

_ V
`
N `1\~_j
l"
v;, G ~._._\(z] rf z z Q] V
1.

gz`_ ~' il* + 1` +


.VL {/<, Ok l \\ `X~` *fi
i_
if z` Jdv'dg,
(zh
izif 19 Q_ (`~ z. 1
/.yQ
.
,
Uf.'\&i0 luzqie Mans Amimid API wi Lxwmpiu Ooqle l\f'.1..\: A.r\drud API v2 H`Yi1'0 GOOQIG M)~ z\' 1 X z

.m3 <'l
_I Wm

;Mapa-V2
E! 9Mapaq/2
\ sMapa-V2
9 ' dia 9
< 96
Hgzmz 22.7 - Puinzctro til! (/im'1na) com os Lulfws O, 45 c QO.

E:.>'nI' (('Q1} me /`\'\(1'(i( AFN ` Lxeinpin Google Maps f\r\(`"("'( API vf' i'x>m,:z~ "

, af.
U

L]

.~ `i \ \ \\\ _
f N \

_ .` _ , .,_ U ir, zw
1'I`Ill122.8 - z\f1.1m um 51) mm H/1 (1H1HIg zli' mm >> 1.1/z_~> . 4.1 U .

22.15 Monitorando os eventos do mapa


lhclzls as vezes que zi P()Si\lU du mapa c ;1llL`I`11Ll;l_ pndcinvw I11()I1iI()I`1I`L`5SL` cwiiiw
c l`CL`llPL`I`1li` em ICIHPUlL*CXL`Cllil)l()d;lS1lS])I`()PI`iL`LiZlLik`S da Ii()\';l psi. wnm
;1 lzuirude L- ]()Ii1ll1(lk`, zm. lili c lwciring. l;11';1 issu lmsm ehzxmm' nicrwdw
Googleffiap.set0nCameraChangeListener(Onamerahangestener) L* iI1i[\IL`I1iL`Ilf;ll`(i |nCli~
onCameraChange(poston) lu il[(`I`|`;ik`L' Onamerahangestener.
Captulo 22 1 Mapas 639
@Override

public void onCameraChange(CameraPosition position) {


// MUOU 6190 na CameraPosition
}

Outro mtodo utiliz


ado para monitorar os eventos o Goog1eMap.set0nMapC1ickList
ener(0nMapClickListener). Nesse
caso, ao tocar no mapa, o mtodo onMapC1ick(LatLng)
da interface 0nMapC1ickListener ser cha
mado, o que permite recuperar a localizao
exata do toque.
@Override

public void onMapClick(LatLng 1atLng) {


Toast.makeTet(getContet(), "Clickz " + iatLng, Toast.LENGTH_SHORT).show();
CameraUpdate update = CameraUpdateFactory.newLatLng(latLng);
map.animateCamera(update);
1

22.16 Marcadores

Os marcadores permitem adicionar guras ao mapa, para marcar os locais


de interesse, a m de exibir informaes importantes e interagir com o usu
rio. Um marcador representado pela classe Marker e criado com o mtodo
GoogleMap.addMarker(marker0ptions), conforme demonstrado neste cdigo:

// Adiciona um marcador
private void adicionarMarcador(Goog1eMap map, LatLng latLng) {
Markerptions marker0ptions = new Marker0ptions();
marker0ptions.position(latLng).title("Meu Marcador").snippet("Livro Android");
Marker marker = map.addMarker(marker0ptions);
l
Para adicionar um marcador, utilizamos um objeto do tipo Marker0ptions, e para con
gurar a localizao do marcador, o mtodo position(LatLng). O retorno do mtodo
Goog1eMap addMarker(marker0ptions) o objeto Marker, que vai conter as propriedades que
lhe fram atribudas Por padro, o marcador tem o cone do Google Maps, mas caso
seja necessrio alterar o cone chame o mtodo Markeroption.icon(BitmapDescriptor):

// Adiciona um marcador 1
MarkerOptions H _ |_, _ H
private void adicionarMarcador(GooglMP W601 |-HU-U9 fl-HQ) f
markeroptong z new Marker0ptions();
marker0ptions position(1atLng).tit1e("Meu Marcador ).snippet( Livro Android );
markeroptong icon(BitmapDescriptorFactory_fromResource(R.mipmap.ic_launcher));
Marker marker z map.addMarker(8Fk@F0Pt0S)J
}
640 Google Android - 4 edio
Ao clicar no marcador, uma janela exibida com os textos denidos nas proprieda
des title e snippet. A gura 22.9 mostra essa janela, que chamada de info window. Para
customizar a janela infowindow, utilize o mtodo GoogleMap.setInfowindowAdapter( adapter)
passando como parmetro um objeto que implemente a interface InfoHindowAdapter.
Essa interface contm dois mtodos: getInfowindow() e getInfoContents(). O mtodo
getInfowindow() deve retornar a view que a borda da janela, alm de todo o con
tedo interno. Mas caso voc deseje alterar apenas o contedo interno da janela,
como ttulo e descrio, utilize o mtodo getInfoContents().

1
i Exemplo ~ Google Mao: Android API v2 2

Meu Marcador

1~ j V ` ` A `1

AI fan
R Pamplona ~ Y~=z.f-- wzz

AI Riofflaro
E2

T -in :masa m
ft l ., u
Figura 22.9 - janela-padro ao clicar em um marcador

Este trecho de cdigo demonstra como implementar o mtodo getInfoContents(),


que cria uma view dinamicamente:
// Adiciona um marcador
private void adicionararcador(GoogleMap map, LatLng latLng) {
Marker0ptions marker0ptions = new Marker0ptions();
marker0ptions.position(latLng).title("Meu Marcador").snippet("Livro Android");
marker0ptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher));
Harker marker = map addMarker(markerOptions);
// Customiza a janela ao clicar em um marcador
map.setInfoNindowAdapter(new InfowindowAdapter() {
@0verride
public View getInfoNindow(Marker marker) {
// Janela completa com contedo e borda (retorna null para deixar a janela-padro)
return null;
}
Captulo 22 n Mapas
641
@Override

public View getInfoContents(Marker marker) {


// View com o contedo

LinearLayout linear = new LinearLayout(getContext());


linear.setLayoutParams(new LayoutParams(LayoutParams.wRAP_CONTENT,
LayoutParams.wRAP_CONTENT));
linear.set0rientation(LinearLayout.VERTICAL);
Textview t = new TetView(getContet());
t.setText("*** View customizada *** ");
t.setTetColor(Color.BLACK);
t.setGravity(Gravity.CENTER);
linear.addView(t);
Textview tTitle = new TetView(getContext());
tTitle.setTet(marker.getTitle());
tTitle.setTetColor(Color.RED);
linear.addView(tTitle);
Textview tSnippet = new TetView(getContet());
tSnippet.setText(marker.getSnippet());
tSnippet.setTetColor(Color.BLUE);
linear.addView(tSnippet);
return linear;
}

});
}

A gura 22.10 demonstra o resultado desse cdigo, cuja janela interna, que exi

be os textos title e snippet, est customizada. Repare que, no cpdigo, obmtoo


getInfowindow(marker) est retornando null, porque nao queremos a terar a or a a
janela A borda da janela deve ser uma imagem com a extenso .9 (N1nePatch ou
9-patch) que um tipo de imagem especial que pode esticar sem perder a resoluo.

. - ' dada
3

Agora vamos alterar o mtodo getInfowindow(marker) para criar a view completa,


3

nuas delegaremos o trabalho para o mtodo getInfoContents(marker) que acabamos


. - w uma imagem de fundo arredon
de implementar. Depois de criar a vie ,
' forme
congurada para Sr 8 borda da janela, condemonstrado a seguir:
public View getInfowindow(M3Vker marker) {
. _ ~ t) this.getInfoContents(marker); // Cria a view
LinearLayout linear - (LIHGHFLHYOU
// Borda imagem 9-Patch
linear.setBackgroundResource(R. dF8wab1e.janela_marker); // Imagem de fundo 9-patch
return linear;

642 Google Android - 4 edio

= - 15 1
{ Exemplo ~ Google Maps Android API v2

_
rf Q 1% 1;-vz .f;=.,;'-3 os- ^ E
lt `
1 t.

l_;
, fx
' .

H?Livro
IIx *
:rw
~-~
1`' __;\.._l

ti' *f 1
- _V'
View
1 '.lz:Android

;:'
l*_ lt2
customlzada

' '1 1. , ,,.11


zl;1r<,a(f>r

, l ~. _3Al,Campinas
z . 1 1 p . _ A ,
H;-_;
H-
" .l'
:ff ~,' :z:- ~ . G Ff ' ga |
M,* 1
` \ / ~ * ff 52 'z
fz
` ,\ fl*
; 1 3 |;

z`;,L|

| R Pamplona 1 wo lvvbl 1 ff 3
l

; iti 1,,,
ll ~
V.` ea
. V
" eL
Al Rio Claro 1
1 {=<~-fvll + `
lL__,___
Gong'__"___,
_.-_oo..o
__ _ _ ,._

Figura 22.10 -janela com o contedo customizado.

Ao executar o exemplo, podemos visualizar a borda customizada da janela,


conforme mostra a gura 22.11. Na gura da esquerda, voc pode visualizar em
detalhes a imagem 9-patch que foi utilizada, e na direita, o resultado.

l, _.M
i1 .fya.
*,-
rz
zw ~.z' l__
za*
.mz
1 ~'*_,. '. t
.
<-
, Exemplo Google Maps androod API v2

\_1, :1_',4, .
I 'H V|ew:ustom|zada~~

. .
E

l Met;f.~lu~ra:,:
' Livro Android
.. , ` H ~,,,, _.

QfVll2'229,i
z

_
,
z V"~=
' R Pamplona ~~<z.[]

Al H|o'L,laro` .:~
Lnnon +
_ 1
p (.03lt* 41,3 ' _ _
Figura 22.11 - janela com a borda customizado.
retornarnu
..Q
Captulo 22 I Mapas 643
uecom' `leta~b ' . ' - . ,
Nota: o mtodo getInfoCon

view t@"'fS() somente chamado se o mtodo getInfowindow()


_ u se 0 metodo 9tIf0W1nd0w() retornar uma view, devera retornar uma
P , com a orda da janela e o conteudo. Se voc no retornar a borda,
q e a imagem de fundo, os textos voarao sem nenhum contorno ao redor.

j aprendemos a customiz
ar a aparncia da janela de detalhes do marcador ue
chamada de Info wlndow. Agora vamos aprender a monitorar o clique nesse marcador.
Para isso, basta utilizar o mtodo setOnMarkerClickListener(listener):
// Evento ao clicar no marcador
7

map.setOnMarkerClickListener(new 0nMarkerClickListener() {
@Override

public boolean onMarkerClick(Marker marker) {


LatLng lartLng = marker.getPosition();
Toast.makeText(getContet(), "Marcador: " + marker.getTitle() + " > " + lartLng,
Toast. LENGTH_SHORT) .show();
return false;
}

});

Porm, o mtodo onMarkerClick(marker) chamado quando o usurio clica em um


marcador, mas quando a janela info window ainda est fechada. Para tratar o evento de
clique na janela que est aberta, utilize o mtodo set0nMarkerClickListener(listener).
// Evento ao clicar no marcador
map.set0nInfowindowClickListener(new OnInfowindowClickListener() {
@0verride
public void onInfowindowClick(Marker narker) {
LatLng lartLng = marker.getPosition();
Toast.makeTet(getContext(), "Clicou no: " + fkef-9@T11e() + >
+ lartLng, Toast.LENGTH_SHORT).show();
}

});

_ , _ , - 'ma-em
- ~ortanto
' ' Paravoc no
tratar o
- ,- ~' ko1sCl'
naokListener(listener)
teria efeito algum.
Nota: a janela info window e renderizada como uma 1 g P
P --
ode adicionar botoes a essa view P
evento de Cl1qL1 116553set0nMar
- ' lize o metodo er Aic_ M W , _ ._
J2n1a HU
644 Google Android - 4' edio
22.17 Polyline -desenhando uma linha no mapa
A classe Poiyiine permite desenhar uma linha no mapa, que, na verdade, uma
sequncia de coordenadas.
No exemplo anterior, criamos o mtodo adicionarharcador(Googlehap,LatLng), 0 qual
adiciona um marcador localizao desejada. Podemos chamar esse mtodo
quantas vezes forem necessrias para adicionar vrios marcadores. Por exemplo,
o seguinte trecho de cdigo adiciona dois marcadores:
LatLng iatLng = new LatLng(-23.564224, -46.653156);
LatLng 1atLng2 = new LatLng(-23.555696, -46.662627);
I/ Adiciona os marcadores
adicionarMarcador(map, 1atLng);
adicionarMarcador(map, 1atLng2);

Nesse exemplo, as duas coordenadas so da Av Paulista - SP, mas em locais di


ferentes. Agora que temos essas duas localizaes, podemos brincar com a classe
Polyiine e desenhar uma linha entre esses dois pontos. O cdigo-fonte a seguir
mostra como desenhar essa linha entre dois pontos conhecidos.
private void adicionaPo1y1ine(Goog1eMap map2,LatLng 1atLng, LatLng iatLng2) {
// Desenha uma linha entre dois pontos
Po1y1ine0ptions line = new Po1yiine0ptions();
1ine.add(new LatLng(1atLng.1atitude, 1atLng.1ongitude));
1ine.add(new LatLng(1atLng2.1atitude, 1atLng2.iongitude));
1ine.coior(Co1or.BLUE);
Polyline polyline = map.addPo1y1ine(line);
poiyiine.setGeodesic(true);
}

A gura 22.12 exibe o resultado desse trecho de cdigo com uma linha entre os
dois marcadores. Apenas para brincar novamente com 0 conceito de inclinao
da cmera do mapa, a gura da esquerda est com a propriedade tilt = 0 e da
direita com o tiit = 90. Note que, embora o zoom da gura da direita seja maior
(mais prximo), o parmetro tilt permite inclinar a cmera, aumentando 0 ho
rizonte de visualizao.
Captulo 22 1 Mapas

Vista`auhsta
I,
4 Exem Io Goo IeM
Exemplo - Goo 9|& Maps Android API v2
AI Joaquim Eugnio de Lima p Q 3p$ Al'ldf0ld API v2
.1_ P I
tuna
El R Pampl
Qfv Al Rio Cl
R\m\>"Campos
uW

<e Cerqueira ar 3~:

QQ

rnM/
(9 Al M R h A

3
fi F C H P J M
H^91 f
[B C K + i
I E1
n z I :nun ]
<

na c
,
Av,Ang|ca
az zv I 49 a on /R
oogle A isbauas GOQ 4%
Figura 22.12 Dozs marcadores e uma lmha

22.18 Links teis


Neste captulo, estu d amos a API de mapas V2 O assunto e extenso e 1nteressante,
portanto recomendo que voc contmue os seus estudos Um bom comeo e a
documentao ocial.
Google Services - Google Maps Android API v2

https://developerandr0id.c0m/google/play servzces/maps html


Android Docs - Google Maps Android API v2

https://developers. google. com/maps/documentatzon/andro1d/


Android Docs - Displaying The Debug Certrcate Fmgerprrnt

https://developers. google. com/maps/documentatzon/androzd/start


` * cAPTuLo 23
Google Play Services e
tw localizao
'

O Google Play Services facilita a conexo com os servios do Google e, conse


quentemente, a utilizao de vrias APIs, como localizao, Google Fit, Google+
Google Drive, Games etc.
Neste captulo, vamos aprender a nos conectar aos servios do Google pelo Play
Services e a utilizar a API de localizao, conhecida como Fused Location Provider.
Uma vez que voc aprender a se conectar ao Google Play Services, ver que vrias
outras APIs podem ser utilizadas da mesma forma. No captulo 35, sobre sensores,
at vamos brincar com o Google Fit.

23.1 Introduo
j sabemos que o Google Play Services um aplicativo distribudo pelo Google
Play que apresenta funcionalidades essenciais para se comunicar com os servios
do Google, alm de vrias APIs.
At o momento, vimos que a API de mapas V2 faz parte do Google Play Services.
porm na prtica no foi necessrio se conectar aos servidores do Google. Neste
captulo, faremos essa conexo para obter a localizao por GPS. Lembrando que
naturalmente o Google Play Services precisa estar declarado como dependncia
do projeto.

app/buld.gradIe

Compile 'com.google.android.gms:play-services:7.0.0'

646
U''-.
CPUO 23 I Google Play Services e localizao 647
23.2 My Location

mp funcionalidade muito comum do aplicativo do Google Maps a bolinha


8Z_U ue representa a localizao do usurio. Pela API, essa bolinha azul pode ser
ativa a com apenas uma linha de cdigo:
GoogleMap map = ._,;
// Exibe a localizao do usurio
map.setMyLocationEnabled(true);

Essa bolinha azul chamada de My Location, e acredito que, se voc utiliza o aplica
tivo do Google Maps, sabe do que estou falando.
Porem, se voce deseja obter as coordenadas, e no apenas mostrar a localizao
do usuario no mapa, necessrio ligar o monitoramento de GPS.

23.3 Monitorando o GPS ( moda antiga)


Antigamente, antes de existir o Google Play Services, a classe Locationllanager era uti
lizada para obter a localizao do GPS. Explicarei rapidamente como funcionava o
monitoramento de GPS com essa classe, para focarmos logo no Google Play Services.
Tudo era feito com a classe LocationManager e o mtodo requestLocationUpdates( . . . ).

LocationManager locationManager = (LocationManager) getSystemService(Contet.LOCATION_SERVICE);


locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, implementao
de LocationListener aqui .);
O monitoramento do GPS costuma ser ligado e desligado nos mtodos onResume()
e onPause() da activity para vincular o funcionamento do GPS com o ciclo de vida
da activity muito importante cancelar o listener do GPS quando a activity
entra em estado de pause ou stop, para no gastar muita bateria sem necessidade.
Lembre~se de que o GPS um dos principais viles da bateria, portanto o utilize
somente quando necessrio:
@Override
protected void onResume() {
super.onResume(); // |-QG 0 GP5 _
locationanager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
}

@Override
protected void onPause() i
super.onPause(); // DESQH 0 GP5
locationManager.removeUpdates(h5)5
}
648

^ l II Google Android - 4 edio

A seguir, veja a explicao de cada parmetro do mtodo requestLocationUpdates(. . .):


lf|TlEI'0 Descriao _ _ gp ____W____
String provedor Nome do provedor de localizao (location provider).
Podemos utilizar as constantes Locationanager . GPS_PROVIDER
ot1Locationanager.NETNORK_PROVIDER.

long tempoinino Tempo em milissegundos que representa o intervalo mni


mo de tempo em que a aplicao deve receber atualizaes
sobre as localizaes. Se o valor for O (zero), o mtodo ser
chamado sempre que a localizao for alterada.
oat distanciatlinina Distncia em metros que representa a distncia mnima
necessria a ser percorrida para a aplicao receber atuali
zaes sobre as localizaes. Se o valor for O (zero), o mtodo
ser chamado sempre que a localizao for alterada.
LocationListener listener Implementao da interface LocationListener que re
ceber as atualizaes das coordenadas pelo mtodo
onLocationUpdate(location).

Feito isso, sempre que o Android tiver uma nova localizao, ele chamar
o mtodo onLocationChanged(location), passando como parmetro um objeto
and roid . location . Location.

@0verride
public void onLocationChanged(Location location) {
// Faa algo com esta coordenada aqui
double latitude = location.getLatitude();
double longitude = location.getLongitude();
}

Basicamente isso. Como vimos, simples. O mais importante que voc precisa
saber de tudo isso o conceito de provedor de GPS (GPS Provider). O mtodo
requestLocationUpdates( . . .) recebe como parmetro o provedor de GPS, que pode ser
uma das duas constantes LocationManager _ GPS_PROVIDER ou Locationanager.NETw0RK_PROVIDER.

O provedor Locationrlanager.GPS_PROVIDER utiliza o hardware do GPS, ou seja, tem


alta preciso. Porm, esse provedor geralmente demora em retornar o primeiro
resultado, tempo que pode variar desde 30 segundos a alguns minutos. Por isso,
nem sempre esse provedor o mais indicado.
O provedor LocationManager.NETwORK_PROVIDER utiliza a triangulao de antenas da
operadora para descobrir a localizao do usurio e tambm o sinal da rede
Wi-Fi. A grande vantagem desse provedor que ele retorna a primeira locali
zao rapidamente. No entanto, pode retornar a localizao com metros ou at
quilmetros de diferena, pois a localizao por meio de triangulao de antenas
calcula um valor aproximado.
M ~ ' ' . 1 1 y erviceselocalizao 649
Captulo 23 u Google Pla S

caso ~ - - .
1 af PIHO, qual provedor utilizar? O provedor GPS_PROVIDER preciso, porem e
en o]aeouti
-provedor NETw0RK_PROVIDER
izar a API antiga, era: Utilize no preciso, porm rpido. A resposta, no
os dois!
// Liga o GPS
LocationManager 1ocationManager z _ _ _;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, G), this);
locationManager.requestLocationUpdates(LocationManager.NETwORK_PROVIDER, 0, 0, this);

Dessa forma, sabemos que o provedor LocationManager.NETw0RK_PROVIDER vai retornar


a localizao de forma mais rpida, o que vai permitir que a localizao no mapa
seja atualizada rapidamente. No importa que a localizao no seja to precisa,
pois melhor mostrar algo para o usurio do que no mostrar nada. Passado
um tempo, o provedor LocationManager.GPS_PROVIDER que utiliza o hardware pode
retornar uma localizao mais precisa, que utilizada para atualizar o mapa.
Nesse momento, sabemos que o GPS por hardware j est funcionando, portanto
podemos cancelar o monitoramento do GPS por triangulao de antenas para
economizar recursos. Agora que j sabemos o que um provedor de GPS, vamos
ento estudar o provedor Fused Location Provider.

23.4 Monitorando o GPS (Fused Location Provider)


Conforme acabamos de estudar, existem dois provedores de GPS no Android:
GPS_PROVIDER e NETwORK_PROVIDER. Embora parea simples utilizar o GPS, na prtica no
. A maioria dos aplicativos que precisavam utilizar o GPS no fazia as otimizaes
necessrias e acabava utilizando recursos demais e gastando muita bateria. Na
verdade, utilizar 0 GPS de forma otimizada era um grande desafio.
Foi ento que nasceu o Fused Location Provider, um provedor de GPS criado
pelo Google, que faz parte do Google Play Services. .Esse provedor encapsula o
acesso aos provedores GPS_PROVIDER e NETw0RK_PROVIDER, aplicando todas as otimizaoes
e boas prticas necessrias. Resumindo, o Google foi l e resolveu o problema.
com a nova API de localizao do Google Play Services, possvel utilizar o GPS
dg fgfma simples e transparente, com a garantia de otimizar ao maximo o uso
. . ' 'isso.
' Goo le Play
dos recursos e bateria.
_ - azer a API,
Contudo, antes de utilizar e necessario
a nov _ se conectar ao g
Services; sendo assim, vamos aprender a f
650 Google Android - 4 edio
23.5 Conectando-se ao Google Play Services
Para utilizar qualquer uma das APIs gerenciadas pelo Google Play Services,
necessrio se conectar aos servios do Google. A classe responsvel por fazer essa
conexo a Goog1eApClent, que encapsula toda a comunicao com os servidores
do Google. Isso feito com o seguinte cdigo:
Goog1eApClient mGoog1eApC1ient = new GoogleApClient.Bu1der(ths)
.addConnectonCa1lbacks(this)
.addOnConnectionFaledListener(this)
.addAp(LocatonServices.API)
.buId();

Veja que foi informada a API de localizao com o mtodo addAp(LocattonServces.API).


Porm, como dissemos antes, existem vrias APIs que fazem parte do Google
Play Services. Portanto, apenas para exemplicar, o seguinte trecho de cdigo
mostra como se conectar ao servio do Google Drive. Basta adicionar a linha
addAp(Drve.API).

Goog1eApClent mGoogleApC1ent = new Goog1eApC1ent.Bu1der(ths)


.addConnectionCa1Ibacks(this)
.addOnConnectonFaIedLstener(this)
.addAp(Drtve.API)
.addScope(Drve.SCOPE_FILE)
.buld();

Depois de construir o objeto Goog1eAptC1ent, o que geralmente feito no


mtodo onCreate() da activity ou fragment, podemos chamar o mtodo
Goog1eApC1ent.connect() para realizar a conexo. Segundo a documentao ocial,
o Google recomenda realizar a conexo no mtodo onStart() da activity e fechar
a conexo no mtodo onStop(), conforme demonstrado neste trecho de cdigo:
@0verrde
protected void onStart() {
super.onStart();
mGoog1eApC1ient.connect();
}

@0verrde
protected void onStop() {
super.onStop();
mGoogleApClent.disconnect();
}

Para receber o resultado da conexo, seja sucesso ou falha, devemos implementar


os mtodos das interfaces ConnecttonCa1lbacks e 0nConnectionFaledLtstener.
CHPUO 23 I Google Play Services e localiza
@Override
651
public void onConnected(Bundle connectionHint) {
// Conectado ao Google Play Services!
// Podemos utilizar qualquer API agora,
}

@0verride

public void onConnectionSuspended(int cause) {


// A conexo foi interrompida.

} // A plicao precisa aguardar at a conexo ser restabelecida


@0verride

public void onConnectionFailed(ConnectionResult result) {


// Erro na conexo.
// Pode ser uma congurao invlida ou falta de conectividade no dispositivo.
}

Agora que sabemos como nos conectar aos servios do Google, vamos estudar a
API de localizao do Google Play Services.

23.6 Obtendo a ltima localizao de forma eciente


Com o Google Play Services, uma das principais melhorias feitas na API de loca
lizao a facilidade de se obter a ltima localizao do usurio de forma rpida.
Uma vez que a aplicao est conectada aos servios do Google, basta utilizar
uma linha de cdigo para recuperar a ltima localizao conhecida pelo sistema.
GoogleApiClient mGoogleApiClient = . . _
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);

Para praticar o conceito, vamos criar o projeto Mylocation. No entanto, se preferir,


abra o projeto pronto no Android Studio. No layout da activity vamos inserir o
mapa com a classe SupportMapFragment.

/res/layout/activity_main.xml
_,_-u
<LinearLayout android:orientation-"vertical . . >
<TetView android:id="@+id/text" _ H
androd-iayout wdth="wrap_content" android:layout_height="wrap_content
androd-tet="Mostra como obter a ltima localizaao pela API."/>
<fragment android:id="@+d/Wap" . .. H
andr0d'1ay0Ut wdth="match_parent" android:layout_height= match_parent
class="com google.android.9ms.maD5-5UPPrtMaPFra9et" />
652 Google Android - 4 edio
Quando a aplicao abrir, o mapa ser mostrado localizao padro. Vamos
inserir um boto na action bar que, ao ser clicado, vai recuperar a ltima localiza
o conhecida e centralizar o mapa nesse local. Esse boto vai simular o mtodo
setMyLocationEnab1ed(boolean) da classe Goog1eMap.

@ /res/menu/activity_main.xml
<menu . . .>
<item android:id="@+id/action_my_1ocation"
android:tit1e="@string/action_my_1ocation"
android:icon:"@drawable/ic_action_1ocation"
app:showAsAction="a1ways" />

Nota: para este projeto funcionar, preciso declarar todas as permisses e


congurar o arquivo de manifesto, como zemos no captulo anterior. Atente
se para a chave API Key, que gerada na pgina de console do Google. No meu
caso, o pacote do projeto br.com.1ivroandroidlocation, que diferente do pacote
br.com.livroandroid.carros do aplicativo dos carros. Portanto, necessrio gerar
outra API Key. No dia a dia, cada aplicativo tem um pacote diferente, ou seja,
cada um deve ter a sua API Key.

No cdigo da activity vamos obter 0 SupportMapFragment, como j zemos ante


riormente, e tambm vamos nos conectar ao Google Play Services. Tudo isso j
aprendemos a fazer, portanto o cdigo a seguir uma reviso. Leia atentamente.
pois, apesar de extenso, o cdigo est comentado.

ManAttivity.java
public class MainActivity extends AppCompatActivity implements OnHapReadyCaI1back,
Goog1eApiC1ient.ConnectionCa11backs, Goog1eApiC1ient.OnConnectionFai1edListener {
private static nal String TAG = "1ivroandroid";
protected Goog1eMap map;
private SupportMapFragment mapFragment;
private Goog1eApiC1ient mGoog1eApiC1ient;
@0verride
protected void onCreate(Bund1e icicle) {
super.onCreate(icic1e);
setContentView(R.1ayout.activity_main);
// Fragment de mapa
mapFragment = (SupportMapFragment) getSupportFragmentManager().ndFragmentById(R.id.map);
mapFragment.getMapAsync(this);
Capt"| 23 ' G9|@ Play Services e localizao

/ C9Ura o objeto GoogleApiClient


mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.buld();
}

@Override

public void onMapReady(GoogleMap map) {


L09-d(TAG, "onMapReady: " + map);
this.map = map;
// Congura o tipo do mapa

} WHP.setMapType(GoogleMap.MAP_TYPE_NORMAL);
@Override

protected void onStart() {


super.onStart();
// Conecta ao Google Play Services
mGoogleApiClient.connect();
}
@Override
protected void onStop() {
// Desconecta do Google Play Services
mGoogleApiClient.disconnect();
super.onStop();
}
@Override
public void onConnected(Bundle bundle) {
toast("Conectado ao Google Play Services!");
}

@Override
public void onConnectionSuspended(int cause) {
toast("Conexo interrompida.");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
toast("Erro ao conectar: " + COHHGCOHRGSU);
}

@Override
public boolean onCreate0ptionsMenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return super.onCreate0ptionsMenu(WGHU)5
}
654 Google Android - 4 edio
@Override
public boolean on0ptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_my_location:
// Obtm ltima localizao
Location l = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
Log.d(TAG, "lastLocation: " + l);
// Atualiza a localizao do mapa
setMapLocation(l);
return true;
}

return super.on0ptionsItemSelected(item);
}

// Atualiza a coordenada do mapa


private void setMapLocation(Location l) {
if (map != null && l != null) {
LatLng latLng = new LatLng(l.getLatitude(), l.getLongitude());
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(latLng, 15);
map.animateCamera(update);
Log.d(TAG, "setMapLocation: " + l);
Textview text = (Textview) ndViewById(R.id.tet);
tet.setTet(String.format("Lat/Lnt %f/%f, provider: %s", l.getLatitude(),
l.getLongitude(), l.getProvider()));
// Desenha uma bolinha vermelha
Circleptions circle = new Circle0ptions().center(latLng);
circle.llColor(Color.RED);
circle.radius(25); /I Em metros
map.clear();
map.addCircle(circle);
}

private void toast(String s) {


Toast.makeTet(getBaseContext(), s, Toast.LENGTH_SHORT).show();
}

Ao executar o exemplo, o mapa mostrado na localizao padro, e ao clicar no


boto minha localizao que adicionamos action bar o mapa ser posicionado na
ltima localizao conhecida. Como um plus para o exemplo, foi mostrado como
desenhar um crculo com a classe Circle0ptions e o mtodo addCircle(circle). Fiz
isso para simular o que o mtodo setMyLocation(boolean) faz. A gura 2.3.1 mostra o
resultado deste exemplo.
9 E ay Services e localizao 655
Captulo 23 . Goo | p|

LH*/Lm 951441071/~49.345422. provider: fused

'Y'

rigol de 50513
lc
R P\o\ a , ado
lgev H2\\' Ne
I'
lmulv

=,\%

<.o\>*oj

f>

'E

< . ol\; 3

Figura 23.1 - Obtendo a ltima localizao.

Dica: embora o mtodo setMyLocationEnabied(boo1ean) da classe GoogleMap faa a


mesma coisa que esse exemplo acabou de fazer, aprendemos a obter a localizao
pela API. O mtodo setMyLocationEnab1ed(boo1ean) apenas mostra o boto de ny
location e desenha a famosa bolinha azul no mapa, mas no fornece a localizao
ao aplicativo. Lembre-se de que este projeto tambm mostrou como se conectar
ao Google Play Services.

23.7 API de localizao do Google Play Services


Para iniciar o monitoramento de GPS, necessrio criar um objeto LocationRequest,
que contm as configuraes referentes preciso e ao intervalo de tempo com
os quais voc deseja receber as coordenadas.
LocatonRequest nLocationRequest = new LocationRequest();
nLocationRequest.setInterval(10000); // 10 segundos
nLocationRequest.setFastestInterva1(5000); // 5 segundos
nLocationRequest . setProrty( LocationRequest . PRIORITY_HIGH_ACCURACY);

O mtodo setPrority(int) dene a preciso do GPS. As constantes mais utiliza


das s o PRIORITY HIGH ACCURACY e PRIORITY_LOw_POwER. A primeira tem como objetivo
retgmaf a localizao mais precisa possvel. A segunda visa obter uma localizao
656 Google Android - 4 edio
aproximada a fim de economizar bateria. Outra constante interessante a
PRIORITY_N0_P0wER, que no inicia o monitoramento do GPS, mas garante que, se
outra aplicao no sistema operacional o zer, voc receber essa localizao.
O mtodo setInterva1(1ong) recebe o intervalo de tempo em milissegundos em que
a aplicao deseja receber atualizaes do GPS. Porm, o Google Play Services
compartilha os recursos com todas as aplicaes instaladas, e, se alguma aplicao
solicitou um intervalo de tempo menor que a sua, o Android vai enviar os dados
para sua aplicao nesse intervalo menor. Por um lado, o Google Play Services
otimiza os recursos e entrega de forma eciente a nova localizao para todas
as aplicaes; por outro lado, sua aplicao pode receber as atualizaes em um
intervalo de tempo menor do que o esperado.
Como 0 Android pode mandar atualizaes freneticamente, dependendo da
congurao do setInterva1(1ong) de todas as aplicaes instaladas, foi criado o
mtodo setFastestInterva1(1ong), que recebe o intervalo mnimo no qual sua aplica
o consegue receber e tratar os eventos corretamente, sem afetar a performance
da aplicao. Dessa forma, garantido que voc no receba nenhuma atualizao
antes desse intervalo.

Depois de criar o objeto LocationRequest com as devidas conguraes, podemos


utilizar os seguintes mtodos para iniciar ou parar o GPS.
GoogieApiC1ient nGoog1eApiC1ient = . . .
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogieApiC1ient, mLocationRequest, this);
}

protected void stopLocationUpdates() {


LocationServices.FusedLocationApi.removeLocationUpdates(
mGoog1eApiC1ient, this);
}

O momento correto de iniciar 0 GPS logo depois de a conexo com 0 Google


Play Services ser realizada, portanto isso deve ser feito no mtodo onConnected().
@0verride
public void onConnected(Bund1e connectionHint) {
// Conectado ao Google Play Services!
startLocationUpdates(); // Inicia o GPS
}

Se quisermos parar o GPS podemos utilizar os mtodos do ciclo de vida da activity


ou fragment, como o onPause() ou onStop().
Captulo 23 1 Goo Ie PI S 657
9 ay erviceselocalizao
@Override

protected void onPause() {


super.onPause();
stopLocationUpdates(); // Para o Gpg
}

a ton an e l . _
Por ltimo ar - ~
ODLOC t. Ch , pd a receber as localizaoes, devemos implementar o mtodo
9 ( PCHWW) da interface Locationustener. Pronto, isso tudo!
@Override

public void onLocationChanged(Location location) {


double latitude = location.getLatitude();
double longitude = location.getLongitude();
// Faa algo com a latitude/longitude aqui.
}

Para ver um exemplo completo de cdigo que utiliza o servio de localizao do


Google Play Services, abra o projeto I.ocationUpdates no Android Studio. Basicamente,
esse projeto idntico ao exemplo que zemos anteriormente, mas eu removi o
boto da action bar que mostrava a ltima localizao.
O objetivo deste exemplo mostrar o mapa, conectar-se ao Google Play Services
e ligar o GPS. Sempre que uma nova localizao for recebida, a posio do mapa
ser atualizada, assim como a bolinha (circle) que desenhamos no mapa para
indicar a atual posio. O Textview que est no layout vai mostrar a latitude e lon
gitude, assim como a ltima hora em que o aplicativo recebeu uma localizao.

MainActivity.java
public class MainActivity implements 0nMapReadyCallback, GoogleApiClient.
ConnectionCallbacks, GoogleApiClient.0nConnectionFailedListener,
com.google.android.gns.location.LocationListener {

@Override
protected void onCreate(Bundle icicle) i - - ~}
public void onMapReady(G0091@MaP apl { ' ' '}
protected void onStart() { - - - l
}

@Override
protected void onSt0D() {
// Para o GPS
stopLocationUpdates( ); _
// Desconecta do G009l@ Play Serv1CS
mGoogleApiClient.disconnect();
super.onStop();
658 Google Android - 4 edio
}

@Override
public void onConnected(Bundle bundle) {
toast("Conectado ao Google Play Services!")'J
// Inicia o GPS
startLocationUpdates();
}

public void onConnectionSuspended(int cause) { . . .}


public void onConnectionFailed(ConnectionResult connectionResult) { . . .}
// Atualiza a coordenada do mapa
private void setMapLocation(Location l) {
. . . // tudo igual antes ...
}

/** TUDO IGUAL A0 EXEMPLO ANTERIOR "My Location"


'k
O que muda essa parte daqui para baixo...
* Adicionados mtodos para fazer start e stop do GPS
* Veja tambm os mtodos onConnected(bundle) e onStop() */
protected void startLocationUpdates() {
Log.d(TAG,"startLocationUpdates()");
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, this);
}

protected void stopLocationUpdates() {


Log.d(TAG,"stopLocationUpdates()");
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}

@0verride
public void onLocationChanged(Location location) {
Log.d(TAG,"onLocationChanged(): " + location);
// Nova localizao: atualiza a interface
setHapLocation(location);
}

Recomendo executar esse exemplo em um dispositivo real e fazer seus testes. Esse
cdigo atualiza a posio do mapa sempre que uma nova localizao recebida
do GPS. Note que a activity implementa a interface LocationListener e o mtodo
onLocationChanged() para receber as localizaes.
Nota:l _ - - , _
<aP*' 23 ' G9l Play Services e localizao 659

ACCESS se de que para utilizar o GPS e necessrio declarar as permisses


man - _ CATION (GPS por hardware) ou AccEss_coARsE_LocAT1oN (Wi-Fi ou
o b_
jetoeu aaf> de antenas), dependendo das configuraes de preciso feitas no
Locat1onRequest.

23.8 Desenhando uma rota entre dois pontos


Um requisito muito comum em aplicativos a necessidade de traar uma rota
entre dois lugares conhecidos. Isso pode ser feito de duas maneiras: a primeira
utilizando uma intent para pedir ao aplicativo do Google Maps que desenhe a
rota, a segunda desenhar a rota manualmente por programao.
Para a grande maioria das aplicaes, no acredito que seja vivel traar uma
rota desenhando manualmente sobre o mapa. A justicativa simples: voc pro
vavelmente no far melhor do que o aplicativo do Google Maps faz. Ento, a
no ser que voc esteja fazendo um aplicativo de mapas customizado, a maneira
mais fcil e recomendada de traar uma rota disparar uma intent, informando
a coordenada inicial e a final. O aplicativo nativo do Google Maps vai interceptar
a intent e se encarregar do trabalho de traar a rota.
O trecho de cdigo a seguir mostra como disparar uma intent para mostrar uma
rota entre duas coordenadas:
// Dispara uma intent para traar uma rota
String origem = "-25.443195, -49.289977";
String destino = "-25.442207, -49.278403";
String url = "http://maps.goog1e.com/maps?f=d&saddr="+origem+"&daddr="+destino+"&hl.=pt";
startActivity(new Intent(Intent.ACTION_VIEw, Uri.parse(ur1)));

A gura 23.2 exibe o resultado:


No entanto caso seja realmente necessrio traar a rota manualmente, voc
7

P oder fazer uma requisio HTTP neste Web service do Google, informando as
coordenadas de origem e ClSI1I101

// Descobrir a rota
String origem z "-25.443195, -49180977 ;
string destino = "-25.442207, -49-2784@3"1 _ _ _ __ _
String url ' llllttp'//Fips-g00Ql.D`l.S.COf'i/l'iDS/3D/d`iI`C`lOI`iS/]S0?0I`1g`Ll'i= + origem +
"&destination="+destino+"8zsensor=true8mode=driving";

String json z HttpHe1per.doGet(uFl);


660
Google Androiil - 4 edio

Y
a \f`
f

' W av
al
lf`
\
~
l

( Ir H
. - nu

a
_
1 io? f
'O wifi
-\ _ _.z~.4
fp*,,...ei
.PW . '
I.'V)
K" "

w^'l|1 z
L .1 ~{-"'H

mnecnons usr )
Figura 23.2 - Desenhando uma rota.

O retorno ser um JSON que contm as coordenadas da rota, separadas por


pontos com a latitude e a longitude. Depois de fazer o parser e obter a lista de
coordenadas, voc poder fazer um loop e desenhar vrias linhas utilizando o
conceito de Polylne, que estudamos no captulo anterior. Se precisar de ajuda,
faa a busca no Google por Google Directions API. Com certeza, encontrar posts em
blogs com bons exemplos de cdigo.
Agora vamos voltar a falar um pouco do projeto dos carros. Na tela do mapa,
lembre-se de que deixamos um boto para traar a rota, cujo identificador da
ao R.d.action_1ocaton_drections_
@0verrde
public boolean on0ptonsItemSe1ected(Menultem tem) {

if (tem.getItemId() == R.id.action_1ocaton_drections) {
// Poscona mapa no usurio
t0a5t("Mostrar rota/direes at a fbrica.");
}

O objetivo desenhar uma rota entre a localizao atual e a localizao da fbrica


do carro Mas isso vou deixar como exerccio para voc, caso queira praticar, pois
tudo 0 que voc precisa saber para implementar esse tipo de funcionalidade j
foi explicado.
Captulo 23 I Google Play Services e localizao 551
23.9 Buscando um endereo
Outra dica interessante que vale a pena mencionar como converter um endereo
para latitude e longitude.

List
Isso pode ser feito facilmente com a classe android.location.Geocoder e o m
todo getFronLocationNane(string), que retorna uma lista de objetos do tipo
android.location .Address, sendo que a classe Address contm a latitude e a longitude
do local encontrado. O retorno uma lista de possveis resultados, assim, geral
mente, o primeiro objeto o mais prximo do resultado:
// Para descobrir a latitude e a longitude do endereo
String endereco = "Av Paulista - SP";
Geocoder gc = new Geocoder(this, new Locale("pt","BR"));

Toast.makeTet(this, "Retorno: " + list, Toast.LENGTH_SHORT).show();

23.10 Links teis


Neste captulo, aprendemos a nos conectar ao Google Play Services e utilizar a
API de localizao. Para continuar seus estudos, separei alguns links interessantes.
Google Services - Accessing Google APIs

https://developer android.com/google/auth/apbclient. html


Android Training - Making Your App Location Aware

https://developer android.com/training/location/index.html
Google Developers - Google Directions API

https://developers. google. com/maps/documentation/directions/


,J ` cAPruto 24
BroadcastReceiver
\,
\.
T
~

Este captulo fala sobre a classe BroadcastRecever, uma das mais importantes na
arquitetura do Android. Ela utilizada para que aplicaes possam interceptar
eventos de sistema, ou seja, mensagens geradas por uma intent.
Na prtica, o broadcast receiver uma classe que consegue interceptar uma intent,
sendo a intent uma mensagem que pode ser enviada pela sua prpria aplicao,
por uma aplicao de terceiros ou pelo prprio sistema operacional.
Um broadcast receiver sempre executa em segundo plano (background) e no
utiliza interface grca. Seu objetivo interceptar uma mensagem (intent) e
process-la sem que o usurio perceba.

24.1 Introduo
A classe androd.content.BroadcastRecever utilizada para interceptar mensa
gens enviadas por uma intent. Quando a mensagem interceptada, o mtodo
onReceve(context,ntent) chamado, e executa em segundo plano, sem mostrar
nenhuma tela ao usurio.
Um broadcast receiver pode ser utilizado para enviar e receber mensagens dentro
da prpria aplicao ou para interceptar eventos de sistema. Um exemplo de evento
de sistema a mensagem SMS_RECEIVED, a qual enviada pelo Android sempre que
uma nova mensagem SMS recebida pelo sistema operacional. Portanto, caso seja
interessante para sua aplicao, possvel interceptar essas mensagens, ou seja.
esses eventos, a m de executar alguma aplicao.
Um broadcast receiver consegue interceptar uma intent/mensagem mesmo se a
aplicao estiver parada, e esse um de seus grandes benefcios.
Captulo 24 n Broadcastkeceiver 663
Um rec ` , . . . .
24.2 Congurando um receiver de forma esttica

ou de f ' ^ ' f - . .
eiver pode ser configurado de forma estatica no arquivo Andro1dManzfest.xml
orma dinamica no codigo. Para comear a brincadeira, vamos aprender
a congur-lo de forma esttica. A r' ` 1
gura
d opdeincipa vantagem de um receiver ser con
forma estatica e que ele consegue interceptar o evento mesmo se a
aplicaao estiver fechada,
ou seja,` e1e consegue acordar a aplicao se necessrio.
Para estudarmos o conceito, crie um projeto chamado HeIIoReceiver. No arquivo de
layout da activity vamos apenas colocar um simples boto que vai enviar uma intent.

/res/layout/activity_main.xmI
<LinearLayout mlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="16dp" android:orientation="vertical">
<Button android:id="@+id/btEnviar" android:tet="Enviar"
android:layout_width="wrap_content" android:layout_height="wrap_content" />

No cdigo da activity vamos disparar uma intent por broadcast. Isso feito com
0 mtodo sendBroadcast(intent).

MainActivity.java
public class MainActivity extends AppCompatActivity inplements View.0nClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ndViewById(R.id.btEnviar).setOnClickListener(this);
}

@Override

}dbd
public void onClick(View v) { _ _ _
// Dgpara a mensagem de broadcast para todas as aplicaoes instaladas
sendBroadcast(new Intef("3NG"))5
Toast.makeText(this, "Intent enviada!",Toast.LENGTH_SHORT).show();
}

_ , - - Seguinte mensagem e roa cast.


Ve]a que e-ste codigo C115Pafu a
sendBroadcast(new Intent("BINGO"));
- - ' t mas
utihzamos umaem vez
rnten , , de utilizar o m
Observe que novamente
t) a, mas
todo startActivity(inten P trocamos por sendBroadcast(intent). A diferena
654 Google Android - 4 edio
que o primeiro tem como objetivo chamar uma activity e o segundo tem
como objetivo enviar uma mensagem de broadcast para ser interceptada por
algum BroadcastReceiver. A palavra broadcast um termo comum para uma
mensagem que enviada para todo mundo ao mesmo tempo. Para interceptar
essa mensagem de broadcast, vamos criar a classe HelloReceiver, que filha de
android.content.BroadcastReceiver. Implementar um receiver no requer prtica nem
habilidade, pois existe somente o mtodo onReceive(contet,intent)_

HeIIoReceiver.java

import android.content.BroadcastReceiver;
public class HelloReceiver extends BroadcastReceiver {
@0verride
public void onReceive(Contet c, Intent intent) {
Log.d("livroandroid", " HelloReceiver !!!");
}

O parmetro intent do mtodo onReceive(contet,intent) exatamente a intent que


o receiver interceptou, ou seja, a mensagem que foi enviada por broadcast. E
para que tudo isso funcione basta congurar o receiver no arquivo de manifesto.
Isso feito com a tag , conforme demonstrado a seguir:
AndroidManifest.xmI
<nanifest . . .>
<application . . .>
<activity android:name=".MainActivity" . . . />
<receiver android:name=".HelloReceiver">
intent-lter
action android:nane="BINGO" />
category android:nane="android.intent.category.DEFAULT" /
</intent-lter
/receiver
</application

Observe que a congurao realizada no arquivo AndroidManifest.xm1 exatamente


a mesma para as tags e , e ambas declaram o nome de uma
classe e o <intent-lter>, que contm tanto a ao quanto a categoria para a qual a
classe deve executar. Dessa forma, quando alguma intent/ mensagem com a ao
BINGO for disparada, a classe HelloReceiver ser executada em segundo plano sem
interferir na tela em que o usurio est trabalhando ou se divertindo.
CaPtu|o 24 n BroadcastReceiver 655
Pronto isso
c1. ,icar
tudo. Parano bot"
conferir , .execute o projeto no emulador. Ao
o resultado,
30, 8 mensagem BINGO sera enviada por broadcast. Se tudo correr bem,
voce vera a seguinte mensagem nos logs do LogCat:
D/lvroandrodz Hel1oRecever E!!

Agora que voc j foi apresentado a mais uma classe do Android, pense um pouco
na arquitetura de Intent e IntentF1ter. Lembre-se de que a Intent representa uma
mensagem com a inteno de realizar algo, e o IntentF1ter o que ltra essa men
Sgftm por ao e categoria. Agora j sabemos que esse ltro pode ser configurado
para executar uma Activity ou um BroadcastRecever.

Note: uma vez congurado no arquivo de manifesto, qualquer aplicao pode


enviar uma mensagem que ser interceptada pelo receiver. Caso queira evitar
esse comportamento, congure o receiver com o atributo androtd:eported="fa1se";
assim, o receiver somente ir receber mensagens da sua prpria aplicao.

24.3 (ongurando um receiver de forma dinmica


Um BroadcastRecever pode ser congurado de duas maneiras diferentes.
1. De forma esttica, no arquivo AndroidManifest.xml com a tag . Isso
til quando precisamos interceptar a mensagem mesmo se a aplicao
estiver fechada. o caso de interceptar uma mensagem SMS ou Push, pois
geralmente esse tipo de mensagem precisa ser interceptada mesmo com a
aplicao fechada.
2. Registrar o receiver dinamicamente na activity Nesse caso, o receiver ca
atrelado ao ciclo de vida da activity ou seja, o receiver ficar ativo somente
enquanto a activity estiver aberta. Esse recurso muito utilizado para enviar
mensagens internas da aplicao. Por exemplo, um receiver pode interceptar
um evento de que o estado da conexo com a internet mudou, e enviar uma
mensagem local para avisar a activity se a aplicao est online ou offline.
Como registrar um receiver de forma esttica foi o exemplo que fizemos no tpico
anterior. Agora, vamos criar o projeto HeIIOReceiverDinamico para demonstrar como
registrar o receiver dinamicamente. Neste projeto no precisamos configurar
nenhuma tag <recever> no arquivo de manifesto, pois faremos tudo programti
camente. A seguir, podemos vericar o cdigo-fonte da activity:
555 Google Android - 4 ediao
MainActivity.java
public class MainActivity extends AppConpatActivity inplements View.OnClickListener {
private Broadcastkeceiver helloReceiver = new BroadcastReceiver() {
@0verride
public void onReceive(Context context, Intent intent) {
Log.d("livroandroid", "HelloReceiver Dinanico!!!");
// Este receiver uma classe interna
// Portanto, ele consegue chamar os mtodos da activity.
Textview text = (Textview) ndViewById(R.id.text);
tet.setTet("Mensagem recebida pelo HelloReceiver Dinnico!!!");
}

};
@Override
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ndViewById(R.id.btEnviar).set0nClickListener(this);
// Registra o receiver
registerReceiver(hellokeceiver,new IntentFilter("BINGO"));
}

@Override
public void onClick(View v) {
sendBroadcast(new Intent("BINGO"));
Toast.makeText(this,"Intent enviada!",Toast.LENGTH_SHORT).show();
}

@0verride
protected void onDestroy() {
super.onDestroy();
// Cancela o receiver
unregisterReceiver(hellokeceiver);
}

No mtodo onCreate(bundle) da activity o receiver est sendo registrado dinamica


mente com o mtodo registerReceiver(receiver,intentFilter). O receiver ficar vivo
durante todo o ciclo de vida da activity pois no mtodo onDestroy() ele cancelado
com o mtodo unregisterReceiver(helloReceiver). O restante do exemplo idntico ao
anterior; a diferena que, ao disparar a mensagem de broadcast, quem vai inter
ceptar o receiver que foi declarado como uma classe interna dentro da activity
No arquivo de layout, alm do boto para disparar a intent, foi adicionado um
Textview, utilizado para mostrar uma mensagem que o receiver vai interceptar.
Captulo 24 n Broadcastkeceiver 657
/res/layout/activity_main.xml
<LnearLayout xnlns:android="http://schenas.androd.com/apk/res/android"

s| p11
android:1ayout_wdth="match_parent" androidzlayout height="natch_parent"
android:padding="16dp" android:orentaton="vertca1">
<TextVew android:d="@+d/text"
android:tet="C1que no boto para disparar um receiver"
androd:layout_width="wrap_content" androd:1ayout_heght="wrap_content" />
<Button androd:d="@+d/btEnvar" android:tet="Enviar"
androd:1ayout_width="wrap_content" androd:1ayout_height="wrap_content" />
</LnearLayout>

A gura 24.1 mostra o resultado deste exemplo. Ao clicar no boto para disparar
a mensagem, ela interceptada pelo receiver dinmico e o Textvew atualizado.

EDmamicoli
I

r
amam l

il
1
Clique no boto para dispara um receiver | Mensagem recebida pelo HeiloRer;ewer i
* Euvuua

Figura 24.1 - Exemplo de receiver dinmico.

24 4 Quando utilizar um receiver esttico ou dinmico?


A ap1tulo,]a deve mas
~ ' " ber a resposta
Se voce leu com atenao O C
sa preciso
, garantir

'l7 ..1'
que voc entendeu o conC1I0
. , ` AndroidManiest.xml.
- f gurado no arquivo A sua principal
Um receiver estatico e COD
` ' terceptar os eventos sempre, mesmo se a
vantagem ' dessa
que forma ele vai in
aplicao estiver fechada. I _ _ .
d. . ico Congurado programaticamente no codigo da activity
Ja um recener m - 'd est
vi atrelado
a ao da activity Isso e geralmente uti
, - agens internas
Nesse caso, o seu ciclo de
' dentro da prpria aplicao.
lizado para enviar lTln5
563 Google Android - 4= edio
24.5 Classe LocalBroadcastManager
Mensagens de broadcast so enviadas para todas as aplicaes instaladas, mas s
vezes o que queremos somente enviar uma mensagem de forma local na aplicao.
Para isso, recomendada a classe android.support.v4.content.LocalBroadcastManager.

Utilizando a classe LocalBroadcastManager, internamente o Android consegue fazer


algumas otimizaes com relao ao desempenho da troca de mensagens. Outra
vantagem por questes de segurana, pois, se a mensagem for trafegada apenas
dentro da aplicao, no corremos risco de nenhuma outra aplicao intercept-la.
Utilizar a classe LocalBroadcastManager simples, basta obter uma instncia da classe:
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);

Feito isso, chamamos os mtodos registerReceiver(receiver,intentFilter) e


unregisterReceiver(helloReceiver) da mesma forma que antes:
// Registra o receiver de forma local
manager.registerReceiver(helloReceiver, new IntentFilter("BINGO")) ;

// Cancela o receiver
manager.unregisterReceiver(helloReceiver);

Por ltimo, usamos a classe LocalBroadcastManager para enviar a mensagem local


para a aplicao.
// Envia a mensagem somente para a aplicao
manager.sendBroadcast(new Intent("BINGO"));

Se quiser, conra o projeto de exemplo chamado HeI|oLocaIBroadcastManager.

24.6 Execuo de um receiver ao inicializar o sistema operacional


Ao longo de sua vida como programador Android, voc vai se deparar algumas
vezes com a necessidade de deixar algum servio executando sempre. Inclusive
preciso iniciar esse servio quando o celular for ligado.
No Android isso simples, pois, quando o sistema operacional termi
na a inicializao, enviada uma mensagem de broadcast com a ao
android.intent.action.BO0T_COMPLETED. Nesse caso, possvel interceptar a mensagem
e iniciar um BroadcastReceiver automaticamente logo depois que o sistema opera
cional termine a inicializao (boot).
A seguir, podemos vericar a configurao de um receiver que intercepta a ao
BO0T_COMPLETED.
Captulo 24 I BroadcastReceiver 669

<recever androd:name="
.SuaC1asseReceverAqu">

_ - - 9 y- "/>
<intent-1ter>

</intent-1ter>
<acton androd:name="android.ntent.acton.BO0T COMPLETED" />
<category android:nane="androd intent cate or BEFAULT

</recever>

Para utilizar a ao BO0T_COMPLETED, necessrio declarar a permisso RECEIVE_BO0T_COMPLETED

<uses-permission androd:nane="androd.permission.RECEIVE_BO0T_COMPLETED" />


Para tSaI` 6553 fL1I'lClOI'13l idade, instale a aplicao com este receiver configurado
e feche o emulador. Depois, inicie o emulador novamente e, quando o boot do
sistema terminar, o broadcast receiver vai receber a mensagem.
Esse tipo de recurso extremamente til quando precisamos que uma aplicao
seja executada sempre. Dessa forma, quando o usurio ligar o celular, podemos
executar algum cdigo. Inclusive, podemos manter uma aplicao executando
por um longo perodo de tempo com um Service (Captulo 27) ou agendar uma
aplicao para executar em uma determinada data e hora, utilizando o sistema
de alarmes (Captulo 26).
Enm, o que voc vai fazer com isso vai depender de cada caso. O importante
conhecer esse recurso.

24.7 Interceptando uma mensagem SMS


. ~ ^ ' ` roid dis ara
Se voc j entendeu o que um receiver faz, provavelmente deve estar tentando
adivinhar as diversas aoes que V0C Pode lnterclftaf* polsj O And P
e o cora
d'ive
fgag mensagens no formato de intents. Como ja foi explicado, a classe Intent
' o do Android e a classe BroadcastRecever intercepta justamente essas
intents. Que dupla, hein?
- uma
demos interceptar
mensagem veja
qll PO a
Apenas para demonstrar ma1S
seguinte congurao do AndroidManiest.xmlI
. - . _" sseRece1verAQU1 >
. ' II
<rece1ver andro1d.name- .SuaC1a

<ac ton an - '


<lntenl1ter:dr0d'name-"android.provider.TeIephony.SMS_RECEIVED" />
~ . z" droid.ntent.category.LAUNCHER" />
<category andro1d.nane
</ntent-1ter>
</recever>
67 Google Android - 4 edio
Nesse exemplo, o receiver est congu rado para interceptar as mensagens com ao
androd.provider.Te1ephony.SMS_RECEIVED, as quais so enviadas pelo Android sempre
que um novo SMS recebido. No captulo 33, sobre SMS, vamos aprender a ler e
enviar mensagens por SMS, ento vou concluir o exemplo depois. Por ora, mostrei
apenas para voc entender um caso clssico no qual um receiver utilizado para
interceptar um evento de sistema.

24.8 Ciclo de vida


Um BroadcastRecever vlido somente durante a chamada ao mtodo
onReceve(contet, intent). Depois disso, o sistema operacional encerrar seu pro
cesso para liberar memria. O cdigo do mtodo onReceve(contet,intent) deve
executar brevemente, e assim que o mtodo terminar o Android vai considerar
que o BroadcastRecever no est mais ativo e est pronto para ser destrudo.
Esse mtodo deve consumir rapidamente a intent (mensagem) recebida e retornar.
Caso demore mais de dez segundos para executar, 0 Android exibir um erro cha
mado ANR (Application Not Responding), que nada mais do que um timeout.
Voc provavelmente j deve ter visto esse erro em alguma aplicao, e se isso
aconteceu porque a aplicao fez algo errado. A gura 24.2 mostra o erro ANR,
quando um BroadcastReceiver demorou mais de dez segundos para executar.

Figura 24.2 - Aplicao no respondendo (ANR).

Nota: um broadcast receiver deve executar em no mximo dez segundos. Caso


contrrio, o Android vai mostrar o err ANR. Outra lorma de o erro ANR
acontecer e quando algum evento gerado dentro de uma activity ou fragment
no e tratado em no mximo cinco segundos. Justamente por isso, importante
utilizar threads para tratar os eventos de interfaces dentro da activity.
CaPtuIo 24 n Broadcastkeceiver 571
24.9 Delegando o trabalho para um service
Um Broadca tR "
I s ecetver nao deve executar tarefas demoradas durante a chamada ao
metodo onReceve(contet
m ntent).
re-seLede
b
que todo o cdigo que intercepta
o evento deve executarnoem ' ' dez segundos.
maximo
Par? exemplllfs digamos que sua aplicao recebeu uma mensagem de Push
indicando que existem atualiz aes no seu servidor. No importa que tipo de
atualizao seja essamas imque seja necessrio consultar um web service.
agine
Dependendo da velocidade da conexo e da quantidade de dados a serem trafe
gados, isso pode demorar um pouco e exceder os dez segundos.
No caso da activ itjg aprendemos que, para no travar a interface do usurio de 7

vemos utilizar threads. Porm, no caso do receiver, no podemos utilizar threads,


pois o sistema operacional vai encerrar o processo do receiver depois de dez
segundos, e ao fazer isso ele pode destruir inclusive threads que foram criadas,
pois o Android no as conhece.
Para solucionar esse problema, foi criada a classe Service, que faz parte do ciclo de
vida do Android e especializada em executar servios em segundo plano por um
longo perodo de tempo. O Android encerrar esse servio apenas se a memria
estiver extremamente baixa e se for necessrio encerrar determinados processos
para liberar memria. Mesmo se isso acontecer, posteriormente o Android pode
inclusive reiniciar o servio para continuar o que ele estava fazendo. Para mais
detalhes, leia o captulo 27, sobre a classe Service.

. , - stRecever
24.10 Mostrando uma noticao para o usurio
Para nalizar este capitulo, gostaria apenas de' lembrar QU Lim Bmadca

ou
me .
no deve interagir com o usurio de forma direta, seja exibindo um alerta ou
abrindo uma tela sem a sua perm1SS20
V ser mais elaro- um receiver no deve atrapalhar o usurio! Nunca chame uma

` e e bem rovV C1 . , .
activity de dentro de um receiver. Lembre-se de que o usurio pode estar fazendo algo
1 ue ele no deseje ser incomodado. No existe nada mais
mconveni
importlnilte do qui aplicaes malfeitas que ficam atrapalhando a vida do usuario.
A manei ndada de interagir com o usurio por meio de uma noti
ra reco ca na barra de status do Android, o qual chama a
_ z . render o conceito.`Quan
(zao, que um alerta qL1 do voc recebe um email
. l uma tela
sua atenao. E simples de 1'1 indesejada
- ~ 1 por acaso ap8fC 3 8
na tela do seu
pela aplicaao do Gmal ,
672 Google Android - 4= edio
d'~^
celular? A resposta no, pois o Gmail mostra uma agradvel notificao na barra
e status, e essa noticaao voce pode ver quando puder ou quiser.
Aproveitando a conversa, notificaes o assunto do prximo captulo.

24.11 Links teis

Neste captulo, estudamos como interceptar mensagens de broadcast, que na pr


tica so intents enviadas pela sua aplicao ou pelo prprio sistema operacional.
Seguem alguns links para continuar seus estudos.
API Reference - BroadcastReceiver

http://developer android. com/reference/android/content/BroadcastReceivex html


Android Training - Reporting Work Status

https://developenandroid.com/training/run-bac/eground-service/report-status.html
` A' CAPTULO 25
Notication
\
l

Uma noticao uma mensagem especial que aparece na barra de status do


Android para chamar a ateno do usurio.
Ao receber a noticao, o usurio pode decidir visualizar seu contedo ou
simplesmente ignorar a mensagem. Quando o usurio clicar na noticao, ser
disparada uma Intent para iniciar uma activity broadcast receiver ou service. O
contedo dessa intent, ou seja, a sua ao e parmetros, voc quem define. No
vamente, as intents so o segredo de tudo.

25.1 Por que usar uma noticao para se comunicar com o usurio
Uma aplicao executando em segundo plano nunca deve exibir um alerta para
o usurio, ou, pior ainda, abrir uma tela sem a sua permisso. Lembre-se de que
o usurio pode estar lendo emails importantes, estar em uma chamada com um
amigo, jogando ou executando qualquer outra aplicaao.
Um exemplo de utilizao de notificaes seria uma aplicao de chat, na qual o
usurio pode receber uma notificao quando chegar uma nova mensagem. Ao
Selecionar a notao, o usurio pode decidir visualizar a mensagem ou nao.

Quzrg exemplo
atualizao de seria um servioNo
informaes. quemOIT1f0
executa em segundqplano,
em que 3 3fU3 Ifa-zeqralagiia
123930 efm '
~ ~ ' suario
caao poderiautilizando uma o not1 u tipos
._25.1
'informar
f ~ ' ' ca
~ l do.Android
uma documentaao OC13
' de ovarios
A gura
noticaoes.
que exibe
_ _mostra
__
. d Android 41 35 noticaes podem ser criadas com mais contedo,
A pamr O l , Outra melhoria do Android 4.1 foi o suporte s
chamadas de Big View Notications
aoes -diretamente
~ t1caOS nas nO
- ~que Saocustomizados que aparecem
botes
na notificao.

673
\

574 Google Android - 4 ediao


The Big Meeting
4 i 5 35 PM
Ba (`|`-fl''f|'(* Room

Q um . amar eus.
New Goog|e+ notifications 5 t4 W
i 'lL.:,"d Added .~ track J

II
Screenshot captured. JI' >M
jul/ him v PRN v: ^"I" ". '
Keep photos 8. videos backed . 4 . . .fm
iy: zt ' PP j2'|-,.'1r- *.l'jP Hr (~';f;-;!l~

3 new messages 4 1 w

' " O
lzurvratar:~a\n:vJn.am;ffi;;Tia 0 z

Q 3(52)263
new0?<'
messages
fiat
z 4; wi

Figura 25.1 - Exemplo de noticacs.

25.2 Criando uma noticao simples


A classe Notcaton.Bulder contm vrios mtodos utilitrios para congurar um
objeto do tipo Notcaton e segue o famoso padro de design Builder do (lol. (Gang
of Four). Caso voc no conhea os padres do Gof, recomendo fortemente que
faa uma leitura complementar:
http://pt.wikipedia.org/wilzi/Builder
http://pt. wikipedia.org/wilzi/Dcsign_Patterns
Como nem todas as funcionalidades das noti licaoes existem em todas as versoes
do Android, foi criada a classe de compatihilidade NotcatonCornpat.Bulder. que tem
como objetivo esconder essa complexidade do desenvolvedor, pis utilizando .i
classe de compatibilidade as novas funcionalidades sero habilitadas se possvel.
ou sero ignoradas, tudo de forma transparente.
Para entendermos melhor com criar noti cages, vamos criar um projeto chama
do HeIIoNoti(ation. Se preferir, ahra o projeto de exemplo deste captulo no Android
Studio. Caso tenha criado o projeto, siga as prximas instrues, pois vamos cons
truir varias notificaces passo a passo. No arquivo de layout da activity adicione
quatro botes, um para cada tip de notificao que vamos criar.
Captulo 25 n Notication
675

/res/layout/activity_main.m|

<LinearLayout mlns:android="http://schemas.android.com/apk/res/androdu
android:layout_width="match_parent" android:layout_height="matCh parent"
android:padding="16dp" android:orientation="vertical"> _
<TetView

android:text="Eemplo de noticaes"
android:layout_width="wrap_content" android:l
ayout _height="wrap_content" />
<Button

android:layout_width="match_parent" android:layout_height="wrap_content"
android:
<Button tet="Noticao Simples" android:onClick="onClickNoticacaoSimples" />

android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao Heads-up" android:onClick="onClickNoticacaoHeadsUp" />
<Button

android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao Big" android:onClick="onClickNoticacaoBig" />
<Button

android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Noticao com Ao" android:onClick="onClickNoticacaoComAcao" />

Repare que cada boto declara a tag android:onClick, portanto ao clicar neste boto
o mtodo congurado ser executado na activity Voc pode adicionar os mtodos
manualmente no cdigo da activity ou utilizar o assistente do Android Studio
para fazer isso automaticamente. A primeira forma de abrir o assistente clicar
na lmpada amarela no cdigo-fonte do arquivo de layout. Essa lmpada um
indicador de que o assistente pode ajud-lo com algo. Para e>c1bi-la,.que com o
cursor na linha em que voc escreveu o cdigo com a tag androidzontlick. A segun
. - - "' ara criar o mtodo.
da forma utilizar a tecla de atalho AIt+Enter tambm nessa linha. Uma vez que o
assistente for exibido, basta escolher a oPa0 P
, f ~ ' ` car assim:
. . ' '
Depois de criar os metodos, O C0d18 da acuvlty deve

MainActivity.java
public class MainActivity extends ADDCPatACt1V1 y {
@0verride _ _
. ' ' l.
public void onCreate(Bundle icicle) {

} - ' 'w){}
super.onCreate(icicle)5 _n)
setContentView(R.laY0Ut-actlvltyrma

.. ' 'ew){}
public void onClickNoticacaoSimples(VIEW Vle
public void onClickNoticacaoHeadsUp(View V1
676 Google Android - 4 edio
public void onClickNoticacaoBig(View view) { }
public void onClickNoticacaoComAcao(View view) { }
}

A primeira noticao que vamos fazer a mais simples e pode ser utilizada em
tod as as verses do Android, pois mostra apenas um cone, ttulo e mensagem.
Para encapsular a criao das noticaes, vamos criar a classe Noticationutil,
conforme o cdigo demonstrado a seguir:

NoticationUti|.java
public class Noticationtil {
// Cria a Pendinglntent para abrir a activity da intent
private static Pendinglntent getPendingIntent(Contet context, Intent intent, int id) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Esta linha mantm a activity pai na pilha de activities
stackBuilder.addParentStack(intent.getComponent());
// Congura a intent que vai abrir ao clicar na noticao
stackBuilder.addNetIntent(intent);
// Cria a Pendinglntent e atualiza caso exista uma com o mesmo id
Pendinglntent p = stackBuilder.getPendingIntent(id, Pendinglntent.FLAG_UPDATE_CURRENT);
return p;
}

public static void create(Context context, Intent intent, String contentTitle,


String contentTet, int id) {
// Cria a Pendinglntent (contm a intent original)
Pendinglntent p = getPendingIntent(Contet, intent, id);
// Cria a noticao
NoticationCompat.Builder b = new NoticationCompat.Builder(context);
b.setDefaults(Notication.DEFAULT_ALL); // Ativa conguraes padro
b.setSmallIcon(R.drawable.ic_notication_icon); // cone
b.setContentTitle(contentTitle); // Titulo
b.setContentText(contentText); // Mensagem
b.setContentIntent(p); // Intent que ser chamada ao clicar na noticao.
b.setAutoCancel(true); // Autocancela a noticao ao clicar nela
NoticationManagerCompat nm = NoticationManagerCompat.from(contet);
// Neste caso a noticao ser cancelada automaticamente quando o usurio clicar
// Mas o id serve para cancel-la manualmente se necessrio.
nm.notify(id, b.build());
}

public static void cancell(Contet context, int id) {


NoticationManagerCompat nm = NoticationManagerCompat.from(contet);
nm.cancel(id);
Captulo 25 n Notication 677
}

nm . cancelAll( ); '
public static void cancellAll(Context context) {
Nt1cauMaa9e"C0WDH DFI = NotificationManagerConpat.fron(contet)'

l
}

.'~0u|..,
Nota: o mtodo setefaults (Notification.DEFAULT ALL) da classe NoticationCompat.Builder

copgurar a noticao
as uzes. ecnicamente comaum
falando, som padro,
constante faz vibrar oaplica
DEFAULT_ALL celular e ainda
todas acende
as constantes
DEFAULT_SOUND, DEFAULT_VIBRATE e DEFAULT_LIGHTS. Como nesse caso o celular vai vibrar
e preciso declarar a permissao android.permss1.on.VIBRATE no arquivo de manifesto.

O cdigo-fonte dessa classe est comentado, portanto leia-o com ateno. Basica
mente estamos utilizando a classe Notificationtonpat.Builder para criar a noticao
com os parmetros informados. Note que alguns parmetros esto fixos, como o
cone da notificao, que deixei com o mesmo cone do aplicativo. O importante
criar a intent e a Pendinglntent que ser disparada ao clicar na noticao. A
Pendinglntent um tipo especial de intent que encasula uma intent real, a fim de
ser disparada ao clicar na notificao.
Para concluir este primeiro exemplo, altere o mtodo onClickNotifcacaoSinples(view)
para chamar o mtodo NotifcationUtil.create( . . .) e criar a noticao simples.

MainActivity.java

} _ __..
public class MainActivity extends AppCompatActivity f

int id = 1; H
public void onClickNotifcacaoSinples(View vi-GW) {

String contentTitle = "Nova mensagem , H


String contentTet = "VOC DOSSU 3 Va5 mensagens ;
u || ' ' '?" ;
Intent intent = new Intent(this , MensagenActivity.class);

o 1 rca i z '
ntent.pUtExtra(..mSg , 013, Leitor, como vai )
N ff' fonutil create(this intent,contentTitle,contentText,id);
}

' d vamos deixar vazios por enquanto, pois construiremos os


O5 Outros meto Os Vea que na intent que foi criada para ser chamada ao cli
'fi a tPaslso.
exemplos passo 1zamO J S a Classe Men5agenActivty que ainda nao existe. Inclusive
Cf 1121passan
UOU Caagu ra
1 ,metro "msg" dg tipo String para essa intent. Portanto,
estamos O um Pa
573 Google Android - 4 edio
utilize o wizard New>Attivity do Android Studio para criar a activity MensagemActivity
e configure a activity pai (parent activity) como a MainActivity. O cdigo-fonte da
classe MensagemActivity pode ser visualizado a seguir. No cdigo estamos lendo o
parmetro "msg" que foi enviado pela intent para mostrar a mensagem no Textview.

MensagemActivity.java
public class MensagemActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_mensagem);
getSupportActionBar()_setDisp1ayHomeAsUpEnab1ed(true);
String msg = getIntent().getStringEtra("msg");
Textview text = (Textview) ndViewById(R.id.tet);
tet.setTet(msg);
}

No arquivo de layout, foi criado o Textview com o identificador @+id/text.

/res/Iayout/activity_mensagem.xml
<LinearLayout . . .>
<TetView android:id="@+id/text"
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content" />

Para este exemplo funcionar corretamente, importante que seja feita a con
gurao da activity MensagemActivity e de sua activity pai no arquivo de manifesto.
Veja que configurei as tags android:parentActivityName e <meta-data indicando que a
activity pai na hierarquia a MainActivity.

AndroidManifest.xmI
<appiication . . .>
<uses-permission android:name="android.pernission.VIBRATE"/>
<activity android:name=".MainActivity" ... />
<activity android:name=".MensagemActivity"
android:parentActivityName=".MainActivity" >
<meta-data android:nane="android.support.PARENT_ACTIVITY"
android:va1ue=".MainActivity" /
Captulo 25 n Notication 679
Nota: lemb
re- ' ' ~ ~ . .
I I _ Sede adicionar a permissao VIBRATE no arquivo de manifesto. Isso
e necessario, pois a notifica o cria
f ` `adcom o mtodo setDefau1ts(Notification.
O1
DEFAULT_ALL) .

Depois dessa alterao, execute o projeto novamente e clique no primeiro boto.

1li zlII
O resultado a notificao exibida na figura 25.2. Ao clicar na noticao, a intent
que foi configurada com a activity MensagemActivity disparada, consequentemente
esta tela mostrada ao usurio. A mensagem Ol, Leitor, comovai? foi lida dos parmetros
da intent, apenas para demonstrar que podemos enviar os parmetros.
O interessante das noticaes que elas podem executar mesmo com o aplicativo
fechado. Portanto, depois de disparar a noticao, feche o aplicativo. Ao clicar
na notificao, voc ver que a activity MensagemActivity ser chamada. Agora faa
o teste, e clique no boto voltar. O resultado que a MainActivity foi exibida, cer
to? Mas se a aplicao estava fechada, e chamamos a MensagemActivity pela intent,
como fizemos para colocar a MainActivity na base da pilha? Isso foi feito no mtodo
getPendingIntent() da classe NotificationUti1, com a ajuda da classe TaskStackBui1der.
A linha de cdigo a seguir adiciona a activity pai na pilha. Se voc comentar esta
linha, somente a classe MensagemActivity ser adicionada pilha.
// Esta linha mantm a activity pai na pilha de activities
stackBui1der . addParentStack(intent . getComponent( ) );

Essa uma deciso de design importante, pois na maioria das vezes, ao abrir uma
activity tambm precisamos carregar a sua activity pai.

' Exemplo
Ol Leiton como vai? 1
de noticaes I

l |:> NOTIFICAO SIMPLES l


NOTIFICAO me i
No'rn=1cAo HEA|sfuP

NOTWFICAO com A

A
u

Figura 25.2 - Notificao simples.


j
53 Google Android - 4 edio
Dica: o icone de notificao do projeto de exemplo deste capitulo foi criado com o
wizard New Image Asset do Android Studio. No wiza rd, selecione o tipo Notification e
clique em Clipart para escolher algum modelo de cne. O Android Studio vai criar 0
mdelo de cone adequado para cada verso da plataforma d Android. No Android
5.0, devido ao Material Design, os cones de notificaes devem ser brancos com
fundo transparente, pois o sistema vai desenha-los com um fundo circular.

25.3 Heads-up notitations


No Android 5.0, foram criadas as notificaes conhecidas como heads-up notitations,
que aparecem no topo da tela por cima de tudo. Um exemplo a notificao
que aparece quando voc recebe uma ligao telefnica, a qual o usurio pode
escolher atender ou no.
Antigamente, o prprio aplicativo nativo da ligao mostrava uma tela grande
para o usurio escolher se ele deseja atender ou no a ligao (Figura 253). Com
as heads-up notitations, a notificao ficou mais elegante e sutil. e o usurio pode
continuar vendo a tela em que estava.

l 99998877 ,riL\~!1i.1LJ z*

"( :ziimisz Yi ..t.~,.u+*

liigura 25.3 ~ Iletzds-up mtiji'ation t1('l1(i('lI(i() 1igci.

Dica: uma heads-up notification pode ser removida fazendo o gesto de swipe lateral.
ou seja, segure-a e arrestc para a direita, jogando a notificao para tora da tela.
(aPtu|o 25 I Notication
681
Para criar uma heads-u noti '
10" "\Dt.Builder. P - f
boolean) da Classe Nolidcafcilnbasta chamaro mtodo setFullScreenIntent(Pendinglntent,
C|_eateHeadSUpNocat_ i _ ara testar o conceito, crie o metodo
10( - -) na classe Notif|cationUtil.

NoticationUtiI.java
public class Noticationtil {

public static void createHeadsUpNotication (. . .) {


Pedgltet D = . . . // Tudo igual antes...
NoticationCompat.Builder b = . . .;
// Cor da noticao Android 5.0
b.setColor(Color.BLUE);
// Heads-up notication
b.setFullScreenIntent(p,false);

nm.notify(id, b.build());
}

Alm do mtodo setFullScreenIntent(Pendinglntent,boolean), voc deve ter reparado


que tambm chamei o mtodo setColor(int), que recebe o RGB de uma cor. No
Material Design, o cone da noticao deve ser branco com fundo transparente,
pois ser desenhado de forma circular pelo sistema. O mtodo setColor(int) justa
mente pinta o fundo transparente com a cor informada. Mais uma vez, atente-se
para a importncia de utilizar a classe de compatibilidade NotificationConpat, pois
ela ativa esses recursos nas novas verses do Android, ou simplesmente os oculta
nas verses antigas.
Para testar a heads-up notication, atualize o cdigo da classe MainActivity.

MainAttvity.java
public class MainActivity extends ADDC0'lD3tACtlVty f

public void onClickNoticacaoHeadsUp(View View) {


// Crie a intent como no anterior ...
o iica
N t'f tionUtil createHeadsUpNotication(this,intent,contentTitle,contentText,id);
}

A gura 254 mostra o resultado da heads-up noti<aIi0.


682
Google Android - 4 edio

3
` 'ss.4.Ancl em
Nova mensagem simples '
'fl' \- L ' Ml ;'iW.; mk \ l if Jfi'

Exmpio de

Q Nmiricwmrmosw
Nurmcmlo BIG

NornF|cA;o cowsclo

Figura 254 - Heads-up notification.

25.4 Noticaes na tela de bloqueio


No Android 5.0, as noticaes aparecem na tela de bloqueio por padro, con
forme a figura 25.5. Para testar esse comportamento, implemente o mtodo
onClickNoticacaol-lithDelay(). Esse mtodo cria a noticao como antes, porm com
um atraso de dois segundos, tempo suciente para voc colocar a tela do celular
no modo bloqueio.

MainActivity.java
public class MainActivity extends AppCompatActivity {

public void onClickNoticacaoHithDelay(View view) {


view.postDelayed(new Runnable() {
@Override
public void run() {
. . . // Aqui igual ao cdigo anterior...
Noticationtil.createHeadsUpNotication(getBaseContet(),
intent,contentTitle,contentTet,id);
Toast.makeText(getBaseContext(),"Coloque o cell na lock-screen",
rost.LENoTH_sHoRT).show();
}

},2000); // Delay de dois segundos


}

}
Captulo 25 I Notication 683
Para, Cutomiaar o modo de visibilidade da noticao na tela de bloqueio, utilize
O melo 0 SVSU/(int) da classe NottficattonConpat.But1der, informando uma das
seguintes constantes:

' NtC3"C0'\Dat .VISIBILITY_PUBLIC - Mostra a noticao completa na tela de


bloqueio.

NoticatonConpat.VISIBILITY_PRIVATE - No mostra o contedo da noticao


na tela de bloqueio.
NottficatonConpat.VISIBILITY_SECRET - No mostra a noticao completa na
tela de bloqueio.
O lado direito da figura 25.5 mostra uma noticao criada com a visibilidade
VISIBILITY_PRIVATE, portanto o contedo cou escondido.

Figura 25.5 - Noticao na tela de bloqueio.

e.
' en
fe-e-'*'"`"~'Ww'JwV S Conguraes do 5i5[ema na op0 Sound 8*
Ateno: no Android 5.0,wntree is locked para voc configurar como deseja
Configuration > Notficatton > blo ueio se comportam. se O usurio tiver uma

. _' _'. _IO.


. neste
. _C
que as noticaoes na tela de . rcisa digitar um PIN) e selecionar a OPQO Hlfle
tela de bl0C1U@0 Pfotegldo (axu pa so sim as constantes de viibldade Sero
SGHSUV' f1f'Ca coient,S seu celular estiver em Portugues, Procure Pelas
3PhCadaS e Sutura? -6 - ' ostrar nottcaoes.
opes de 50m e Not1fICa > Nao .,__-..---z---~---- ~~e ""`""
534 Google Android - 4 edio \

25.5 Criando uma noticao grande (big view notications)


A partir do Android 4.1, foi criado o conceito de big view notications, que so
noticaes que podem ocupar um espao maior na tela, conter um texto com
vrias linhas, ou at aes customizadas, como os botes de atender a ligao que
vimos no exemplo anterior.
Para demonstrar como criar uma noticao expandida com vrias linhas, crie
o mtodo createBig(...) na classe Noticationtil.

) NoticationUtiI.java
public class NoticationUtil {

public static void createBig(Contet context, Intent intent, String contentTitle,


String contentTet,ListString> lines, int id) {
Pendinglntent p = getPendingIntent(contet, intent, id);
// Congura o estilo Inbox
int size = lines.size();
NoticationCompat.InboxStyle inboStyle = new Noticationtompat.InboxStyle();
inboStyle.setBigContentTitle(contentTitle);
for (String s: lines) {
inboStyle.addLine(s);
}
inboStyle.setSummaryText(contentTet);
// Cria a noticao
NoticationCompat.Builder b = new Noticationtompat.Builder(contet);
b.setDefaults(Notication.DEFAULT_ALL); // Ativa conguraes padro
b.setSmallIcon(R.drawable.ic_notication_icon); // cone
b.setContentTitle(contentTitle); // Titulo
b.setContentTet(contentText); // Mensagem
b.setContentIntent(p); // Intent que ser chamada ao clicar na noticao.
b.setAutoCancel(true); // Autocancela a noticao ao clicar nela
b.setNumber(size); // Nmero para aparecer na noticao
b.setStyle(inboStyle); // Estilo customizado
NoticationManagerCompat nm = NoticationManagerCompat.from(contet);
nm.notify(id, b.build());
}

No cdigo foi utilizada a classe InboStyle para congu rar o estilo da noticao C
adicionar vrias linhas na mensagem. Agora, atualize o cdigo da classe HainActivity
para criar a noticao grande.
Captulo 25 n Notication
685

MainActivity.java

public class MainActivity extends AppConpatActivity {

public void onClickNoticacaoBig(View view) {


int id = 2;
String contentTitle = "Nova nensagen";
String contentTet = "Voc possui 3 novas nensagens";
Intent intent = new Intent(this,MensagenActivity.class);
intent.putEtra("msg","Ol, Leitor, como vai?");
List lines = new ArrayList<>();
lines.add("Mensagem 1");
lines.add("Mensagem 2");
lines.add("Mensagen 3");
NoticationUtil.createBig(this, intent, contentTitle, contentTet, lines, id);
}

Neste exemplo, criamos uma lista com trs mensagens, e ao clicar no segundo
boto o resultado ser como a gura 25.6. Repare que no canto direito inferior da
noticao apareceu o nmero 3, pois no cdigo utilizei o mtodo setNunber(int)
a.classe Noticationtompat.Builder.

l| _l
Exemplo de noticaes

NoTu=|cAo s|M||_ss

i No1'|F|cAo HEADS-UP

!:> NOTIFICAO sw;


' Nurrmco coMAO
.

F'zguf -'
a 256 Exemplo com noticao expandida
636 Google Android - 4 edio
25.6 Criando uma noticao com aes
Para criar uma noti cao com ao, basta chamar o mtodo builder.addAction(icone,
string, intent) congurando o cone da ao, o texto e a intent que ser disparada
ao clicar na ao.
Para o prximo exemplo, crie o mtodo createwithAction( . . .) na classe NoticationUtil.
Repare que resumi o cdigo, pois a forma de criar a noticao igual ao exem
plo da noticao simples, com exceo de que estou chamando o mtodo
builder.addAction(icone, string, intent).

NoticationUtiI.java
public class Noticationtil {
public Static nal String ACTION_VISUALIZAR = "ACTION_VISUALIZAR";

public static void createNithAction(Context context, Intent intent, String contentTitle,


String contentText, int id) {
// Crie a NoticationCompat.Builder normalmemte aqui...
// Ao customizada (deixei a mesma intent para as duas aes)
Pendinglntent actionlntent = Pendinglntent.getBroadcast(
context, 0, new Intent(ACTION_VISUALIZAR), 0);
b.addAction(R.drawable.ic_acao_pause, "Pause", actionlntent);
b.addAction(R.drawable.ic_acao_play, "Play", actionlntent);
NoticationManagerCompat nm = NotificationllanagerCompat.from(context);
nm.notify(id, b.build());
}

Para o cdigo compilar, adicione os cones pause e play na pasta /rcs/drawablc


Uma dica interessante criar os cones com o wizard New Image Asset. No wizard.
selecione o tipo Notication e clique em Clipart para escolher algum modelo de cone.
Foi assim que criei os cones de play e pause deste exemplo. Para Finalizar, atualize
o cdigo da MainActivity para criar a notificao com as aes. Como uma intent
ser disparada ao clicar na ao, precisamos registrar um broadcast receiver para
receber o evento. Note que estou disparando a mesma intent para ambas as aes
para simplicar o cdigo, mas na prtica teramos intents diferentes.

-3 ManActivity.java

public class MainActivity extends AppCompatActivity {

private BroadcastReceiver customActionReceiver = new BroadcastReceiver() {


@0verride
Captulo 25 I Notication

public void onReceive(Cont@t


context, Intent intent) {
Toast.makeText(context, "CLICOU N ^ ,H
NoticationUtii.cance11(context, :)?A0. , ToaSt.LENGTH_SHoRT)'Sh0w();
}

};
@0verride

protected void onResume() {


super.onResume();

} registerReceiver(custonActionReceiver, new IntentFi1ter(NoticationUti1.ACTION_VISUALIZAR))


@0verride

protected void onPause() {


super.onPause();
unregisterReceiver(custonActionReceiver);

public void onC1ickNoticacaoComAcao (View view) {


int id = 3;
Intent intent = new Intent(this,MensagemActivity.c1ass);
intent.putEtra("msg","Msica iega1.");
String contentTit1e = "None da msica";
String contentText = "Nome do artista";
Noticationtil.createwithAction(this, intent, contentTit1e, contentText, id)
}

A figura 25.7 mostra o resultado ao criar uma noticao com ao.

) Exemplo de notcaes Exemplo de notica595

|
..
NoT|F|cAco suv||1.Es

NOTIFICAO HEDS-UP

, NOTIFICAO BIG

E> NOTIFICAO OOMO


NOTIFICAO s|MP|.ss

NOTIFICAO HEADSHUP

NoTu=|cAo BIG

NOTIFICAO oomAO

F'zgur
zz 257-ExemP0
' d "0' mm o
533 Google Android - 4 edio
Ao clicar nos botes, uma intent disparada. A terceira parte da gura mostra o
alerta informando que o broadcast receiver recebeu a intent. Lembre-se de que
usei a mesma intent e receiver para ambas as aes para simplicar o cdigo, mas
na prtica voc teria uma intent e receiver para a ao pause e outra para o play.

25.7 Cancelando uma noticao


Para cancelar uma notificao, ou seja, remov-la da barra de status, podemos
utilizar os seguintes mtodos da classe NotficatonManager:
cance1(nt id) - Cancela a notificao utilizando o id fornecido.
cance1A11() - Cancela todas as noticaes do aplicativo.
Outra forma chamar o mtodo setAutoCance1(boo1ean) da classe NotcatonCompat.Buder
no momento de criar a notificao, assim ela ser automaticamente cancelada
quando o usurio abri-la.

25.8 Mais informaes sobre a classe Pendinglntent


Na classe NotcatonUt1 criamos um objeto do tipo Pendinglntent com a classe
TaskStackBu1der, a qual consegue ter um maior controle para criar a pilha de activities,
sendo que naquele exemplo a pilha era formada por: ManActvty > MensagemActvty.
Mas a forma clssica de criar uma Pendnglntent para chamar uma activity assim:
Pendinglntent p = Pendnglntent.getActvity(ths, 0, new Intent(ths, NomeDaActivty.c1ass),
Pendnglntent.FLAG_UPDATE_CURRENT);

E para criar uma Pendnglntent que vai disparar um broadcast para um receiver assim:
Pendnglntent p = Pendnglntent.getBroadcast(ths, 0, new Intent("ABRIR_APLICACAO_TESTE"),
Pendnglntent.FLAG_UPDATE_CURRENT);

Isso significa que, ao criar a Pendnglntent, preciso especicar exatamente se ela


deve chamar uma activity ou disparar uma mensagem de broadcast. importante
voc guardar bem na memria essa informao, pois ela ser til mais tarde.

Dica: recomendo sempre criar a Pendnglntent com a flag FLAG_UPDATE_CURRENT, para


que o Android sempre entregue uma intent atualizada ao receber a noticao.
Caso contrrio, voc pode receber mensagens desatualizadas. Para mais detalhes,
verifique a documentao ocial.
Captulo 25 n Notiation

e .
_ ast e 1 . .
259 Exemplo mm "t'(a0 E BroadtastReteiver
No captulo anterior, criamos um ro.
a

.__g
como envlar uma intent por broadcp 1 to chamado HelloReceiver que mostrou
broadcast recelver. nterceptar a mensagem utilizando um
. ~ , os a r1morar
vam ' es
Para exercitar o conceito de n oticaes
. 61' rcce er a mensa em Para '
uma noucaao quando O receiv b P se exemplo e mostrar
projeto HeIIoReceiver do captulo ante ` g . Commuar, Cople O
rior e renomeie para HeI|oReceiverNotication.

Andro dM . , s
Este projeto j declara um receiver esttico que est confi urado no
. 1 amespt xml para interceptar as mensagens com aao BINGO. Como queremos
criar uma noticaao, adicione a seguinte classe ao projeto'

NoticationUtiI.java
public class Noticationtil {
public static void notify(Context context, int id, Intent intent, String contentTitle,
String contentText) {
NoticationManager manager =
(NoticationManager) context.getSystenService(Contet.NOTIFICATION_SERVICE);
// Intent para disparar o broadcast
Pendinglntent p = Pendinglntent.getActivity(context, 0, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
// Cria a notication
NoticationCompat.Builder builder = new NoticationCompat.Builder(contet)
.setContentIntent(p)
.setContentTitle(contentTitle)
.setContentTet(contentText) .
setSmallIcon(R.drawable.ic_notication_icon)
.setAutoCancel(true);

}
l
.~e
// Dispara a notication
Notfkaton n z builder.build();
nanager.notify(id, H);

. Cdigo

receiver recebe um parmetr


ro da classe HelloReceiver para mostrar uma
Na sequenc1a,vamos atualiza nsagem de broadcast. Observe que a intent do
noticaao ao receber uma fflmsg
o do tipo string 6 est repassando esse parametro
d o 8clicar na notificao. A intent da noticao
d' ara 21
para a intent que ser ISP
vai abrir a MainActivity.
59 Google Android - 4 edio
ij Hel|oRecever.java

public class HelloReceiver extends BroadcastReceiver {


@Override
public void onReceive(Context context, Intent intent) {
Log.d("livroandroid", "HelloReceiver !!!");
// Recebeu a mensagem de broadcast.
Intent notiflntent = new Intent(contet,MainActivity.class);
String msg = intent.getStringExtra("msg");
notifIntent.putExtra("msg", msg);
// Cria a noticao
Noticationtil.notify(contet,1,notifIntent,"Texto digitado",msg);
}

No arquivo de layout da activity adicione um campo de texto para o usurio


chgnarainensageni
// arquivo /res/layout/activity_main.xml
<EditTet android:id="@+id/text"
android:layout_width="match_parent" android:layout_height="wrap_content" />

Por ltimo, no cdigo da activity vamos disparar a mensagem de broadcast com a


ao BINGO. A diferena para o exemplo do captulo anterior que estamos lendo o
texto digitado no EditText e enviado por parmetro com o chave "msg' Lembre-se de
que o receiver est mapeado no AndroidManiest.xm1 para interceptar a ao BINGO.

MainActivity.java
public class HainActivity extends AppCompatActivity implements View.0nClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ndViewById(R.id.btEnviar).set0nClickListener(this);
readHsg(getIntent());
1

@0verride
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
readHsg(intent);
}

private void readHsg(Intent intent) {


String msg = intent.getStringExtra("msg");
if(msg != null) {

P L`
...fxx
1. " `*.` bai
Captulo 25 u Notication 691
Toast.nakeTet(this "Voc di it
} else { ' 9 = " + P1S9,Tost.LENoTH_sHoRT).shw();
Toast.m k T " ... .
} a e et(this, Extras. + intent.getEtras(),Toast.LENGTH_SHORT).show();
}
@0verride
public void onC1ick(View v) {
EditTet text = (EditText) findViewById(R.id.tet);
String msg = tet.getTet().toString();

. IlTVT1Ti1
Intent intent = new Intent(new Intent("BINGO"));
intent.putExtra("nsg",nsg);
sendBroadcast(intent);
Toast . nakeTet(this , " Intent enviada! " ,Toast . LENGTH_SHORT) . show( );
}

A gura 25.8 mostra o resultado desse exemplo. O uxo simples: o receiver vai
receber a mensagem e mostrar a noticao. Quando o usurio clicar na notica
o, a MainActivity ser chamada e neste momento estou lendo o parmetro "msg
que chegou da intent.

l Ca! W 7 z V '
;1LDigite
0_ um
Digite
l ENVIAR
umtexto
R_ texto e dispara
doe dispara
Qi Ricardoa intent
a intent 4 _ _12
ENVIAR

Voc digitou: Oi Ricardo

on ' ~
.

. - I- ' ci .
1
I

Figura 25 8 - Broadcast receiver com notica0.


alidade interessante pois veja que no cdigo
Este exemplo tem uma fu. nado
o O mwdo onNewIntent(intent). Por padrao,
da Classe MmACtmty fm ahl da uma nova activity criada. Mas neste caso
sempre que uma intent e c entO pois precisamos garantir que sempre exis
eu no quero esse Comportlm , 'lha de activities. Para isso, congure a tag
' A tivity viva na P1 _
ta apenas uma Main C H AndroidMamfest.xml.
android:iaunchMode="S9leTp HO
Google Android -4' edio
/ Artdroidanifestnl
activity android:nane=".HainActivtty" androtd:launchHode="singleTop" />

lsso mantm apenas urna HainActtvity na pilha. Caso a activity no exista, ela
ser criada; caso contrario, a sua instncia ser preservada em memria e o m~
todo onNewIntent(intent) sera chamado. Exatamente por isso o codigo faz a leitura
do parmetro "msg" em dois lugares diferentes: nos mtodos onCreate(bund1e) e
onNewIntent(intent).

Dica: entender esse exemplo fundamental para dominar o desenvolvimento para


Android. Um receiver pode interceptar mensagens de broadcast. sejam elas da
sua aplicao ou de sistema. Porm, veja que o receiver no chamou a HainActivity,
pois isso agressivo demais. O que fizemos foi mostrar urna notificao para o
usurio. Quando o usurio clicar na notificao, disparada uma nova intent
com o objetivo de abrir a activity, pois agora podemos fazer isso, e foi o usu rio
quem executou a ao.

25.10 Mostrando uma barra de progresso na noticao


Uma funcionalidade bem interessante que foi adicionada ao Android 4.0 a opo
de exibir uma barra de progresso na notificao. Essa barra pode ser facilmente con
figurada pelo mtodo setProgress(ma, progresso, innito) da classe Notcation.Bui1der ~

Nesse mtodo, voc pode informar o valor maximo do progresso. corno 100. e
depois ir atualizando os valores para informar o status de determinado proces
samento diretamente na noticao. Esse conceito pode ser muito utilizado em
conjunto com servios que estao fazendo um processamento em segundo plano.
O metodo setProgress(ma, progresso, innito) tambm contem um flag do tipo
booleano. Caso voc informe true, os valores maximo e atual serao ignorados e
uma animao contnua sera exibida. Esse parmetro deve ser utilizado quando
voc no souber ao certo quando o processarnento sera finalizado.
A figura 25.9 mostra como seria a notificao com a barra de progresso normal e
com o flag infinito para true. Na figura da esquerda, o valor informado foi 5()*`t
com o seguinte cdigo:
bui1der.setProgress(100, 50, false);

Na gura da direita, o valor informado esta infinito e no determinado com o


seguinte codigo:
but1der.setProgress(9, 0, tFU);
CaPU|0 25 I Notication
693

Figura 25.9 - Noticao com barra de progresso.

25.11 Links teis

Nottcaes representam um mecanismo importante da plataforma do Android


para se comun1car com 0 usurio. O assunto parece simples, mas recomendo
fortemente que voc complemente seus estudos com a documentao ocial.
Android API Guides - Notications

http://developer: android.com/gaide/topics/ui/notiers/noticatiorzs.html
Android Training - Notifying the User

http://developer android. com/training/rzotiy-user/index. html


Android Design - Notications

http://developer arzdroid.com/design/patterns/notications.html
Android DevBytes: Noticaes no L Developer Preview [Portuguese]

https://wwwyoatube. com/watch P1/=EHTU5CxhoZ4*list=PLiGZ1/gu/A5Gmg5Eo


1/EGdPo5 SaFv 741 N dHx
J * ` cAPruLo 26
AlarmManager
*H

A classe Alarrnllanager permite agendar uma aplicao para ser executada em de


terminado momento no futuro. Para isso, uma Intent utilizada para enviar uma
mensagem ao sistema operacional na data e na hora desejadas.
Esse processo conhecido como agendar um alarme e pode ser usado para sim
plesmente agendar uma aplicao para executar todos os dias meia-noite, ou
para car repetindo determinado processo a cada hora.

26.1 Por que utilizar um alarme (agendar uma tarefa)


Imagine que voc precisa executar uma aplicao a cada 30 minutos, ou a cada hora,
ou executar algo todo dia ao meio-dia. Antes que sua criatividade comece a ganhar
asas, explicaremos a maneira correta de fazer isso, que utilizando um alarme.
Um alarme criado utilizando a classe androd.app.A1armManager e dene a data e
a hora em que uma Intent deve ser disparada ao sistema operacional. Depois,
qualquer aplicao pode interceptar essa mensagem/intent para iniciar uma
aplicao ou servio.
Na prtica, as intents dos alarmes so interceptadas por broadcast receivers, que
por sua vez decidem o que fazer. Talvez o receiver mostre uma noticao ou
simplesmente seja disparado um processo em segundo plano (veja o captulo 27,
sobre a classe Service).
A vantagem de um alarme que depois que ele ativado voc pode at esquece-lo.
Mesmo se o celular car inativo (entrar em modo de espera), o alarme continuar
l, vivo e pronto para disparar na data e na hora para as quais foi configurado.

694
Captulo 26 u AIarmManager 695
26.2 Mtodo da classe AIarmManager
Para a endar um ala ' ' .
pod 8emos vera classe
rme, utiliza-se os mtodos mais
classe. im ort d O a
android . app.AlarmManager
P antes essa
Na lista a seguir

5( D0 l09 'triggerAtMillis, Pendinglntent intent)


Metodo usado para agendar o alarme, informando a data e hora em que
ele de;/e ser disparado, bem como a intent que contm a mensagem a ser
envia a. O parmetro do tipo long o tempo corrido em milissegundos e
pode ser extraido a partir de objetos do tipo java.util.Date ou java.util.Calendar.

setRepeating(int tipo, long triggerAtMillis, long intervalMillis, Pendinglntent intent)


Idntico ao anterior, mas neste caso o alarme repetir continuamente de
tempos em tempos, especicado pelo parmetro intervalo em milissegun
dos. O alarme ser repetido at que o mtodo cancel(intent) seja chamado.
Isso til, por exemplo, se voc estiver desenvolvendo um aplicativo de
dieta balanceada e precisa lembrar o usurio de comer de trs em trs horas.

setInexactRepeating(int tipo, long triggerAtMillis, long intervalMillis,


Pendinglntent intent)
Este mtodo uma alternativa ao setRepeating( . . .) e segundo a documen
tao do Android ele economiza recursos. Caso mais de uma aplicao
agende um alarme na mesma hora, o Android consegue fazer otimizaes
e disparar o mesmo alarme para ambas as aplicaes.

cancel(PendingIntent intent)

.cria
`~osii' o,
5eto
m
Cancela o alarme baseado na intent fornecida, que deve ser a mesma

se , automa
l rme .
?

utilizada para iniciar o alarme.

aS _
C) , C1 t(tpo dataH0ra intent) e setRepeating(tipo,dataHora,intervalo,intent)
~ undo ainda e capaz de repetir o a a
fazem a mesma coisa, mas O Seg
lado. Independentemente de como o alarme
ticamente durante o tempo estip ' 1,40
' d b ta chamar o mtodo cancel(intent) para cance a .
'di o ue dispara
Omo.' larme
Para demonstrarcriar um
Csegue
emacaa 21
` d um
30 ums:
_ de co g q
trecho
HHH
o alarme exatamente as 9.00 am e rep
private AlarmManager alarmMgr;
t alarmlntenti
private Pendin9"te

// L o servio do AlarmMa89@f
Google Android - 4 edio

lfgr = (Alarmanager) context.getSystemService(Context.ALARM_SERVICE);


// Prepara a intent que vai receber o alarme
Intent intent = new Intent(contet, MeuBroadcastReceiverAqui.class);
alarmlntent = Pendinglntent.getBroadcast(contet, 0, intent, 0);
/I Calcula a hora para 9:00 am.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeHillis());
calendar_set(Calendar.HOUR_0F_DAY, 9);
calendar.set(Calendar.MINUTE, 30);
// Agenda a cada 30 minutos (precisa informar o tempo em milissegundos)
alarmHgr.setRepeating(AlarmManager.RTC_NAKEUP, calendar.getTimeInHillis(),
1000 * 60 * 30, alarmlntent);

Repare que ao chamar o mtodo setRepeating(...) informamos a constante


AlarmManager.RTC_NAKEUP, sendo que existem quatro constantes.

ELAPSED_REALTIME

Este tipo de alarme til se voc deseja dispar-lo a partir do tempo


atual. No necessariamente em uma hora especca como 9:OO, 9:30 ou
10:00. Por exemplo, se for 9:10, e voc disparar o alarme para daqui a 30
minutos, ele executaria s 9:40. Neste caso, voc no est interessado na
hora exata que o alarme vai executar, e sim em que ele execute daqui a 30
minutos ou uma hora.

ELAPSED_REALTIME_NAKEUP

Idem constante anterior, mas neste caso a CPU acordada para receber
o alarme, mesmo se o dispositivo estiver no modo de espera. Por padro.
os alarmes s so recebidos se estiverem ativados.

Dispara um alarme em uma data e hora especca que pode ser configu
rada no cdigo.

RTC HAKEUP

Idem constante anterior, porm acorda a CPU ao receber o alarme.


Baseado nas explicaes acima, eu geralmente utilizo uma data especica e prefiro
utilizar as constantes RTC e RTC_NAKEUP.
-s.
Captulo 26 n AIarmManager

26.3 Agendando um alarme


Para demon strar o uso dos alarmes vamos '
ou eS fpre
_ b erir a raumo projeto
_ criar ro eto chamado
dz
Dlg8mOS qL1 pI`ClSaITlOS COI'lStI` u1r

horas Como trs hora


HeIIoAIarme,
P J C exemplo deste capitulo no Android Studio.

objetivo mostrar alar f ~ ' um aplicativo de dieta balanceada e nosso


mes Para 0 usuario a m de lembr-lo de comer a cada trs
' ' tempo para testar, vamos fazer a mesma coisa,
e muito
porem vamos disparar os alarmes a cada 30 segundos

Nota: quando falamos e m executar ou agendar um alarme, estamos agendando


697

umaintent para ser disparada em determinadas data e hora. O alarme agendado


no .sistema operacional, portanto sua aplicao vai receber a intent mesmo se
estiver fechada.

A seguir, podemos verificar o arquivo AndroidManiest.xml do projeto:

AndroidManifest.xmI
<manfest ... package="br.com.lvroandrod.helloalarme" >
<applcation . . .>
<actvity androd:name=".ManActvty" androd:launchMode="sngleTop" ... />
receiver androd:name=".LembremeDeConerRecever">
<intent-lter>
<acti.on androd : name="br . com . livroand rod . helloalarne. LEMBREME_DE_COMER" />
<category androd:name="androd.ntent.category.DEFAULT" />
</ntent-lter>
</recever>
</applicaton>

l confi urou a MainActvty COH1()lH1


O arquivo AndroidManiest.xm 8_~
`d:l hM de="sn91@Tr" P0 _
is vamos criar uma noticaao e queremos man
androl aunc O essa activity na pilha. Tambm foi congurado um
ter apenas uma instncia Cl LEMBREME_DE_COMER, sendo que essa classe pode ser
broadcast receiver para a 21<a0
visualizada a seguir.

LembremeDeComerRecever.java
oticatontil;
import lvroandrod.lb.utls.N - ds BroadcastRecever {
- D ComerRecetver et
, , . TAG - ltvroa
publtc class Lembreme e . _ H . ndrodn;
prlvate Statlc nal String z "br.com.lvroandrod.helloalarme.LEMBREME_DE_COMER";
public static final String ACTION
593 Google Android - 4 edio
@0verride
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"Voc precisa comer: " + new Date());
Intent notifIntent = new Intent(context,MainActivity.class);
Noticationtil.create(context, 1, notifIntent, R.mipmap.ic_launcher,
"Hora de comer algo...","Que tal uma fruta?");
}

O receiver vai interceptar a intent que ser disparada pelo alarme e vai mostrar
uma noticao. A classe Notiticationutil voc pode criar como foi feito no pro
Jeto anterior, ou se preferir utilize a classe livroandroid.lib.utils.NoticationUtil da
biblioteca android-utils.

Para disparar os alarmes, vamos criar a classe AlarmUtil. O objetivo desta classe
apenas encapsular o acesso classe AlarmManager do Android.

AIarmUtiI.java
public class AlarmUtil {
private static nal String TAG = "livroandroid";
// Agenda o alarme na data/hora informado.
public static void schedule(Context context, Intent intent, long triggerAtMillis) {
Pendinglntent p = PendingIntent.getBroadcast(context, 1, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
Alarmanager alarme = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarme.set(AlarmManager.RTC_wAKEUP, triggerAtMillis, p);
Log.d("livroandroid-alarm", "Alarme agendado com sucesso.");
}

// Agenda o alarme com a opo de repetir


public static void scheduleRepeat(Context context, Intent intent, long triggerAtMillis,
long intervalMillis) {
Pendinglntent p = Pendinglntent.getBroadcast(context, 1, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
AlarmManager alarme = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarme.setInexactRepeating(AlarmManager.RTC_wAKEUP, triggerAtMillis, intervalillis, p);
Log.d("livroandroid-alarm", "Alarme agendado com sucesso com repeat.");
}

// Cancela o alarme
public static void cancel(Context context, Intent intent) {
AlarmManager alarme = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Pendinglntent p = Pendinglntent.getBroadcast(contet, 1, intent,
Pendinglntent.FLAG_UPDATE_CURRENT);
Captulo 26 1 A|armManage 699

alarme.cancel(p);
L09.d("livroandroid al " "
- arm

a' 'u1 a ... ..


} Alarme cancelado com sucesso.");
}

Feito isso no la out da ` '


um alame par); d 'fl.V1ty
A ....,
vaiP ada
re 30etir o alarme
Segundos. E o terceiroa
CIRCOvamos
c -vai~cancelar
adicionar
Segundos.
botao . o alarme.
tres botoes.
O segundo O primeiro
vai fazer a mesmavai agendar
coisa, mas

/res/layout/activity_main.xmI
<LinearLayout . . .>
<TextView android:tet="Agendar o alarme" . . . />
<Button android:tet="Agendar para 5 segundos" . . .
android:onClick="onClickAgendar"/>
<Button android:tet="Agendar e repetir a cada 30 seg" . .
android:onClick="onClickAgendarComRepeat" />
<Button android:text="Cancelar"
android:onClick="onClickCancelar"/>

Por ltimo, no cdigo da activity vamos adicionar os trs mtodos para tratar os
eventos.

MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

// Data/Tempo para agendar o alarme


public long getTime() {
Calendaf C z Ca1endar.getInstance();
tTimeMillis());
c.setTimeInMillis(SySt~CUffe
5) // 5 segundos
c.add(Calendar.SECOND

Inten
public
.. "){
long time z .getTimeInMilliS();
return time;

. C1 kA endar(View view .
void 02iniceaew
= Intent(LembremeDeComerReceiver.ACTI0N);
// Agenda Para daqui g 5 segundos
T ' );
AlarmU til.schedule(th1S, Intent' ge* 1me()
700
Google Android - 4 edio

Toast.makeTet(this,"Alarme agendado.",Toast.LENGTH_SHORT).show();
}

public void onCiickAgendarConRepeat(View view) {


Intent intent = new Intent(LembreneDeComerReceiver.ACTION);
// Agenda para daqui a 5 segundos, repete a cada 30 segundos
A1arnUti1.scheduIeRepeat(this, intent, getTime(), 30 * 1000);
Toast.makeTet(this,"Alarme agendado com repetir.",Toast.LENGTH_SHORT).show();
}

public void onCiickCance1ar(View view) {

l
Intent intent = new Intent(LenbremeDeConerReceiver.ACTION);
A1arnUtil.cance1(this,intent);
Toast.nakeText(this,"Alarme cancelado",Toast.LENGTH_SHORT).show();
}

Ao executar o exemplo e clicar no boto para agendar o alarme, a intent sera


disparada depois de cinco segundos. O receiver vai interceptar essa intent e mos
trar uma noticao conforme a gura 26.1- O segundo boto tambm agenda o
alarme para daqui cinco segundos, a diferena que o alarme car repetindo a
cada 30 segundos.
l

/r~tiU;~i ^.2' iif_

AGENDAR PARA S SEGUNDOS

AGENDAR E REPETIR A CADA 30 SEG

CANCELAR

Alarme agendado
l

FKHHI-/UUHNUCMHIHHUUL
f9
Captulo 26 n AlarmManager 701
26.4 Repetindo o alarme
No cdigo que z
. . eemos Para repetir o alarm
a cada
Caso voc prera utilizar cons
30 segundos foi passado o
tempo em milissegundos, portanto zemos

long INTERVAL_HALF_HOUR = 2 * I
a co ,
nta 30 * 1.000.

tantes para fazer essa conta a classe AlarmMana er


contem algumas, conforme demonstrado a seguir' ,
long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000'

NTERVAL_FIFTEEN MINUTES;
INTERVAL_HOUR = 2 * INTERVAL_HALF_HOUR; _
INTERVAL_HALF_DAY = 12 * INTERVAL_HOUR;
INTERVAL_DAY = 2 * INTERVAL_HALF_DAY;

Portanto, para agendar um alarme com a opo de repetir todo dia, poderamos
utilizar a constante AlarmManager.INTERVAL_DAY.

// Agenda este alarme com a opo de repetir todos os dias no mesmo horrio.
AlarmUtil.scheduleRepeat(this, intent, getTime(), AlarmManager.INTERVAL_DAY);

26.5 Classe Calendar

Para disparar um alarme, necessrio fornecer o tempo em milissegundos, ou


seja, a data e hora. No exemplo que zemos anteriormente, o objetivo era pegar
a data atual e somar cinco segundos, assim o alarme seria agendado para daqui
a cinco segundos. Isso foi possvel com a ajuda da classe java.util.Calendar.
Calendar C z Calendar.getInstance();
c . setTimeInMillis(System . currentTimeMillis( ) );
c.add(Calendar.SECOND, 5); // 5 5e9U"d5

_.-~
long time = c.getTimeInMillis();
f aula
nao e de
dar Calendar, ate porque isso e assun ' ` ' to para
1 '/ bre
S Ca lin 'Mas
uagem Java . ~con
Meu objetivo aqui uma
7 apenas para dar uma dica, essa classe
um hvro ; ntrolar facilmente datas e horas, assim como fazer operaoes
como
tem metoaumentar
O P ma horauaumentar
um dia etc. Outro exemplo interessante
data de hoje s 9:30 am.
como congurar o Calendar com 8
();
Calendar calendar =.Calendar.getInstance'
tTimeM'uS());
1
calendar. setTimeInMill1S(5y5te'cume"
Y, 9 5
calendar . set(Calendar . H0UR_0F_;2)'
calendar.set(Cle"dar'MINUTE' , )
lon9 time = C-9@tTeI"M11S();
702 Google Android - 4 edio
Se voc quiser que esta data seja criada apenas amanh, basta somar um dia ao
criar no Calendar.

Calendar calendar = Calendar.getInstance();


calendar.setTmeInMlllis(Systen.currentTneMlls());
calendar.set(Calendar.HOUR_0F_DAY, 9);
calendar.set(Calendar.MINUTE, 30);
calendar.add(Calendar.DAY_0F_HONTH, 1);
long time = c.getTneInMllis();

Como eu disse, meu objetivo no dar uma aula sobre Calendar; portanto, caso
voc no conhea essa classe, recomendo que a estude.

26.6 Quando utilizar ou no um alarme


Para nalizar este captulo, veremos uma lista de quando necessrio utilizar um
alarme e quando seu uso no recomendado.
recomendvel utilizar um alarme para:
1. Agendar uma aplicao para ser executada em determinado momento no
futuro, ou seja, agendar o disparo de uma intent.
2. Um servio no deve car executando eternamente para no utilizar recur
sos e memria demais. aconselhvel utilizar um alarme para agendar o
servio na data e na hora desejadas e se necessrio repetir o alarme para
acordar o servio.
No recomendvel utilizar um alarme para fazer uma thread dormir e acorda-la
em determinado momento. Para isso, necessrio utilizar a classe Handler, a qual
j estudamos. Caso voc queria executar operaes de tempos em tempos dentro
de uma activity a classe Handler tambm deve ser utilizada, pois ela contm os
mtodos postDelayed(Runnable, long) e postAtTme(Runnable, long). Lembre-se de que
o Handler ca atrelado ao ciclo de vida de uma activity por isso utiliz-lo nesses
casos recomendado.
O alarme deve ser utilizado justamente quando a aplicao no est executando,
ou provavelmente no est, pois neste caso o Handler no dar conta do recado.
Por ltimo, lembre-se: a principal vantagem de um alarme disparar uma intent na
data e na hora desejadas. Um broadcast receiver pode ser utilizado para interceptar
a mensagem e acordar a aplicao, mesmo com ela fechada. Ao interceptar uma
mensagem com a aplicao fechada, lembre-se de que o recomendado mostrar
uma noticao para o usurio.
Captulo 26 I AIarmManager 703
26.7 Links teis
Neste captulo, aprendemos a agendar alarmes. Para continuar seus estudos, separei
um link da documentao ocial.
Android Training - Scheduling Repeating Alarms

https://developer android.com/training/scheduling/alarms.html
J ~* CAPTULO 27
Service e Joblnfo
`*1

A classe Service utilizada para executar um servio em segundo plano, geralmente


vinculado a algum processo que deve executar por tempo indeterminado, e tem
um alto consumo de recursos, memria e CPU.
Um Service tambm pode ser utilizado para criar aplicaes que so executadas
em segundo plano, sem a necessidade de exibir uma interface ao usurio.
Neste captulo, aprenderemos a criar servios que cam executando em segundo
plano e entenderemos como funciona o seu ciclo de vida.

27.1 Introduo
A classe android.app.Service utilizada no Android para executar um processa
mento em segundo plano por tempo indeterminado, chamado popularmente
de servio. Um servio geralmente faz um alto consumo de recursos, memria
e CPU. No precisa interagir com o usurio e consequentemente no precisa de
interface grfica.
Geralmente um servio com a classe Service iniciado a partir de um BroadcastReceiver 7

o qual precisa executar rapidamente e retornar do mtodo onReceiveIntent(context,


intent) o mais breve possvel. Por isso, as classes BroadcastReceive e Service formam
uma dupla comum no Android, pois o receiver intercepta a mensagem enviada
pela intent, e se necessrio um servio iniciado para executar o processo, uma
vez que o receiver precisa terminar em dez segundos.
A classe Service faz parte do ciclo de vida dos processos controlados pelo sistema
operacional, e seu processo no ser finalizado enquanto estiver em execuo.
a no ser que as condies de memria do celular estejam realmente baixaS,
situao na qual o Android pode decidir encerrar alguns processos para liberar
memria e recursos.

704
Captulo 27 n Service e Joblnfo

.._emoria --
705

ara r ` ' "


O interessante que, mesmo que um Servicese` a
mria, o Android Posteriormente tent ' J encerrado devido falta de me
Commue asslm . ~ emma 10 Para que o processamento
que as condioes de m f ~
normais. Nesse caso, o servio deve ser codi C Os recursos unhzados estqam
I`CL1pI`81' O S8(lO Cm (1116 O I`OCS
cado de uma forma que seja possvel
P samento foi encerrado anteriormente.

de threads
ao e ' - '- ,' ,
27.2 Exemplos de servios
Geralmente, studar servios pela primeira vez, e comum confundir o conceito
e servios, pois ambos executam de forma assincrona e teoricamente
em segundo plano.

Primeiramente, no confunda o termo servio no Android com web service?


Web service um tipo de servio que executa l no servidor web e tem como
objetivo fornecer contedo para que os aplicativos consultem informaes. No
Android, se voc ouvir o termo servio' provavelmente um atalho para refe
renciar a classe android.app.Service.

Tambm no confunda o conceito de threads com um servio em segundo plano.


Uma thread deve ser utilizada sempre que o objetivo for desvincular um proces
samento da thread principal. o caso de quando uma activity precisa buscar
informaes em um Web service ou banco de dados porque, a m de no travar a
interface, uma nova thread deve ser iniciada. Um servio criado pela classe Service
tem um propsito diferente e pode at executar sem uma activity estar aberta.
A seguir, podemos ver uma lista de possiveis situaoes nas quais um servio
poderia ser utilizado, para voce entender bem o conceito.
. Quando voc instala um aplicativo pelo Google Play utilizado um servio.
Veja que enquanto o d ownload est sendo feito mostrada uma notica
anhar o status, e o usurio est livre para acessar
o para o usurio acomp
qualquer outro aplicativo ~ _ _
Claramente podemos ver que um servio fica
executando sem a necessidade de a aplicaao ou activity estar aberta.
^ criou um 'o o mas, P ara diminuir o tamanho do download inicial
. Voce J g , artefatos do jogo como imagens, mapas, cenarios,
do aplicativo, todos OS _
sons etc. ficaram no servidOr Na primeira vez que o usurio instalar o jogo,
1 download de t0
ele tera de f8Z1` 0
dos esses arquivos. Como esse processo
ndado iniciar um servio. Durante o download,
pode demorar, 0 femme
r fazendo outra coisa, como navegar na internet.
o usurio pode continua
706 Google Android - 4 edio
Voc recebeu uma mensagem de push do servidor informando que existem
atualizaes importantes que seu aplicativo tem de fazer. Caso voc precise
consultar um web service para isso, pode utilizar um servio. Tudo deve
ocorrer sem atrapalhar a atividade do usurio.
Por ltimo, vou citar um aplicativo de checklist de carros, o qual desenvolvi
para uma empresa de auditoria. Basicamente, o aplicativo ajudava a fazer uma
inspeo no carro, e, caso alguma avaria (risco, amassado, batida) fosse encon
trada, ela era registrada no aplicativo e vrias fotos eram tiradas O operador
do aplicativo que fazia as inspees podia at trabalhar de forma offline o dia
inteiro. No nal do dia, quando uma conexo Wi-Fi estivesse disponvel, o
aplicativo fazia o sincronismo com a internet. Isso era feito com um servio em
segundo plano, sem atrapalhar a atividade do usurio. No nal do sincronismo
das informaes, o aplicativo mostrava uma noti cao ao usurio, inforrnando
que todas as inspees tinham sido coletadas corretamente.

27.3 Como iniciar e parar um servio


Depois da teoria inicial, vamos aprender como iniciar um servio. Primeiro,
necessrio criar uma subclasse de androd.app.Servce e congurar o arquivo
AndroidManiest.xml, como todas as outras classes do Android, adicionando uma
simples tag <servce>, como mostrado a seguir:
<servce androd:name=".He11oServce" /

E para iniciar o servio basta chamar o mtodo startService(intent):


startService(new Intent(ths, He1loService.c1ass));

Caso prera disparar o servio com uma ao, utilize a tag <ntent-1ter> para
congurar a ao que deve disparar o servio. O atributo androd:eported="fa1se"
indica que esse servio privado da aplicao, de forma que outras aplicaes
no podem utilizar essa intent para inici-lo.

<ntent-1ter> '
<servce android:name=".He1loService" android:eported="fa1se">

<action android:name="ACAO_PARA_INICIAR O SERVICE" />


<category android:name="androd.ntent.category.DEFAULT" />
</intent-1ter>

Neste caso, poderamos iniciar o servio com o seguinte cdigo:


startServce(new`Intent("ACAO_PARA_INICIAR O SERVICE"));
(3PtuIo 27 I Service e Joblnfo
707

. . _ _ o uti izando ' ' ~


Eu particularmente prero iniciar o servi '1'
unhzar uma Intent lmplcita com O no d a Primeira oPao, ou seja,
I me a classe que quero executar.
O metodo startService(intent) inicia um servi
indeterminado O inte _ ` 0 que fica executando por tempo

omeooso ' ' - ...


mo se O usuri- _ SSalate do servio e que ele vai continuar executando mes
De ois de in' ` ' '
O Salt a aphcaaa Ou 5613, O servio independente da activity

P, t d tlcaf 0 5fV10, X1Stem duas maneiras de par-lo. A primeira, chamar


D @FV1C@(1'C) com a mesma intent que foi utilizada para inicia-lo.
A Segunda C O prprio servio se autoencerrar com o mtodo stopSelf().
Para exemplicar quando parar o serv io com um mtodo ou outro, vou explicar
utilizando o aplicativo do Google Play como exemplo. Quando o usurio solicita
a instalao ou atualizao de um aplicativo, o Google Play inicia um servio. No
nal do download, o prprio Google Play pode terminar esse servio, chamando
o mtodo stopSelf( ). Porm, durante o download, o usurio tambm pode decidir
entrar no Google Play e interromper a instalao, talvez porque ele pretende desli
gar o Wi-Fi ou o celular est cando sem bateria. Como isso uma ao iniciada
pelo usurio, neste caso utiliza-se o mtodo stopService(intent) para solicitar ao
Android que encerre o servio.

27.4 Exemplo prtico


Vamos colocar a mo na massa e criar o projeto HelloService no Android Studio. O
exemplo que vamos criar um clssico, o qual vai executar umloop com um
contador de O at 10 imprimindo as mensagens no LogCat. O objetivo e apenas
simular que algum processamento est sendo realizado.
Para criar um servi o p _ ,
reciso criar uma subclasse de android.app.Service e imple

mentar obrigatoriam nte o mtodo IBinder onBind(intent). Opcionalmente, tambem
e ntados os mtodos onCreate(), onStartCommand(intent,ags, startld)
podem ser implem ~ ' classe Service.
. ' 'ro servio:
e onDestroy(), referentes ao ciclo de Vlda da
- ` lizar a classe de nosso Pflmel
A seguir, podemos VISU3

HeIIoService.java
_ , . -~ ' Util;
import livroandroid.lib.utils.Notif|cat10
public class HelloService extends Service {
Drivate static nal int MAX = 195
TAG z "livro";
private static final String
protected int count;
private boolean running;
Google Android - 4 edio

@0verride
public IBinder onBind(Intent i) {
// Por enquanto vamos deixar null. Depois vamos estudar isso.
return null;
1

@0verride
public void onCreate() {
Log.d(TAG, "HelloService.onCreate() - Service criado");
}

@0verride
public int onStartCommand(Intent intent, int ags, int startld) {
Log.d(TAG, "HelloService.onStartCommand() - Service iniciado: " + startld);
count = 0;
// Mtodo chamado depois do onCreate(), logo depois que o servio iniciado
// 0 parmetro startId representa o identicador deste servio
running = true;
// Delega para uma thread
new NorkerThread().start();
// Chama a implementao da classe me
return super.onStartCommand(intent, ags, startld);
}

// Thread que faz o trabalho pesado


class workerThread extends Thread {
public void run() {
try {
while (running && count < MAX) {
// Simula algum processamento
Thread.sleep(1000);
Log.d(TAG, "HelloService executando... " + count);
count++;
}

Log.d(TAG, "HelloService m.");


} catch (InterruptedEception e) {
Log.e(TAG,e.getMessage(),e);
} nally {
// Autoencerra o servio se o contador chegou a 16
stopSelf();
// Cria uma noticao para avisar ao usurio que terminou
Context context = HelloService.this;
Intent intent = new Intent(contet,MainActivity.class);
Noticationtil.create(context, 1, intent, R.mipmap.ic_launcher,
"HelloService", "Fim do servio.");
}
}
}
CPtuIo 27 n Service e Joblnfo

}
}

@0verride

public void onDestroy() {


// Ao encerrar o servio, alt

running = false;
ere 9 Para a thread parar (isso ' '
// para encerrar a thread caso al ' . E Importante
QUEM tenha chamado o stopService(intent)

L09-d(TAG, "He11oService.onDestroy() - Service destrudo--).

flg 0 Codlgo <f0mI?13f importe a classe iivroandroidlib.utiis.NotificationUti1 da


1 ioteca android utils. Feito isso, congure o servio no arquivo de manifesto.
709

AndroidManifest.xmI
<appiication . . >
<activity android:nane=".MainActivity" ... />
<service android:nane=".He11oService" /

Observe que no cdigo da classe He1ioService no tem nada de muito complica


do. Basta implementar os mtodos do ciclo de vida e o mtodo onBind(intent). O
importante entender que um service executa na thread principal da aplicao,
podendo prejudicar seu desempenho. Portanto, recomendado utilizar uma
thread ou um AsyncTask dentro do service, para executar o processamento pesado.
No cdigo eu chamei essa thread de workerThread. Apenas para simular um proces
samento demorado, a thread est fazendo um loop at 10 e chamando o mtodo
Thread ,51eep(1G00) para dormir por um segundo a m de demorar um pouco para
tgfminar o Servio Quando o valor do contador chegar a 10, o loop da thread ter
mina 5 o mtodo 5topSelf() chamado, o qual encerra o ciclo de vida do servio,

0. .~'
fazendo com que o AndrOd Ch ame o mtodo onDestroy(), encerrando o processo
para liberar memria e recursos utilizados. Outra forma de parar um servio
simplesmente cham ando 0 mtodo stopService(intent).

tava executando em segundo plano precise informar


Nota. ao O Servlo q o terminou, utilize uma noticaao. Um exemplo d1sso

, . . - 1o.
ao usuario que a execL1 1. tivo
6 qU21Hd0 VOC Instala
iniciado para baixar O HP , _
ica pelo Google Play. Nesse caso, um servio
un? ap. ' 1 "o termina uma noticaao
l1cat1vo, e quando a msta aa
e utilizada para avisar ao ullfg F _ O
710
Google Android - 4 edio

Para mostra r como iniciar e parar o servio, vamos criar uma activity com os
botes Start e Stop no layout.

/res/layout/activity_main.xml
<LinearLayout android:orientation="vertical" ...
<TetView

android:tet="Eemplo de servio, verique os logs no LogCat."


android:layout_width="wrap_content" android:layout_height="wrap_content" /
<Button
android:layout_width="match_parent" android:layout_height="wrap_content"
android:tet="Start" android:onClick="onClickStart" />
<Button
android:layout_width="match_parent" android:layout_height="wrap_content"
android:text="Stop" android:onClick="onClickStop" />

No cdigo da activity implemente os mtodos para tratar os eventos dos botes


a m de iniciar e parar o servio. Observe que a mesma intent usada para iniciar
o servio utilizada para par-lo.

MainActivity.java
public class MainActivity extends AppCompatActivity {
@0verride

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

public void onClickStart(View view) {


startService(neu Intent(this, HelloService.class));

public void onClickStop(View view) {


stopService(new Intent(this, HelloService.class));
}

Pronto! Feito isso, execute o projeto e clique no boto Start para iniciar o servio. O
resultado da execuo voc pode conferir no LogCat, que deve mostrar as seguintes
mensagens, que mostram o servio executando e incrementando o contador de O
a 10. Voc pode inclusive sair da aplicao que 0 servio vai continuar executando.
pois esse o seu propsito.
Captulo 27 n Service e .oblnfo
711
D/1ivro( 2606): He1oService.
onCreate() - 5 ervice criado
D/1ivro( 2606): HeioService.
onStartConnand() - Serv ice iniciado: 1
D/1ivro( 2606): He1oService
executando...
D/iivro(2606): He1oService executando...
D/1ivro(2606): HeioService executando...
D/iivro(2606): He1oService executando...
D/1ivro( 6): He1oService executando...
D/1ivro(2606): He1ioService executando...
D/1ivro(2606): He1oService executando...
D/1ivro(2606): He11oService executando...

77.
D/1ivro(2606): He11oService executando...
D/iivro( 6): He11oService executando...
D/1ivro( 6): Hei1oService m thread: 10
D/1ivro(2606): He1ioService.onDestroy() Service destruido

Observe que o prprio servio encerrou seu processo com a chamada do mtodo
stopSe1f(). Neste exemplo, caso o boto Stop seja chamado antes do m, o servio
ser nalizado antes de o contador chegar a 10.
Embora para acompanhar o resultado deste exemplo voc deva olhar no LogCat, a
figura 211 mostra a noticao que criada quando o servio terminar por conta
prpria ou seja o contador chegar a 10. Noticaes so muito utilizadas nesses
casos para avisar ao usurio que um servio terminou, se for algo que ele estiver
esperando, como uma atualizao de dados do aplicativo.

r_c;gCa. l
Exempo de servuz *@5fUe os WQS no l

START

STOP

J'. ' ddo servio


a notic00
mostran 0 um
Fzgura 221 ~ Fim
712 Google Android - 4 edio
lsso tudo. Para criar um servio, basta criar uma subclasse de android.app.Service
e declarar a classe no arquivo AndroidManiest.xmI com a tag <servce>. Depois, o
sistema operacional do Android se encarrega do resto, executando a classe em
segundo plano at que ela mesmo encerre o processamento com a chamada do
mtodo stopSe1f() ou algum chame o mtodo stopServce(ntent).

Nota: lembre-se de que a classe Service executa na thread principal da aplicao.


Portanto, para executar o processamento, recomendado iniciar uma thread
dentro do servio. Caso contrrio, o servio poder prejudicar o desempenho
do aplicativo que esta interagindo com o usuario.

27.5 Deixar o servio executando depois de sair de uma tela


O que acontece com o exemplo anterior, se, logo depois de iniciar o servio, o
usurio sair da tela? A resposta que a activity ser destruda, mas o servio no.
Ele continuar executando em segundo plano automaticamente. Para encerrar
o servio, basta iniciar a activity novamente e pressionar o boto Stop ou esperar
que o servio termine por conta prpria.
Enm, este breve tpico foi apenas para lembr-lo de fazer o teste de fechar a activity
e deixar o servio executando, pois essa a verdadeira essncia de um servio.

27.6 Entendendo o ciclo de vida de um servio


O primeiro exemplo que demos de um servio foi simples, e basicamente isso
o que voc precisa fazer. Neste tpico, vamos discutir alguns aspectos mais avan
ados que bom voc dominar.
A gura 27.2 mostra o ciclo de vida da classe Service.
Fonte: lirtp://developerandroid.com/guide/components/services.html
Ao chamar o mtodo startServce(ntent), o Android cria o servio apenas se ele
no estiver em execuo. Se o servio no existir, o mtodo onCreate() chamado.
Logo depois, o mtodo onStartComnand(ntent,ags,startId) chamado para iniciar a
execuo do servio. possvel chamar o mtodo startService(ntent) vrias vezes, e
se isso acontecer o Android chamar o mtodo onCreate() somente da primeira vez.
Nas outras vezes, apenas o mtodo onStartConnand(intent,ags,startId) chamado.
informando 0 id da execuo desse servio, que vamos verificar adiante. Quando
o servio for finalizado, o mtodo onDestroy() chamado.
Captulo 27 n Service e Job|nf0
713

e, Cimo. .

,
0nCreate()

StartComman<1() i

if,...-....-_.r
_&~\*

,xoService
running'
\`^----`--....--~..
. ,.. of V

The service Is stopped


by itself Of a client

j onDestroy()
f "-~.
Unbounded
service

Figura 222 - Ciclo de vida da classe Service.

Observe que o mtodo onStartComnand(intent,ags,startId) retorna um nmero in


teiro, o qual deve ser uma das constantes a seguir, que definem o comportamento
que ocorre caso o Android encerre o processo do servio por motivos de falta de
recursos e memria, entre outros.
Constante Descrio
START NOT STICKY Indica que, caso o sistema encerre o service, ele no ser re~
criado, a no ser que ainda existam intents a serem entregues.
START_STICKY Este o retorno padro caso o super.onStartConmand(. . .) da

co service,
am ele _ _
classe me seja chamado. Indica que, caso o sistema encerre
ser recriado, e o mtodo onStartComnand(. . .) ser
h ado novamente Mas, neste caso, a intent recebida como
parmetro ser nula. Se voc no precisar dessa intent, essa
constante dever ser utilizada.
START__REDELIVER_INTENT
Idem anterior, mas a mesm a intent que iniciou o service
entregue novamente. Se o service utiliza a intent para dife
renciar o contedo, como cada thread pode fazer o upload
' ` ' m ido.
de diferentes arquivos, utilize essa constante para receber a
intent n OV2lI`I'1I'l[, C3SO O SCl'VlC SCJH 1I`l[I`I`O p
714 Google Android - 4 edio
Nota: no importa quantas vezes o mtodo startService(intent) cha mado: uma
umca chamada ao mtodo stopService(intent) nalizar o servio.

Conhecer o ciclo de vida da classe Service importante, pois a maneira como voc
vai gerenciar as threads dentro do servio vai depender do que voc precisa fazer.
Se voc deseja que exista apenas uma thread, poder cri-la no mtodo onCreate( ),
de forma que novas chamadas no afetem a thread que j est executando.
Voc tambm pode criar uma nova thread a cada chamada do mtodo
onStartCommand(intent,ags,startld), mas deve se preocupar com o fato de o cdigo
ser multi-threading. Nesse caso, o parmetro startld pode identificar a thread.
Esse parmetro startld deve ser armazenado para encerrar o servio com o m
todo stopSe1f(startId). Veja que esse mtodo recebe o id da thread que precisa ser
interrompida, diferentemente do stopSe1f(), que utilizado para destruir todos
os servios.

Dica: no projeto de exemplo deste captulo, voc vai encontrar a classe


HeiioService_workerThread, que demonstra como criar uma thread a cada chamada
do mtodo onStartCommand(intent,ags,startld). Assim, cada vez que o mtodo
startService(intent) for chamado, uma thread separada ir executar.

27.7 A classe IntentService


At o momento, vimos como criar um service com a classe android.app.Service. Agora
vamos estudar a classe android.app. IntentService, que facilita um pouco esse trabalho.
Conforme estudamos anteriormente, um service executa na mesma thread da
aplicao, portanto recomendado iniciar uma thread separada para cuidar do
processamento, a fim de no prejudicar 0 desempenho da aplicao. Ns j estu
damos o ciclo de vida da classe Service e vimos que a cada chamada do mtodo
startService(intent) chamado 0 mtodo onStartCommand(intent,ags,startId), 0 qual
utilizado para fazer uma programao multi-threading, se necessrio.
Porm, na maioria das vezes isso no preciso, nem mesmo recomendado, pois voc
precisa evitar problemas de concorrncia. Por isso foi criada a classe IntentService,
que lha de Service e implementa todos os seus mtodos. Para utilizar a classe
IntentService, voc s precisa implementar o mtodo onHand1eIntent( Intent intent),
e a intent utilizada para ler os parmetros enviados ao iniciar o servio.
Captulo 27 I Service e Joblnfo 715
. . ICQ e que ela ' cria uma thread d nt
A van tagem de utilizar a classe IntentServ' '
dela, inclusive
_ _ - n ocom umacada
assim, la de execuoao
chamada Semtodo
d J e ro
e, .garantido
^ P essapara o pelavoceIntentService. Portanto,
startServ1ce(intent) entra nessa la que roc d
'
mado sequencialmente, se h x .
_ que Smpre o metodo onHandleIntent(Intent intent) ser cha
m nen um problema de concorrenc1a.Enm, a classe

7 7 S7
IntentService faz o que provavelm ente voc teria de fazer se ela no existisse.
Outra vantagem da classe IntentService que ela chama automaticamente o mtodo
stopSelf () ou stopSelf(startId) no nal da execuo' portanto como eu disse ante
voce so precisa implementar o mtodo onHandleIntent(Intent intent).
O cdigo de exemplo a seguir mostra como implementar um servio com a classe
IntentService. Estamos utilizando o mesmo exemplo do contador de O a 10. Note
que no preciso criar uma thread nem chamar o stopSelf(), pois o IntentService
j faz tudo isso internamente.

He||oIntentService.java
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("NomeDaThreadAqui");
}

private static nal int MAX = 103


private static nal String TAG = "livf0";

@0verride _
private boolean running;

protected void onHandleIntent(Intent intent) {


running = true; em
d uma threa _
uan
't d executa
stedgeeleoterminar o mtodo stopSelf() ser chamado automaticamente
int count = 0;
while (running && count < MAX) f
fazAlgumaCoSa(); ,_ _
Log d(TA(;, "ExemploServico executando... + COUH),

} , f. u ) ,
count++;

} 'sa() f
L0g_d(TAG, "ExemploServico IN-

private void fazAlgumaC01


try {
/ Simula al9UW PVCe5Samento
Thread.sleeD(100)5 _ ) {
} catch (IHEFFUPEXCDUOH e
Log.e(TAG,e.getMessage(),e);
}

@0verride
public void onDestroy() {
super.onDestroy();
// Ao encerrar o servio, altera o ag para a thread parar
running = false;
Log.d(TAG, "EemploServico.onDestroy()");
}

Para testar esse servio, basta execut-lo, e o resultado ser o mesmo do exemplo
anterior.
startService(new Intent(ths, HelloIntentService.class));

27.8 (riando um player mp3


Servios geralmente so criados para executar algum processamento pesado ou
demorado, como o sincronismo com algum web service.
Mas s vezes o que queremos executar em segundo plano no necessariamente
pesado, mas precisa ser feito apenas pelo fato de que o processo precisa continuar
executando. Esse o caso de um player mp3, pois depois que o usurio escolhe
a msica e clica no boto Play ele provavelmente vai sair do aplicativo para fazer
outra coisa. Portanto, a msica deve continuar executando.
No capitulo 21, sobre multimidia, criamos um exemplo simples de um player
mp3 que tinha os botes Play, Pause e Stop. Porm, ao sair da activity ns paramos
a msica, pois assim que funciona o ciclo de vida da activity
Neste prximo exemplo, vamos aprimorar o player mp3 que zemos no captulo
21 e vamos tocar a msica dentro de um servio. Mas para fazermos isso teremos
de praticar alguns conceitos um pouco mais avanados. No se preocupe, pois
vamos fazer tudo passo a passo, e o resultado ser muito legal.
Para comear, copie o projeto PIayerMp3 e renomeie para PIayerMp3Service. Esse projeto j
deve estar funcionando e tocando um mp3, mas lembre que, do jeito que zemos
0 cdigo, o arquivo mp3 precisa existir no SD card.
A primeira classe que vamos implementar a Mp3Servce, que o servio que vai
executar a msica em segundo plano.
Captulo 27 n Service e Joblnfo

Mp3Service.java
public class Mp3Service extends Service implenents Int f
private static nal String TAG z "1vr0". er aceMp3 {
private PlayerMp3 player = new p1ayerMp3E). S

private String mp3;


// Classe lha de Binder para retornar no onBind(intent)
public class Mp3ServiceBinder extends Binder {
// Converte para InterfaceMp3
public InterfaceMp3 getInterface() {
// Retorna a interface para controlar o Service
return Mp3Service.this;
}

}
@0verride
public IBinder onBind(Intent intent) {
// retorna a classe ConexaoInterfaceMp3 para a activity utilizar
Log.d(TAG, "Mp3Service onBind(). Aqui retorna o IBinder.");
return new Mp3ServiceBinder();
}
@Override
public void onDestroy() {
// Fim do servio
Log.d(TAG, "Mp3Service onDestroy().");
// Para a msica
5D()
}

// Mtodo da interface InterfaceMp3


public void start(String MD3)
this.np3 = WD3;
try {
player.start(mp3);
} catch (Exception e) {
L0g,e(TAG, e.getMessag() );
}

// Mtodo da interface InterfaceMD3


public void pause() {
player.pause();
} 3
// Mtodo da interface InterfaceMp
public void stoD() {
player.stoD();
713 Google Android -4= ediao
}

// Mtodo da interface InterfaceMp3


public String getMp3() {
return mp3;
}

// Mtodo da interface InterfaceHp3


public boolean isPlaying() {
return player.isPlaying();
}

Observe que adicionamos um atributo do tipo PlayerMp3 classe do servio, pois


essa classe que vai tocar a msica, e no a activity
Outro detalhe: a classe Mp3Service implementa a interface InterfaceHp3, portanto
crie-a no projeto.

InterfaceMp3.java
public interface InterfaceMp3 {
void play(String mp3); // Inicia a msica
void pause(); // Faz pause da msica
void stop(); // Para a msica
boolean isPlaying(); // Retorna true se est tocando a msica
String getMp3(); // Caminho da msica
}

O segredo da classe Mp3Service que ela implementou o mtodo onBind(intent), o


qual at o momento sempre tinha nos retornado um valor nulo. Isso necessrio
para expor a interface de comunicao, que a InterfaceMp3, ao cdigo cliente
que est utilizando o servio, que neste caso a activity O servio implementa
a interface InterfaceMp3, pois ela chamada de interface de comunicao e ser
utilizada para se comunicar com 0 servio. No mtodo onBind(intent) retornada
a classe interna Mp3ServiceBinder, a qual permite obter a interface InterfaceMp3 com
o mtodo getInterface(). Isso ser utilizado pela activity quando ela se conectar
ao servio que est tocando a msica.

Dica: o conceito de interface na Orientao a Objetos bem difundido e ajuda a


separar as responsabilidades da aplicao, alm de frequentemente ser usado para
fazer com que uma camada ou servio da aplicao se comunique com outra.
Assim, a aplicao que vai reproduzir a msica no precisa conhecer detalhes
de como 0 cdigo foi implementado; basta chamar os mtodos dessa interface.

f fw .Y
Na prtica a1Vlty
-D33
Captulo 27 n Service e Joblnfo

TVI O O " `
act' ' vai se conectar ao se
terface Interfacem 3 h I bI` U.I'I`l8. I`fI`l'lCl para 3. lfl
D 6 C amar os seus metodos 1
vamos partir para o cdigo da activity que a ymp
parte,mais
pauseo, Stopo etc. Agora
complicada.
719

MainActivity.java
import livroandroid.lib.utils.NoticationUtil;
public class MainActivity extends A ppConpatActivity {
private static nal String TAG = "livro";
// Classe que encapsula o MediaPlayer
private EditText text;
private InterfaceMp3 interfaceMp3;
private Serviceonnection conexao = new ServiceConnection() {
public void onServiceConnected(ConponentNane classNane, IBinder service) {
/I (*3*)
// Recupera a interface para interagir com o servio
Mp3Service.Mp3ServiceBinder conexao = (Mp3Service.Mp3ServiceBinder) service;
interfaceMp3 = conexao.getInterface();
Log.d(TAG, "onServiceConnected, interfaceMp3 conectada: " + interfaceMp3);
}

// ** ._
public void onServiceDisconnected(ComponentNane className) {

Log.d(TAG, "onServiceDisconnected, liberando recursos. );


interfaceMp3 = null;
}

};
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle); _
setContentView(R.ly0Ut~aCt1V1tyfam)'_ _
text = (edttrext) fndvewById(R-ld-t^1)'
Intent ntent z new Intent(this,Mp3Service.class);
Log.d(TAG, "Iniciando o service");
I/ (*1*)
startService(intent)
// Faz o bind/l9H
/I (*2*) .c-r~rt,BIND Auro0"CREATE);
conexao. 9'-
boolean b = bindService(inten H b
L09.d(TAG,"Service conectado: + )5
}public void
V'ew view) {
onClickPlaY( 1
I/ (*4*)
if(interfaceMp3 != null) {
720 Google Android - 4' edio
String np3 = tet.getTet().toString();
Log.d(TAG,"play: " + np3);
interfaceHp3.play(np3);
}

public void onClickPause(View view) {


I/ (*4*)
if(interfaceHp3 != null) {
Log.d(TAG,"pause");
interfaceMp3.pause();
}

public void onClickStop(View view) {


I/ (*4*)
if(interfaceMp3 != null) {
Log.d(TAG, "stop);
interfaceMp3.stop();
}

@0verride
protected void onStop() {
super.onStop();
if(interfaceMp3 != null && interfaceMp3.isPlaying()) {
// ("5*)
Log.d(TAG, "Activity destruda. A msica continua ");
unbindService(coneao);
// Cria a noticao para o usurio voltar ao player.
String mp3 = interfaceMp3.getMp3();
Noticationutil.create(this,1,new Intent(this,MainActivity.class),"HP3 Player",mp3);
} else {
/l (*7*)
Log.d(TAG, "Activity destruida. Para o servio, pois no existe msica tocando.");
unbindService(coneao);
stopService(new Intent(this, Hp3Service.class));
}

Antes de eu explicar 0 cdigo, execute o projeto no emulador e clique no boto


Play para tocar a msica. Lembrando que, para funcionar, o arquivo de mp3 pre
cisa existir no SD card do emulador. O resultado pode ser visto na gura 273.
Mesmo que 0 usurio feche a aplicao, a msica vai continuar executando. Ao
sair da activity, estamos inserindo uma noticao na barra de status, pois issu
f- ~. ist
721'_
Captulo 27 n Service e Joblnfo

e um padrao comum para indicar que ex


Pode clicar na notificao Para voltar ao . ap
umicativo.
`W
Servlo executando 6 0 U5UI`lO

Player MP3 do sdcard

/sdcard/Music/linkin_park1.mp3

* DU rj

Figura 223 - Player mp3.

Agora vamos entender o cdigo da MainActivity. Para facilitar a explicao, observe


que deixei marcaes no cdigo.

(1) Inicia o servio


O mtodo startService(intent) foi chamado logo ao abrir a activity para
iniciar o servio em segundo plano. Esse servio por enquanto no faz
nada mas permanece vivo aguardando a prxima instruo. Lembre-se
7

de que um servio faz p arte do ciclo de vida da plataforma do Android,


e portanto o Android vai deix-lo executando l.

. , eOredo
(2) Conecta ao serviom de tudo.
arametros um_J_I _
Pfqul esta O S g A Qbeto que implemente a interface
todo bindService(intent,con,ags) pre

usa passar nos P ' mtodos de callback para quando o codigo


ServiceConnection, a qual conIm
desconectado do servio.
da activity conectado OU

(3) Recupera a interface de comuniC33

, . ., fV1OeOme , z do b-1_dSe`V.C(`l.tl,CO,fl8gS) for bem-sucedida o


Se a Chamada do meto ' 'todo onServiceConnected() ser chama
C0d1g0 lfa S6 Conectar go S unicao InterfaceMp3 e recuperada.
a interface de COI
do. Nesse rnoIT1U0
722 Googie Anam-4- edio
Tudo consiste em programao Java, mas se tiver diculdades em entender
o cdigo no se preocupe, continue lendo e depois tente novamente. Na
segunda vez sempre mais fcil.

(4) Usurio ciica nos botes Piay, Pause e Stop


Uma vez que o servio foi iniciado e a activity conectou-se a ele, podemos
tocar a msica. Portanto, os mtodos que tratam os eventos da tela sim
plesmente chamam os mtodos da interface InterfaceMp3, a qual foi obtida
quando nos conectamos ao servio. Veja que essa interface de comunicao
o segredo de tudo, pois por meio dela a activity pode chamar mtodos
que sero executados l no servio.

(5) A msica ca tocando mesmo se sair da activity


No mtodo onStop() da activity feito o teste para vericar se o servio est
tocando alguma msica. Se estiver, a activity simplesmente desconecta
do servio chamando o mtodo unbindService(conn). Como nesse caso o
servio continua executando, uma noticao criada para mostrar ao
usurio qual msica est tocando, para permitir que o usurio volte para
o aplicativo de forma fcil.

(6) Remove a interface de comunicao

Logo depois de se desconectar do servio, o mtodo onServiceDisconnected()


chamado. Nesse momento, o objeto que contm a interface de comuni
cao no mais vlido.

./v'd
(7) Encerrando o servio caso no esteja tocando nenhuma msica
No mtodo onStop() da activity caso a activity no esteja tocando nenhu
ma msica, no tem razo para deixar o servio executando em segundo
plano. Por isso, o mtodo stopService(intent) chamado para encerr-lo,
liberando os recursos e memria. i
Esse exemplo avanado, mas demonstrou como uma activity pode se conectar
a um servio que esta executando em segundo plano. Vimos que por meio 21
interface de comunicao, a qual exposta pelo servio, a activity pode invocar
mtodos l dentro da classe do servio.
Entenda que o servio iniciado pelo mtodo startService(intent) e permanecc
executando at que o mtodo stoptService(intent) seja chamado. Sempre qu 0
activity for iniciada, independentemente de o servio j estar executando OU
AO
.eza
_.
encerrar aXamv.
Isso signica ue Qd
'
(aPtuIo 27 n Service e Joblnfo 723
no, o mtodo bndService(intent,con,ags) utili d
, _ o para se conectar ao servio.
0 metodo unbtndService( COHH) e chamado para desconectar.

troasse
da erC1 odser
a inter ace ~todo
. , .den
. _e gcomunica '
Vezes a m deqobtp mosfnos onectar e desconectar de um servio
ao e chamar al umdiversas
me
t S _ VIO- E, quando o servio nao for mais necessario, o mtodo
S OP @FV1C@('C't) Chamado para encerrar tod o o processo.

27.9 Mtodo bindService(intent,con,ags)


Caso voce precise de mais informaes sobre o mtodo bndServce(ntent,con,ags)
que estudamos no tpico anterior, este tpico visa complementar o assunto.
O mtodo bindService(intent,con,ags) pode iniciar um servio se ele ainda no
estiver executando ou simplesmente conectar-se a ele. Ao estabelecer a conexo,
possvel recuperar uma referncia para a interface de comunicao.
Espere, voc prestou ateno na frase? Eu disse que este mtodo pode iniciar um
servio ou se conectar a ele. Isso significa que no necessariamente voc precisaria
chamar o mtodo sta rtService(intent) para iniciar o servio. Conforme estudamos,
o mtodo startService(intent) utilizado para iniciar um servio, mas tambm
podemos fazer isso usando o mtodo bndService(ntent,con,ags).
O mtodo bindService(intent,con,ags) pode iniciar um servio se ele ainda no
estiver executando chamando o mtodo onCreate() da classe Service, mas ele no
chama o mtodo onStartConnand(). Esse mtodo usado principalmente para se
Conectar ao servio, obtendo uma referncia de uma classeou interface que pode
ser utilizada para se comunicar e chamar mtodos do servio. Essa interface e do
tipo android.os.IBnder, e a aplicao est conectada ao servio enquanto manter
uma referncia para 6121

A seguir podemos ver a descrio dos parmetros deste metodo.


7

Parmetro Descr _
ntgnt Intent para executar o servio.
Ca Imp , ' Connected(c1asse,1 tn e
emen da interface android . content. ServiceConnection, que dene os
1 rao
'b' d r)eonServiceDisconnected(classe)
metodos onServtce
~ ~ hamados para noti`car que uma conexo com o servio
05 quais sao C
foi realizada OU encerrada, respectivamente.
ags _ ' sario. E se for BIND_AUTO_CREATE, o servio automaticamente
OU BIND_AUTO_CREAT 2 I _
criado, caso seja neces
.c10 de vida de um servio executado com o mtodo
A gura 274 mostra O C1
bindService(intent,Ca95)
724 Google Android - 4 ediao
cum q
`_ ~ '- yr' ,.'r, ..\z..a.
\` .. .. .. .`-$*...l.,<.-z.ia..z_...':/

onCreate()

l
onBind()

i
' Clients ara
,i.
bound to
service

All cinema unoma by caling


unoinasowum =
l
onunbmam

l
OD68lI'Oy0

l
Service
HL lyhilt down _,
Boundod
servico

Figura 224 - Ciclo de vida ao chamar o mtodo bindService().

Conforme vimos anteriormente, o mtodo startServce(ntent) tem por nalidade


maior iniciar um servio completamente desvinculado do processo principal de
quem o criou. J o objetivo principal do mtodo bndService(ntent,con,ags) es
tabelecer uma conexo com um servio j em execuo, para literalmente chamar
mtodos da classe que representa e controla esse servio.
No caso de o servio ser iniciado pelo mtodo bndServce(ntent,conexao,ags)
, obrigatrio implementar corretamente o mtodo onBnd(intent), que precisa
retornar uma referncia para uma interface de comunicao, 'a qual deve expor
mtodos para controlar o estado do servio ou consultar informaes.

Nota: ao criar um servio com o mtodo bndService(intent,conexao,ags), o processo


ca vinculado a quem o criou, que, neste caso, a activity. Isso significa que,
se a activity for encerrada, o servio tambm ser. o contrrio do mtodo
startServce(ntent).
Captulo 27 u Service e Joblnfo 725
27-10 Qual mtodo utilizar para iniciar um servio?
entender a di ~, - - l
A80f8 que vimos as d
ele iniciado. uas maneiras de se conectar a um servio importante
ferena em relaao ao seu ciclo de vida dependendo da forma como

Entenda que ambos os mtodos startServce(ntent) e bndServce(ntent,coneao,ags)


podem iniciar o servi
o. Com o mtodo bndService(ntent,coneao,ags), o pro
cesso do servio est vinculado a quem se conectou a ele, por exemplo, uma
activity Ento, se a activity for encerrada, o servio tambm ser. J o mtodo
startServtce(tntent) deixa o servio executando por tempo indeterminado, de for
ma independente da activity; at que algum chame o mtodo stopServce(ntent).
Com o mtodo startServce(ntent), possvel deixar o servio executando em se
gundo plano mesmo depois de o processo que o criou ser nalizado. Mas somente
com o mtodo bndServce(ntent,coneao,ags) possvel conectar-se ao servio e
recuperar a interface de comunicao para interagir com ele.
Ento, qual mtodo utilizar para iniciar um servio? O melhor utilizar os dois
juntos. Por exemplo, possvel iniciar o servio com o mtodo startServce(ntent)
para garantir que o servio que executando continuamente e depois chamar o
mtodo bnServce(ntent,coneao,ags) para se conectar ao servio j iniciado e obter
a interface de comunicao. Posteriormente, os mtodos unbindService(conexao) e
stopServce(ntent) podem ser chamados para desconectar do servio ou destru-lo,
respectivamente, conforme a situao desejada. Foi isso que zemos no exemplo
do mp3 player.

27 11 Um servio em execuo contnua no consome muito processamento?


Um fator importante que deve ser considerado ao desenvolver um servio que
no recomendado deix-lo executando continuamente para no consumir
muitos recursos.

tem de verifica , ~ . .
Ima ine ue seu serviO
ecise baixar Pfservidor
atualizaes de um I _Web,_mas_antes
g q r se existem atualizaes disponiveis. Ele poderia car dormindo
de minuto em minuto Pa ra realizar essas vericaoes, o que poderia consumir
muito processamenw
- ' duas ^maneiras
' r.' lver essadesituao
Basicamente existem
resotudo
, depende na
verdade do que VOC Pfeclsa faze
72 owgiznnami-4zd;
A primeira maneira utilizar um alarme criado com a classe androd.app.A1armHanager
e agendar uma intent para acordar o servio na data desejada. Os alarmes pode
riam ser utilizados para executar o servio de hora em hora, ou executa-lo todos
os dia s meia-noite, por exemplo. A maneira que gosto de fazer disparar um
alarme que vai acordar um broadcast receiver, que por sua vez apenas inicia o
servio chamando o mtodo startService(ntent).
A segunda maneira o servio no car buscando dados no servidor, na tentativa
de verificar se existem atualizaes. O melhor seria o servidor enviar uma mensa
gem de push para o dispositivo, para avisar aplicao que existem atualizaes.
Vamos estudar sobre mensagens push no prximo captulo, mas basicamente
o push uma mensagem enviada do servidor para o dispositivo, a qual utiliza
a conexo com a internet como canal de comunicao. No aplicativo, o push
recebido por meio de um broadcast receiver. Neste caso do servio, quando a
aplicao receber a mensagem de push, ela poderia buscar os dados no servidor.
Isso economiza recursos e bateria do dispositivo, e bem mais eficiente do que
car de tempos em tempos perguntando ao servidor se existem atualizaes.

Nota: o objetivo de um servio executar algum processamento pesado em


segundo plano sem interferir na atividade do usurio. Mas voc no deve deix-lo
executando para sempre, pois o servio pode consumir muitos recursos. Utilize
alarmes para agendar a data/hora em que o servio deve executar, ou utilize
mensagens push para acordar o aplicativo.

27.12 Joblnfo - a nova API do Lollipop


Para fecharmos este captulo com chave de ouro, vamos estudar uma das APIs
mais interessantes que foi criada no Android 5.0 Lollipop, que a API de jobs. Um
job uma tarefa que pode ser agendada para executar em determinado momento.
desde que ele satisfaa os critrios estipulados como gatilho para essa execuo.
Podemos dizer que um job uma mistura de um alarme e de um servio qu
acabamos de estudar. Com a API de alarmes, conseguimos disparar uma intent
na hora desejada e at program-la para repetir de uma em uma hora, ou repetir
todos os dias. E o servio (classe Service) especializado em executar uma tarefa
em segundo plano.
O job meio que mistura essas duas APIs. O job um tipo de servio que p0d
executar em determinados momentos, conforme voc tenha feito o agendament0,
de maneira parecida com a que agendamos um alarme.
[\ - , .
CaPtuIo 27 n Service e Joblnfo 727
Vantagem d1<>b qu O gatilho para ele executar pode ser:
Existe conexo Wi-Fi.
O dispositivo foi conectado na USB_

Voc^ * ' . . .
O dispositivo entrou em modo bloqueio.
O dispositivo est carregando a bateria.
e talvez nao esteja interessado na hora que o job vai executar, mas sim nas
condioes/criterios sob as quais ele ir executar. Sendo assim, podemos execu
tar o job sempre que o dispositivo estiver plugado e com uma conexo Wi-Fi
disponivel. Esta API e de grande ajuda em muitos casos, mas compatvel com
Android 5.0 ou superior. Na poca em que este livro estava sendo escrito, no
existia nenhuma biblioteca de compatibilidade, porm quem sabe o Google logo
no construir uma?
Para agendar um job, basta criar um objeto do tipo Joblnfo utilizando o
JobInfo.Builder. Feito isso, basta obter uma referncia da classe JobScheduler e criar
o job, representado pela classe Joblnfo. Para brincarmos com a API de Jobs, crie
um projeto chamado Helloloblnfo. Feito isso, crie a classe JobUtil, que mostra como
agendar um job para executar assim que o dispositivo estiver conectado ao Wi-Fi
e estiver carregando.

JobUtiI.java
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class JobUtil {
public static void schedule(Context context, Class<?> cls, int id) {
// JobService que vai executar
ComponentNane mServiceConponent = new C0F1D0@'CNF1(C0t, Cl-S);
JobInfo.Builder builder = new Joblnfo.Builder(id,nServiceConponent);
// Ni-Fi
builder setRequiredNetworkType(JobInfo.NETHORK_TYPE_UNMETERED);
// Carregando
builder.setRequiresChargin9(FUE);
// Agenda o j0b
h d l r obScheduler =
Jobsc e(oScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
Joblnfo job = builder.build();
jobScheduler . schedule( job);
}
728 Google Android - 4 edio
public static void cancel(Contet context, int id) {
JobScheduler jobScheduler = _ . .;
jobScheduler.cancel(id);
}

public static void cancelAll(Contet context) {


JobScheduler jobScheduler = . . .;
jobScheduler.cancelAll();
}

Com essa classe utilitria em mos, vamos criar o layout e o cdigo da activity
Teremos apenas um boto para agendar o job e outro para cancel-lo. Deixarei o
layout com voc. Basta criar dois botes e chamar os mtodos onClickAgendar(view)
e onClickCancelar(view), conforme demonstrado a seguir.

MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
}

public void onClickAgendar(View view) {


int id = 1;
JobUtil.schedule(this,HelloJobService.class, id);
Toast.makeText(this,"Job agendado",Toast.LENGTH_SHORT).show();
}

public void onClickCancelar(View view) {


JobUtil . cancel(this, 1);
}

Esse cdigo vai agendar o job da classe HelloJobService para executar assim que
o dispositivo estiver carregando a bateria e com uma conexo Wi-Fi disponvel.
A classe HelloJobService deve ser lha de JobService e deve implementar o mtodo
onStartJob() responsvel pela lgica do job. A classe JobService_por sua vez lha
direta de Service, portanto tudo o que estudamos sobre servios continua se apli
cando. No mtodo onStartJob(), voc deve iniciar uma thread para desvincular 2
tarefa do job da thread principal da aplicao.
A seguir, podemos visualizar o cdigo-fonte da classe HelloJobService.
CaPtuIo 27 n Service e Joblnfo
729

HeIIoJobService.java
public class HelloJo
bService extends JobService {
private static nal Stri"9TAG = "livroandroid";
@Override

public void onCreate() {


super.onCreate();
Log.d(TAG, "onCreate()");
}

@Override

public int onStartCommand(Intent intent, int ags, int startld) {


Log.d(TAG, "onStartCommand()");
return super.onStartCommand(intent, ags, startId);
}

@Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob(): " + parans.getJobId());
Intent notIntent = new Intent(this,MainActivity.class);
String title = "Job";
String contentTitle = "Hello Job: " + parans.getJobId();
NoticationUtil.create(this, R.mipmap.ic_launcher, notlntent, R.mipmap.ic_launcher,
title, contentTitle);
return true;
}

}...
@Override
public boolean onStopJob(JobParaneters DFWS) {
Log.d(TAG, "onStopJob()");
return true;
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy()");
}

_ f. tJob()
. . . _e onara
P _I. _
_ , mesmos mtodos de ciclo de vida da classe Service.
Note que O Job Contem Os Sto Job() so especcos para informar quan
Apenas Os metodos onstar p finalizar a congurao, basta adicionar a tag
do
aoOAndroidMcmfe5t-xm
Job Val lmclar 6 parar le informara permisso BIND_JOB_SERVICE,necessaria

para utilizar os jobs


73 Google Android - 4' edio
AndrodManifest.xmI
<appl.icaton . . . >
<servce androd : nane=" .He1loJobServce"
androd:pernsson="androd.permission.BIND_JOB_SERVICE" androd:eported="true"/>
</app1caton

Pronto! Isso tudo. Agora execute o projeto no seu Android e clique no boto
para agendar o job. Veja que com o celular plugado na USB e com Wi-Fi o job vai
executar. Neste exemplo, o job est somente mostrando uma noticao, apenas
para voc saber quando ele executou.
Esta API nova, mas tem grande potencial. Sua documentao ainda escassa,
e ca at difcil recomendar uma leitura, mas espero que em breve exista algo na
documentao ocial. Recomendo tambm importar no Android Studio pelo
menu Import Sample o exemplo lobScheduler, que com certeza vai ajud-lo.

27.13 Links teis


Neste captulo, aprendemos a executar servios em segundo plano e zemos at
um exemplo com um player mp3.
Para continuar seus estudos, separei alguns links interessantes.
Android API Reference - Service

http://developer android. com/ reference/android/app/Service. html


Android API Guides -Service

http://developexandroid.com/guide/components/services. html
YouTube Google Developers - Using the Android Job Scheduler

https://www. youtube. com/watch?v=QdI N LG5 Qr] c


G* CAPTULO 28
W GCM - Google Cloud
gq Messaging

Neste capitulo, vamos estudar o Google Cloud Messaging, um servio disponi


bilizado pelo Google para enviar mensagens por push.

28.1 O que push?

Tradicionalmente, aplicativos utilizam web services para se comunicar com o servi


dor, e a comunicao sempre iniciada pelo lado do cliente, ou seja, o dispositivo.
Contudo, esse paradigma atende bem a alguns casos, como por exemplo abrir uma
tela para visualizar uma lista de carros ou notcias. Porm, dependendo do caso,
esse paradigma de o dispositivo iniciar a conexo pode no ser a melhor soluo.
Um exemplo clssico que podemos citar o aplicativo do Gmail. Voc j pensou
como ele faz para monitorar os novos emails que chegam sua caixa de entrada?
Para implementar essa vericao, o dispositivo poderia iniciar uma conexo de
e etitivamente com o servidor, para car buscando os novos
I`I`1pOS CITI tI'I`lpOS I' p
emails Mas o problema que voc pode receber emails de cinco em cinco minutos
ou, s vezes, de c inco em cinco horas; ento, de quanto em quanto tempo voc
deve monitorar o servidor?
justamente por n o saber essa resposta, a soluo inverter os papis e fazer o
servidor acordar o cl iente. Isso possvel graas s mensagens de push, que so en
Viadas dg Sgfvidor para o aplicativo. Por esse motivo, os dispositivos Android sempre
mantm uma pequena co nexo ativa com os servidores do Google, para criar um
canal de comunicao que fi ca dormindo, porm pode receber mensagens a qualquer
ica o utilizado para que o servidor do Google
momento. Esse canal de cornu
- ~ ' le acorde e rea`a a determinado
- d1spos1t1v0 Para que e J
envie uma mensagem ao

731
732 g1zAniwii-- iam
*`\`\`"\\ No Caso do (imail. o servidor do Lioogle envia uma mensagem de push
informando que existem novos einails disponveis e que o dispositivo pode, ento.
Iniciar uiu \veb serviu: para fazer o sincmnismo mm o servidor.
P590 Phkvsso de o servidor enviar uma mensagem para o celular conhecido
\""\\* Plh. v o servico responsavel por iinplementar essa tarefa no Android chama
-Se Gooqleoud Messaging ou. simplesmente. GCM.

Noti: o tamanho do conteudo da mensagem de push que o servidor do Google


envia ao dispositivo e pequeno. com limite de 4 bytes. Portanto. ao enviar a
mensagem para o dispositivo. utilive um texto pequeno. por exemplo. um simples
codigo. apenas para indicar que existem novos emails disponiveis no servidor.
importante entender que os emails ou qualquer outro tipo de conteudo no so
enviados com a mensagem de push. pois isso excederia facilmente o tamanho
da mensagem. Receber uma mensagem de push e como receber um alerta (ping)
indicando que existem informacoes no servidor.

28.2 (omo funciona o GCM

Para enviar uma mensagem de push para o dispositivo e preciso obter o seu codigo
unico. chamado de registration id. Para isso o aplicativo deve fazer uma consulta no
servidor do GCM e obter o registration id. conforme ilustrado na gura 28.1.

'**Y ' il E
' ..f '_ 1. * 1 -\
..
z' -~ .'i 1-t
_3.\z_"- leiblvu
*I * /'
z* 7 . s il?? ._
' . . ii_):.i4, i z
:xp \. _
. - _.
L.:..-, \sn;Ibsvvw~-.|i.oinn~u\==*l\.dQ-nIv-1.(;"\l$_I
, -..l fz. \ QT

*X ^ I~ .
_ negz>tzat'.o ED

VJ.
-_ f 1 ` `_.1z \V ~,
.. .................,............,. _.....z...)

,- **
.W _..\\5i
%< zz___3 leltlfew U ?=Cl\`i
;:_ -..~wvf=-- 1..e;~-z~~~z.z ... z~.-z-~-=~,-z~zz.~zz~ -.~-.~~.--..n-z --- 4- ,
_.

hgura 28.1 - Uliterrdo im: tzj;isrr.itiov: id.

De-pois de obter o registration id do dispositivo. e rcsponsabilidade sua salva-lo em


algum lugar. Geralmente os aplicativos enviam o reqistntionid para um servidor da
empresa. e pode existir uma tabela de usuarios no servidor que vincule dctermr
n.ido usuario do sistema aplicativo ao reqistntion id. Dessa forma. quando o servidor
Ptecisat se connmlvar com o usuario. basta ele consultar o banco de dados 6 lo
o nqistrationid do usuario deseiado.
em . , . .
13ging

o. as lembre-s ' . .,
. 733
~
Captulo 28 I GCM - Google Cloud Messa

Este processo de env` '


isso consiste d f 0 f9|5fI0I\ Id para um servidor nao faremos no livro, pois
M esenvolver um pequeno sistema, o que esta fora do nosso esco
P 6 de que no capitulo 17, sobre web services, Ja mostramos como
fazer HTTP Post em web services , portanto voc ter todas as condies de fazer
algo assim quando precisar.

Para ns de testes, HSS1m que gerarmos o registration id no aplicativo, vamos copi-lo


manualmente e inseri-lop no
g a ro
queram f que por sua vez vai enviar a
aremos,
mensagem de push.
A gura 28.2 demonstra o uxo para enviar uma mensagem. Observe que o seu
servidor nunca vai se comunicar diretamente com o dispositivo. Na prtica, a
mensagem enviada ao servidor do GCM, e este por sua vez entrega a mensagem
ao dispositivo. Se existir uma conexo disponvel com o dispositivo, a mensagem
enviada quase que instantaneamente; caso contrrio, o prprio GCM aguarda
para enviar a mensagem quando o dispositivo se conectar.
O disposto receber a mensagem por meio de um broadcast receiver que deve
interceptar toda as intents da ao com.goog1e.androd.c2dm.ntent.RECEIVE.

2) Entrega mensagem
1) Envia mensagem

Canal de comunicao

Figura 28.2 - Enviando uma mensagem de push.

28.3 Gerando a chave de acesso do GCM


. . ' ' e'onecessari
. . meiramente habilitar o servio na pgina
Para utilizar o GCM PU
do Googl DVPIOPPYS Console'
https://console.devel0PeT5'300gle'Com/
d 21 agl
, .na do Console no captulo 22, sobre mapas, voc j sabe
Como j estu amO5 1 minha Orientao, deve ter criado um projeto chamado
^ 56 L11L
utihz-10' se VOC1
Livro Android
a
no qua PI Google Maps Android API v2 est habilitada.
- edirrlfO entre no menu AP|s&auth > APIs e habilite a API
Desta vez, repita o proc forme a figura 283.
Google Cloud Messa99 ff ^"d'd Con
, a
7" owtzmna-4-amo
~._tir, fz
J ,U
._ I ~. I.$..

, va i ,I

I| I.I
Figura 28.3 - Habilitando o GCM.

E da mesma forma que geramos uma chave para a aplicao dos mapas, desta vez
vamos gerar uma chave para 0 servio do GCM. Contudo, essa chave no ser
gerada para a aplicao cliente no Android, mas sim para a aplicao que ser o
servidor do push, a qual vai enviar as mensagens para o dispositivo.
Portanto, entre no menu Credentlals para criar u ma nova chave de acesso, chamada
de API Key. Lembre-se de que voc j deve ter uma chave API Key feita no captulo 22,
sobre mapas. Clique no boto Create New Key para criar uma nova chave e escolha
a opo Server Key. Na prxima janela, voc pode restringir os endereos IPs dos
servidores que podem enviar as mensagens de push. N nosso exemplo, deixe
este campo de texto vazio e clique no boto Create.
A gura 28.4 mostra a chave criada. Copie o cdigo do API Key antes de prosseguir,
pois vamos utiliza-lo posteriormente no servidor para enviar a mensagem de push.

Key for never applications l


l IP: Any IP ollowed I
l m eu Aizzemcozmeomnqntommuuqarzzu I
Acttvuion duo Mu 26. 2015. 1150.00 AM l
l Aeumcu by Iwvomdvodqmm wf (YOU)
I

= Edit david lP| Roqomme Iuy Domo I


Figura 28.4 - (eran a API Key para o servirlor do push.
jj. <.
(PU|0 23 I GCM - Google (loud Messaging 735
28.4 Obtendo o Project Number

Ainda na pgina do Google Develo ers C 1


projeto 6 Copie O cd

lQ

l
- Google
~-~~-

jLivro
180dOP projeto

'
Gl' ` f .L
. .
- .__

Overview
onso e, entre

l
"' ' * ~z~
4M_._

Android
- `l
A _.
.W
na pgina
conhecido
gura 28.5. como Project Number, conforme a

_a.
' > Overview do

E553 Codigo Utilizado no aplicativo Android para se registrar no GCM a fim de


obter o registration id que identifica o dispositivo

<P .s--._~.-_._.rr_..__,o ___. g__\__ E ZE


:LDevelopers .. r
_ _"rx
V, H
_ _,~ ~'V' '
.. _`;MV
-g_'~E https.f/consoIe.deveiopers.googie.com/project/smcere~aimanar:-42 ? E j
1 (;1(3Sl\ j

Yi -r ' ;7_"V`f Sign up for a free trial. Iivro3ndrod@gm3|_Q0m

i mlems Project ID; sincera-almanac-435 Project Number: 9995590520538 j


i

Acftvty for the last fl days 1 hour (ih 12h y 2d i


Permissions APS

Request
P5 3 l-h Requestss
Biing settings

i,-_._ ao se Wa.-a.._--....- -W W =
Figura 28.5 - Obtendo 0 Project Number
li

28.5 Executando o projeto de exemplo


^ ' ' ' He oPus is onve nos
O digo do projeto Android para fazer o push funcionar ui plouco extpnso,
portanto recomendo que voce teste Pf1m1f0.0 QYOJCO P I
exemplos do livro Assim voc j ter uma boa ideia do que queremos fazer, alem

ra O P - ^ ' d Goo le
de ser jegal VermOS a mensagem de push chegando ao dispositivo.

. f do projeto que voce criou na sua conta o g .


Ab rojeto HeIIoPush no Android Studio e altere a constante PROJECT_NUMBER da
interface Constants pelo numf0

Constantsjava
package br.1ivro.androd.CP23~PU5h5
public interface Constants f I . 8 do Google Developers console
// Project Number criado na DHQW
999559052058"$
String PROJECT_NUMBER = "
}
/lb
Google Android - 4 :digno

hill.. z~:. .|\:m,iu. Lfxcumr u puJ:u um um d>m:|Uvu u:.i| umA1u|uuI uma


*'%*' '~W 5l`\-lwS u\:'.1l.aI.

_-1 ~.

* 1*H|\#l|H.\*\llH|ug|Irmz(.( ML'zregismltinnidhw||pl|\'u|1||u.~,
*l l* LJ H 'BW- HU |'.u uHlIlH.l. || Iegifin id quai .|mlu L' |I:z lug lu
|\l;(a.l.|1ul\ \.HllH^lll||I./J u p.xl.l f1l\'I.ll` .1 lllLll.l}{3ll|.

iiili 1..Imh 115% QEGWIRAR

)
ligam ._8.(~-l'L4c1J Ifxjllu mv (A M

28.6 Enviando a mensagem de push


(Hill U EQS alion id ln Lli>|z>u\ uu lll\>.7 \'.llllU* um um |)lH1,,lllll.l vm Luzn
J ll13LlBL'll|
l'.m zm uu um.: uuruz azgun lv puh .u ||.|u\., 1 Hu; .zum il/.\'l um H HI
||\|`1\.l'\V|(l(l|L|(|ll\H|g|\U( '.*i'lll',ll.lrzcllllllh'
ml Mw. 5. E UR
z

hltp //.uulnul.`,;._1;lm1> . n4;;. III/JLUIHI

lu uhl)pu`lll\l do la n'1
Nu H ml |l\\.|Tn|llU1\`|l\|U uu|l.\1uu|r,zu(Ily,u\l|;;leqitldlinl
. _lllm
, .x lll\'l1>J;\ill zum U* |1xI'\lHs'll"~. ||.\ URI xlzllllkl-1
.Mu |\u.u .a\\,.|Iu\l.z |u|ul^|.1 HI ll u'uuul| Aulhu uauou., mu \.zl1
kuyAPl hay |'.ll.l \|\'llhll*\|~|I \'l|\ 'wul u~\ uNly wytlu' Hill.: Iu[|rl|\ Il\.u..l\
n\|||z.mI\ I.\\.\.
Captulo 28 I GCM - Google Cloud Messaging

SendPushMessage.java
public class SendPushMessage {
// Registration id do dispositivo

_ _ ea e new server key)


Pf1Va@ Static nal String oEv1cE_REo1sTRAT1oN_1o = "APA91bGZNbjA2zeXelrPA26PeA . .
// Chave criada no Console. Menu > API AQCQSS > (cr t
private static nal Strin API K z " - ~
o . O 9 _ EY AIzaSyCMCG]v]eoAmI8]llDH4388EJI4qJFZsU";
public static void main(String[] args) throws IOEception {
MaP<5tr"9 5tF9> params = new HashMap<String, String>();
params.put("msg", "0l, Leitor");
String result = post(API_KEY, DEVICE_REGISTRATION_ID, params );
System.out.println(result);
}

// Faz POST no servidor do Google "httpsz//android.googleapis.com/gcm/send"


public static String post(String apiKey, String deviceRegistrationId, Map<String
String> params) throws IOEception {
// Parmetros necessrios para o POST
StringBuilder postBody = new StringBuilder();
postBody.append("registration_id").append("=").append(deviceRegistrationId);
// Cria os parmetros chave=valor
Set keys = params.keySet();
for (String key : keys) {
String value = params.get(key);
postBody.append("&")-6DD@d("dHtH-")-HDD@d(ky)-DD@d("=")-DD@d(
URLEncoder.encode(value, "UTF-8"));

// Cria a mensagem H H
}

// Faz Posr _ _, _
byte[] postgata z postB0dy.t0String().9etBY@5( UTF'3 )

URL url = new URL("htD5//"dVd-991eap1S'cm/gcm/Send )'


Https onne
URLC ction setDefaultHostnameVerier(new CustomizedHostnameVerier())
HttpsU RLC0nnectOn Conn = (HttpsURLConnectio) Url-P@"C0"@C0();
conn.setDoOutpU(fUe)3
conn.setUseCaches(falS)5
conn.setRequestMethod("P05T")5
P rty("C0nt@t-TYP" ..
conn.setRequest rowlform ur1enCoded.Char5et=UTF-3 );
"app1Caton/- _ .. I t_ntfb" Integer.toString(postData.length));
LHQ
Con"5etRequeStProperty("Conherization", "key=" + aPK@Y)3

L^ a reSD05a .
conn.setReQUSPF0P@ftV( Aut O

tD:tStream out = conn.get0utputStream(),


out.write(postDat8);
out.close();
int responsetode = conn.getResponseCode();
f(FesponseCode == zeo) {
// OK
String response = conn.getResponseHessage();
return response;
} else {
Systen.err.println(responseCode + ": + conn.getResponseHessage());
}

return null;
}

private static class CustonizedHostnaneVerier inplenents Hostnaneverier {


public boolean verify(String hostnane, SSLSession session) {
return true;
}
l
l

Para testar esse cdigo, crie um projeto java em alguma IDE, como o Eclipse. Lembran
do que uma classe java pode ser executada no Eclipse pelo menu RunAs>Java Application.
Mas antes de executar o cdigo altere a constante DEVICE_REGISTRATION_ID para o valor
que voc copiou do seu dispositivo ao se registrar no GCM.
// Registration id do dispositivo
private static nal String DEVICE_REGISTRATION_ID = "APA91bGZNbjA2zeXelrPA26PeA ..... ";

Altere tambm a constante API_KEY com a API KEY que voc copiou do seu projeto.
// Chave criada no Console. Menu > API Access > (create new server key)
private static nal String API_KEY = "AIzaSyCMCGjvjeoAmI8jllDH4388EJI4qJFZsU";

Pronto! Agora execute o cdigo para enviar a mensagem de push para o disposi
tivo. Ao receber a mensagem, o aplicativo vai mostrar uma noticao, conforme
mostra a gura 281
Para nalizar, importante saber que a requisio HTTP enviadaao servidor do
GCM pode retornar os seguintes cdigos:
Retorno HTP Descrio
E E Indica que a mensagem recebida cm sucesso, e o servidor do GCM
tentar entregar a mensagem.
461 Indica que ocorreu um erro ao autorizar o envio da mensagem. Neste
caso, voc deve vericar se a chave utilizada est correta.
5 Qualquer cdigo de retorno entre 500 a 599 indica que ocorreu um
erro no servidor do GCM.
_
Captulo 28 n GCM - Google Cloud Messaging 739

REGISTRAR CANCELAR

Ol Leitor

Figura 28.7 - Enviando a mensagem.

Para obter mais detalhes, recomendo ler a documentao ocial.


https://developerandroid.com/google/gcm/index.html

28.7 Criando o projeto Android passo a passo


Agora que j sabemos como o push funciona, vamos criar o projeto Android passo
a passo. Se preferir, apenas abra o projeto de exemplo deste captulo no Android
Studio e acompanhe a explicao.
Se for criar o projeto do zero, crie um projeto com o nome HeIIoPush e o pacote
br.com.1ivroandroid.he11opush.

Ateno' eu criei o projeto He1loPush com o pacote br.com.1vroandrod.hellopush.


Caso voc crie o seu pr0J eto com outro pacote, tenha muita ateno e leia
cuidadosamente as prximas instrues, pois existem conguraes feitas no
arquivo de manifesto que levam o nome do Pacote

P ' ' meme necessrio declarar a dependncia do Google Play Services no


anlaitaapp/build gmdle pois, assim como a API de mapas, o GCM tambm faz
parte do Google Play ServiCS~

app/buiId.gradIe
' ' ' . , _ ' :7.0.0'
Compile .comg00g1e_andro1d.gms.play services
74 oaagiennaria-4-ediao
28.8 (Iasse GoogIe(IoudMessaging
No Codigo do aplicativo, para obter o reglstratlon Id, basta utilizar o mtodo
f9Ster(projectNunber) da classe Googleloudessaging do Google Play Services:
// Registrando o dispositivo no GCH
Googletloudessaging gcm = Googletloudnessaging.getInstance(context);
String registrationld = gcn.register(Project Number Aqui);

O metodo register(projectNunber) recebe corno parmetro o Project Number, que, relem


brando, o cdigo do projeto que foi configurado na pgina do console do Google.
Mas para cncapsular a classe do Googletloudessaging, a m de tornar o cdigo mais
simples. criei a classe GCM, a qual voc pode reutilizar em todos os seus projetos.

ff!-S G(M.java

public class GCH {


private static nal String TAG = gcm";
public static nal String PROPERTY_REG_ID = "registration_id";
// Preferncias para salvar o registration id
private static SharedPreferences getGCMPreferences(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences("livroandroid_gcn",
Context.HODE_PRIVATE);
return sharedPreferences;
}

// Retorna o registration id salvo nas prefs


public static String getRegistrationId(Context context) {
nal SharedPreferences prefs = getGCHPreferences(context);
String registrationld = prefs.getString(PROPERTY_REG_ID, "");
if (registrationld == null II registrationId.trim().length() == 0) {
return null;
}

return registrationld;
}

I/ Salva o registration id nas prefs


private static void saveRegistrationId(Context context, String reglstrationld) {
nal SharedPreferences prefs = getGCHPreferences(context); '
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, registrationld);
editor.conmit();
1

// Faz o registro no GCH


public static String register(Context context, String projectNunber) {
Googletloudessaglng gcn = GoogleCloudHessaginq.getInstance(contet);
Captulo 28 n GCM - Google Cloud Messa
try {
Qio 741
s'--_.
L9-d(T^G, ">> GCM.re
gistraroi " + DrojectNunber);
tring regiStrat10Id - Qcm.register(projectNunber);
1f(re9istrtton1d az null) {
// Salva nas prefs

} saveRegistrationId(context,registrationld);
L09-d(TAG, "<< GcM.re
QSFHFO OK, registration id: " + registrationld);
return registrationld,
} catch (IOEception e) {
L0g.e(TAG, "<< GCM.registrar() ERRO: " + e.getMessage(), e);
}

return null;
}

// Cancelar o registro no GCM


public static void unregister(Contet context) {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
try {
gcm.unregister();
saveRegistrationId(context, null);
Log.d(TAG, "GCM cancelado com sucesso.");
} catch (IOEception e) {
Log.e(TAG, "GCM erro ao desregistrar: " + e.getMessa9(), G);
}

az _ _ , ,
}

A seguir temos a explicao dos mtodos da classe GCM.

public static String register(Contet context, String projectNumber)


F o re istro no GCM e retorna o registration id. Este mtodo precisa ser
c1-1
ama di dentro de uma thread. Depois de obter o registration id, ele e salvo
au tom
aticamente nas preferncias com a classe SharedPreferences.

public static void unr95te"(Ctet context)


' GCM
Cancela o registro nO e limpa' ferncias.
as pre Este mtodo precisa
ser chamado dentro de uma thread
. - t t ontext)
Dublic static Strin9 9@tRe915tratwnId(C0n ex C
' t fa
Retorna O W915 t'on
1 id salvo nas preferncias. Se o registration id estiver
' ifica uelicativo
O est
3Pregistrado
. ~ no, .GCM. Se o retorno for
salvo, Slgll
nulo, Slgl Casue O dispgsitivo ainda nao esta registrado.
" Googlehndioid-indcio
A 5'~'iUIr. vamos ver alguns exemplos de cdigo de como utilizar esta classe. Para
0 registro no GCM, faramos assim:
/ Fil 0 registro no GCM
5ff09 f09StrationId = GCH.register(getContext(), 'Project Nunber AQUI');

Para verificar se o dispositivo j est registrado, basta chamar o metodo


getRegistrationId(contet). Se o retorno for nulo, signica que 0 dispositivo ainda
no est registrado.
// Recupera o registration id
nal String registrationld = GCH.getRegistrationId(this);

E. por ltimo, para cancelar o registro, basta fazer:


// Faz o registro no GCM
GCH.unregister(getContet());

Utilizar a classe GCM simples. Note que necessrio informar o Project Number para
fazer o registro Portanto, crie no projeto uma interface ou classe abstrata chamada
Constants com a constante PROJECT_NUHBER.

iri Constantsjava
package br.livro android.cap28.push;
public interface Constants {
// Project Number criado na pgina do Google Developers Console
string PRoJtcT_uu-een = "999S590S2058";
}

costume deixar o Projectllumber em uma constante, pois vamos utiliz-lo em outros


lugares. Sendo assim, futuramente no cdigo poderemos utilizar essa constante
para fazer o registro.
/I Faz o registro no GCH
String registrationld = GCH.register(getContet(), Constants.PROJECT_NUHBER);

28.9 Configurando o projeto Android


Agora vamos para a pior parte, que oongurar o arquivo dc manifesto Como a con
gurao meio trabalhosa. recomendo que voc oopie o arquivo AndmidManirsLxml
dos exemplos do livro para no oorrer o risco de digitar algo errado
Caso tenha utilizado um nome de pacote diferente de br .con. livroandroidmellopush.
substitua o pacote nos lugares indicados. O c1'digo-fonte a seguir est comentado.
portanto leia atentamente o significado de cada configurao:
I.- .
Captulo 28 n GCM - Google Cloud Messaging

AndroidManifest.xmI
<?1 V@FS0="1.0" encodin9="utf-8"?>
<af@5 PCkage="br.com.livroandroid.hellopush" , , _>
<. Para receber a mensagem precisa de internet -->
<uses-permission android:name="android.permission.INTERNET" />

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


<. Permissao utilizada para travar a tela, e evitar o modo de espera. -->
'__ . ~ ' 1 o
<uses-permission android:name="android.permission.wAKE_LOCK" />
<. Permissao customizada necessaria para receber as mensagens.
Ela precisa ser chamada PACOTE.permission.C2D_MESSAGE -->
<
permission android:name="br.com.livroandroid.hellopush.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="br.com.livroandroid.hellopush.permission.C2D_MESSAGE" /

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />


<application . . . >

_
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity android:name=".MainActivity" . . .>

GcmIntentService, que esta declarado mais abaix . H


<recever andrdname:".GCHBFOBCBSRECEIVEF
androd.perm55i0n="com.google.android.c2dm.permission.SEND >
<intent-lter>
!-- F'ltrar as aes para receber mensagens. --> H
<ac
<cai
< t'oh android'name="com.google.android.c2dm.intent.RECEIVE />
tegory androd.name="br.com.livroandroid.hellopush" />
</intent-lter>
, ' ,r_u
acima.
. cl
<service andro1d:name- .GcmIn

No manifesto, declaramO5 21
receiver chamado GcmBr08
dcas
a 55 MainActivity como de costume, mais um broadcast
tpecever e um service chamado GcmIntentService.
7" coqtznemu-4-mu
^ ***R""'~ Psllios ver o codigo do broadcast receiver que tem como ohieti
vo interceptar as mensagens de push. Iisse receiver esta interceptanclo a ao
WH-99le.androld.c2dn.tntent.RECEIVE. a qual e enviada por broadcast pelo sistema
quando chega utna nova mensageni de push.

ifri GcmBroadcastRece|ver.]ava

public class Gcnroadcastkecetver extends HakefulBroadcastRecelver {


prtvate static nal Strlng TAG = gcn;
00verrlde
publlc void onReceive(Contet context, Intent intent) {
L09~\(TA6. 'GcnBroadcastRecelver.onRecelve: " + lntent.getEtras());
// Inicia o service con HAKE LOCK.
ConponentNane conp = new ConponentNane(contet.getPackageNane(),
GcnIntent$ervlce.cless.9etNane());
startHakefulServtce(context, (intent.setConponent(conp)));
setResultCode(Actlvtty.RESULT_0K);
}

Repare que esse receiver herda de NakefulBroadcastRecelver. um tipo especial de


receiver que permite hloquear o processador (wake lock) para ter certeza de que
a aplicao vai tratar a mensagem de push. justamente por isso. colocamos a
permisso HAKE_LOCK no arquivo de manifesto Depois de interceptar a mensagem.
o receiver inicia um servico em segundo plano para trata-la. isso e leito com
o metodo startHakefulService(context, intent). no qual a intent refere-se a classe
GcnIntentServlce. que e o servico declarado no flIl(lf()l(lM1Illft'$..\ml.
servlce androtd:nane=".GcnIntentServlce" /

A classe Gci-iIntentServlce e quem vai definitivamente tratar a mensagem. e o motivo


pelo qual a utilizamos porque servios podem executar por um longo periodo
de tempo. enquanto ieceivers precisam executar rapidamente. Apenas para relem-~
hrar. esta e uma solucao comum em aplicaoes Android: utilizar o receiver para
interceptar as intents e delegar o trabalho para utu servico
A seguir. podemos visualizar o codigo-Ionte da classe GcnIntentServlce. Ela e lilha
da classe IntentServlce que estudamos no capitulo 27. sohre servios.

Un GcmIntentService.java

public class GcnIntentServtce extends lntentServlce {


private static nal String TAG = "9cn":
public GcnIntentSer`vice() (
CaPU|0 28 I GCM - Google Cloud Messaging

SUner(constants.PRoJEcT_NuMaEn);
}

@Override

protected void onHandleIntent(Intent intent) {


Bundle extras = intent.getEtras();
L0g.i(TAG, "GcmIntentService.o
nHandleIntent: " + extras);
GoogleCloudMessaging gem z 50091951 dm
ou essaging.getInstance(this);
// Verica o tipo da mensagem
String messageType =9Cm, tp
ge essageType(intent);
if (!extras.isEmpty()) {
// O etras.isEmpty() precisa ser chamado para ler o Bundle
// Verica o tipo da mensagem, no futuro podemos ter mais tipos
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
// Erro
onError(extras);
} else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// Mensagem do tipo normal. Faz a leitura do parmetro "msg"
// enviado pelo servidor
onMessage(etras);
}

// Libera o wake lock, que foi bloqueado pela classe


// "GcmBroadcastReceiver".
GcmBroadcastReceiver.completewakefulIntent(intent);
}

private void onError(Bundle extras) {


L0g,d(TAG, "Erroz " + etras.toString());
}

private void onMessage(Bundle extras) { _


// L a mensagem e mostra uma noticaao

n_
String msg = extras.getString("S9");
L .d TAG, msg); _ _
i.''
Ioen ntent _ new Intent(this, MainActivity.class);
ntent.DUfEtr("mS9" 59)5
Notication t.1 create(th5 1 intent,R.drawable.ic_notication_icon,"Nova
mensagem",mS9);
}

s
Importante: o construtor da das e GcmIntentService recebe como parmetre a
PR _ '
Constante Constants. OJEU NUMBER que 0 Project Number que copiamos na paglna
deconaedO(300gk'
74 aoogiznnami-4~zar1
No mtodo onHand1eIntent(intent) da classe GcmIntentService estamos lendo a mensa
80111 d push que chega via a intent. O cdigo est verificando o tipo da mensagem
6 chamando os mtodos onError(bund1e), no caso de erro, ou onHessage(bundle), no
Caso de sucesso. Atente-se para os tipos das mensagens, pois no futuro o GCM
pode ser expandido e novos tipos podero surgir.
Um fator importante sobre a lgica do cdigo-fonte que no final do
mtodo onHand1eIntent(ntent) e chamado o mtodo GcmBroadcastRecetver.
completewakefullntent(intent). Isso feito para liberar o bloqueio do processador
(wale lock), o qual foi travado no passo anterior pelo receiver GcmBroadcastReceiver.
No mtodo onHessage(bund1e) recebe-se a mensagem. Neste caso estamos lendo um
parmetro "msg" do tipo String, pois o servidor que vamos criar vai enviar uma
mensagem exatamente com esse parmetro. Logo depois de ler a mensagem, o
aplicativo vai mostrar uma noticao para o usurio. Ao clicar na noticao, a
ManActvity ser aberta para ler a mensagem.
private void onHessage(Bund1e extras) {
I/ L a mensagem e mostra uma notcao
String msg = etras.getStrng("msg");
Log.d(TAG, msg);
Intent intent = new Intent(this, HanActivty.c1ass);
ntent.putEtra("msg", msg);
Notcatontil.create(this,1,intent,R.drawable.ic_notcaton_con,
Nova mensagem", msg);
1

28.10 Criando a activity para fazer o registro no GCM


Estamos quase terminando o cdigo do projeto Android, falta apenas a activity
No arquivo de layout, vamos inserir dois botes na tela, Registrar e Cancelar. O boto
Registrar vai fazer o registro no GCM e obter o registration id do dispositivo. O boto
(ancelar obviamente vai cancelar o registro. Na tela, tambm vamos inserir um
Textvew para mostrar as mensagens que vamos receber por push.

f /res/layout/activity_main.xml
LnearLayout mlns:androtd="http://schemas.androd.con/apk/res/android"
android:Iayout_width="match_parent" androtd:1ayout_hetght="match_parent"
android:padding="16dp" androtd:orentaton='vertica1>
LinearLayout android:orientation="vertca1"
android : i.ayoutwdth="match_parent " android : layout_hetght="urap_content"
Captulo 28 n GCM - Google (loud Mes
<Button
Soio 747
androidzl ' :H H ,
andrd_t:k\th WcD_content android:layout_height="wrap_content"
(Button - 91Strar android:onClick="onClickRegistrar" />

an:r?d:layou-Wdth="WfD_C0tent" android:layout_height="wrap_content"
. an roid.text= Cancelar" android:onClick="onClickCancelar" />

<TetView android:id="@+id/text"
android: y0Ut_F9"T0D="20dp" android:tet="Clique em registrar."
3"dfd313Y0Ut_Wdth="wrap_content" android:layout_height="wrap_content" />

Com o layout pronto, estamos a um passo de terminar o exemplo. Falta apenas


o cdigo da activity; que podemos visualizar a seguir.

MainActivity.java
public class MainActivity extends AppCompatActivity {
private static nal String TAG = "livroandroid";
@0verride
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Verica se o Google Play Services est instalado
boolean ok = checkPlayServices();
if (ok) {
// J est registrado
String regld = GCM.getRegistrationId(this);
setText(regId);
/ Quando iniciar, l a msg da noticao
String msg = getIntent().getStrin9EfFH("S9");
setText(mS9);
}

@0verride _
}

ama u ,,"
1 tent(intent);
protected void onNewIntent(Intent intent) {

/upr.o2:e:onC1Car na notcao 59 a activty Ja estava aberta


/ 'dDevi oa
` con9urao android:launchMode= singleTOD

rin ~ '
// L a msg da notica0
St ` 9 ms9 ' intent 9etStrin9Etfa("S9");
setTet(l'lS9)3
}
private Context 9etCtet() {
mounu-qu
Fllf thlg
I
ll Nostra nag dn qqqg gg f|\
hflvt vold x0tTent(nal Strln ) {
llla Q null) {
fvnlibroidlu Runnabloli {
O veffldo
public void runl) {
Textvlou text E (fextvlou) ndVlouiyld(R.ld.tont)z
\oxt.stTet(s):
log.d(IAG.s):
)
}):
l
I
/I Fac o reqlslro no GCM
publlc vold onCllckRo9latrar(vlou vlow) {
nen Throod() {
Qverrlde
ou llc vota run() {
supef.run();
String rogld Gt.QotoglstrtlonId(9etontet());
lf (regld 35 null) {
ll Faz o roqlstro Q pego o reglslratlon ld
fegld = QflFQQSOf(QGCOK(). Constants.PROJECT,NUN!ER):
set\ext(Reglstrado con sucesso.\n + rQgId);
) else {
tost('voc j est reglstrdo\):
1

I
).stort():
l
/1 fnclo o reqlslro no Gta
pub\\ vol! onCl\k|ncolr(V\|u vlou) (
nem IhrQd() l
0vorldQ
ubllc vold run() {
mw \run( )
0N~UlfI\\\f(|('\\\())l
gQ1ot(Concoldo con suso1')
l
}.xlar():
)
(aPtu|o 28 n GCM - Google (loud Messaging 749

// Verica se o Google Play Services est instalad o


private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
int PLAY_SERVICES_RESOLUTION_REQUEST = 90G0

GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
toast("Este dispositivo no suportaoog
o Ge lPl ay Services.);
nish();
}

return false;
}

return true;
}

private void toast(nal String s) {


run0nUiThread(new Runnable() {
@0verride
public void run() {
Toast nakeTet(getContet(), "Toast: " + S, TOHS-LENGTH_5H0RT)-Sh0W();
}

});
}

Como a- 'MainActivity
' de ser aberta por
pOuma
.. . ~ ~ ura ~ HO1
ao.
adicione o atributo
'cao
android:launchMode= S19l@TP asua Con g
. . - .android.name . - - I-
z" MainActivity ' - hMode="singleT0p"
android.launc . . . />

,, -3- me , .
<activity

ao sera A
na notl
~ e o Android crie apenas uma ins
elembrand0, 6553 Cogufaao faz com qu - ~ tivit aberta
Apenas r . . d ue se o usurio clicar na noticaao com a ac Y _
tncia da act1v1t} Sen O q ,tcdo 0nNew1ntent(intent) ser chamado. Sem esse atributo,
` . reutilizada
elaclicar caoa e Sern
O pre uma nova activity seria criada e colocada na pilha.

. . se ' registra ..
A _ , Sabe como eleve ser feito o cdigo-fonte do aplicatiV0
E isso e tudo! Agora voce J Sa eng de push ge vge fez tudo passo a passo, execute
Android para receber as rnenr o GCM_ Depois, com o registration id do dispositivo
o a llC8t1V0 Para
m
p a mensagem por push utilizando o cdigo Java que
em maos, P0
j_n1ostran1O5
\

azgienumi-4-aim

0l<l= HH cdigo da activity. estamos vericando se o Google Play Services est


llellmlu no dispositivo. (Iaso no esteja. no 6 possivel fazer o registro no
(ICM. Nesse caso, para simplificar o exemplo. estamos chamando o mtodo
lshi) el lim de fechar o aplicativo. Outro dctillie importante no cdigo 6 que
estamos utilizando threads (ou async task) para se comunicar cont o servidor do
(K IM. lsso e necessrio. Eu utilizo threads simples nos exemplos para facilitar a
leitura. Mas na przitica costumo utilizar o mtodo startTask(task). que encipsuln
tt Asynclask. conforme fizemos no projeto dos carros.

28.11 Links teis

Neste captulo. estudamos o GCM e aprendemos a enviar mensagens de push


para o aplicativo. Existem muitas particularidades deste servio que voc precis i
conhecer. por isso. nada melhor do que nalizar seus estudos lendo a documcn
tto ocial.
Google Services - Google (loud Messaging

httpss//developemndroid cm/gogIe/gcm/indezx. html


CAPTULO 29
- . ' \E\

p Salvandooestadoda
_,,,,___.

aplicao
Entender o ciclo de vida de uma activity ou fragment um fator decisivo para
construir um aplicativo de sucesso.
Neste captulo, vamos entender quando uma activity pode ser destruda e re
criada, e como o aplicativo deve salvar o estado das informaes. Provavelmente
voc j conhece o conceito, pois j mostramos casos simples sobre como salvar
informaes durante a troca de conguraes de sistema, mas este captulo visa
complementar os seus estudos.
Se voc quiser, pule este captulo. Eu s preciso garantir que voc domine o as
sunto, pois misso dada misso cumprida.

29.1 Troca de conguraes - conguration changes


Quando uma activity criada, o mtodo onCreate(bund1.e) chamado, e na sequncia
todos Os mtodgg do ciclo de vida so chamados, at chegar a vez do, onDestroy(),
d a activity destruda. Sabemos que uma activity ser destruda quando
girfiio pressionar o boto voltar ou o aplicativo chamar o mtodo nsh().
z - fatores que podem fazer com que uma ac tivity seja des
Porem existem OLIIIOS
,d , f uandg ocorre uma troca de congurao do sistema, chamada de
um ai que ehq
configuration A lista a seguir mostre as trocas de configuraes do sistema
c 9@5
mais comuns:
- ' (vertical - ` 1) - acontece ao girar o dispositivo.
e horizonta
_ -
1. Troca de orientao
' ' m teclado.
~ fsico em dispositivos co
2. Abrir o teclado
d A d `d.
3. Troca de idioma nas congura0S 0 U f01

751
752 awgiznamia-4-zuiao \

hsses tres casos so exemplos de troca de con gurao, e, se alguma dessas condi
oes ocorrer, o Android vai encerrar a activity atual e vai recri-la logo em seguida
para que a nova congurao faa efeito.
Veja que at que faz sentido. Vamos dizer que voc tenha um layout para vertical
e horizontal, conforme demonstrado a seguir:
/res/layout/activity_main.xmI
' /res/layout-Iand/activity_main.xml
Neste caso, 0 identicador land de landscape e representa o layout horizontal. O
sistema do Android vai utilizar o layout correto conforme a orientao do dispo
sitivo. Quando a activity criada pela primeira vez na vertical (portrait), o layout
/res/layout/activity_main.xml utilizado. Ento, ao girar a tela para horizontal (landscape), o
Android vai destruir essa activity e recri-la novamente, mas desta vez quando o mtodo
onCreate(bundle) for chamado o layout utilizado ser o /res/layout-land/aaivity_main.xm1.
O Android apresenta esse comportamento porque muitas vezes preciso atualizar o
layout em casos de alteraes nas conguraes do sistema.
Com os fragments, o conceito o mesmo, e o mtodo onCreateView() pode inar uma
view diferente dependendo da orientao. justamente por isso que a activity e
seus fragments so destrudos e recriados, assim voc tem uma chance de recriar
a tela com as configuraes adequadas.
Como desenvolvedor, voc deve estar ciente desse comportamento e conhecer
como salvar o estado da aplicao nesses casos.

29.2 Salvando o estado com o mtodo onSaveInstanceState(bundIe)


Para testar 0 prximo exemplo, abra o projeto deste captulo e execute-o no
emulador. O cdigo parecido com um que j zemos ao estudar fragments,
mas agora vamos relembrar somente a questo do ciclo de vida e o mtodo
onSaveInstanceState(bundle).

iii Fragmentljava
public class Fragnentl extends 0ebugFragnent {
private int count;
@0verride
public View onCreateView(Layoutlnater inater, Viewroup container,
Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragnent_1, container, false);
nal Textview t = (Textview) view.ndViewById(R.id.text);
Captulo 29 n Salvando o estado da aplicao 753
// Recupera o estado
f(savedInstanceState != null) {
COUH = savedInstan
ceState . getInt( "count");
// Atualiza o Textview com o valor salvo
t.setText("Count: " + gount).
}

view.ndViewById(R.id.btOk). to
@Override se nClickListener(new OnClickListener() {

public void onClick(View v) {


count++;
t.setText("Count: " + count);
}
});
return view;
}

@Override

public void onSaveInstanceState(Bundle outState) {

s..
super.onSaveInstanceState(outState);
// Salva o estado
outState.putInt("count", count);
}
}

Nota: a classe Bundle como uma HashMap e salva os objetos por chave e valor. Ela
pode salvar vrios tipos, como Integer, Boolean, String, Serializable, Parcelable etc.

1nser *
E5 e fra ment tem um simples layout com um boto que ca incrementando o
Cntadg Note que este ele lho de DebugFragment. A activity desse fragment apenas
' e O fragment no layout sendo que ela lha de DebugActivity.
Estou fazendo essas heranas P Qrque quero reforar a ordem da chamada dos
mtodos do ciclo de Vlda
Portanto, faa O Seguinte teste:
. - Incrementar cinco vezes.
1. Execute O pf0J 6 clique no botao
, va
2 O lor do contador ser atualizado para 5
. ire
3 G' a tela do dispositivo (110 @mu1df (m+m)`
4. Depois de glfaf
. on
tela oavalor
' do contador deve continuar como 5.
ltado O mtodo onSaveInstanceState(bundle) salvou o
A gllfa 29-1 mostra O resu . tador lido na inicializao do fragment.
estado do apli cativo, 6 0 Valor do C
754 Google Android - 4' edio

Figura 29.1 - Salvando o estado do fragment.

O mais interessante desse exemplo so os logs do LogCat, que imprimem a ordem


correta de execuo dos mtodos da activity e do fragment. Veja que o mtodo
onSaveInstanceState(bundle) chamado sempre antes do onPause(). Os logs a seguir
so impressos ao girar o dispositivo:
// Salvando estado
Fragmentl.onSaveInstanceState()
DemoSa1varEstadoActvty.onSaveInstanceState()
Fragment1.onPause()
DemoSa1varEstadoActivity.onPause()
Fragment1.onStop()
DemoSa1varEstadoActvty.onStop()
Fragmentl.onDestroyVew()
Fragment1.onDestroy()
Fragment1.onDetach()
DemoSa1varEstadoActvty.onDestroy()
// Recriando a activity e seus fragments
Fragment1.onCreate()
DemoSa1varEstadoActtvty.onCreate()
Fragmentl.onActvtyCreated()
Fragment1.onStart()
DemoSatvarEstadoActvty.onStart()
DemoSa1varEstadoActvity.onResume()
Fragment1.onResume()

z\lazer esse teste de~girarzitela.\xc pixle ver ehiraniente cn1us nieudu


JU giln de vida so chamados e como o Android faz para destruir a activity L'
Captulo 29 n Salvando o estado da aplicao 755

recri-la. Saiba que o mtodo onSaveInstane5t t


atualmente recomendado deixar a l ` a eo tambm existe na activitx mas
sois glca
sibilidade de reutilizar o cdigg por esto dentro dos fragments devido pos
Somente com O fragment ll mostrando como salvar o estado

Nota: casoomtodof ' ' Si S S . `


t _ "T1Sh() Seja chamado explicitamente no cdigo da activit
o amos
es mtodo onSaveIn
conscientemente t i n edessa
fechandoatela rol naoc
formaoAamam
d 'd ~ h
I] C1 S ancestatelbundlef (3 nnetocho onSaveInstanceState(bundle) ser
C ama O apenas quando 0COrrer uma troca de congurao de sistema

29.3 Salvando o estado com o mtodo setRetainInstance(booIean)


Salvar os dados do fragment com o mtodo onSaveInstanceState(bundle) simples,
mas voc precisa preencher o Bundle manualmente. Caso voc queira fazer algo
ainda mais simples, possvel manter a instncia do fragment viva durante a troca
de configurao, assim os dados sero preservados automaticamente.
Se o mtodo setRetainInstance(true) for chamado no fragment, algo que geralmente
feito no mtodo onCreate(bundle), a instncia do objeto do fragment ser preser
vada em memria, e apenas a view ser criada novamente.
O cdigo a seguir est atualizado para salvar o estado com o mtodo
setRetainInstance(true).Veja que muito simples. Por sorte, como neste caso a varivel
inteira, ela inicializada com O (zero), sendo assim nem precisamos validar se ela
est inicializada. Ao girar a tela, o estado do contador ser mantido normalmente.

Fragmentl .java
public class Fragmentl extends CicloVidaFragment {

@0verride {
private int count;

public void onCreate(Bundle savedInstanceState)


super.onCreate(savedInstanceState);
// Mantm o fragment vv0
setRetainInstance(true);

@0verride ' ntainer


}

- t , Viewroup C0
public View onCreateView(Layoutlnater l ef
Bundle savedInstanceState) i tamem false);
View view = inater.in ate(R-lYUt-fra9ment'1' Co
75 ooqtzrtnafaia-4~ata
final textviw t n (Textvtn) vtew.ndVtovById(R.td.t|t);
/I 0 atributo count vat permanecer en nondrto
t.||tTeirt(Count: " + count);
vtew.ftndVtevById(R.td.bt0k).set0nC1tckLtstener(nev 0nCltckListener() {
O0verrlde
public votd on(tick(vtew v) {
count++;
t.setTet("Count: " + count);
1

i);
return view;
1

29.4 A importncia de reter a instncia do fragment


At aqui foi uma reviso, pois j estudamos esse assunto no capitulo 8, sobre
iraginents, entao agora vamos complicar um pouco.
listas duas abordagens fazem a mesma coisa, mas o mtodo setRetatnInstance(true)
6 mais simples. pois cm ele no preciso salvar o estado manualmente preenz
cltendo Bundle. pois a instncia do fragment e preservada. Mas existe outra
grande vantagem do mtodo setReta1.nInstance(true), que s quando temos alguma
thread executando para linscar inlormaes.
Vamos imaginar que temos uni aplicativo que busca notcias de tim welo service.
Se o usuario girar o dispositivo, o que vai acontecer? Sabemos que por padrao o
Android vai destruir e recriar a activity, junto coin seus lragiiietits. Sendo assim.
o que acontece coni a thread que estava cxectttando?
Se o desenvolvedor nao controlar isso, a thread vai continuar executando, mas
a activity e lragrnent serao destrudos durante a troca de orientao. llina nova
activity e l'rajtnent sero criados, e voc precisar liuscar os dados no weli service
novamente. Isso significa que, se voc nao projetar seu cdigo de maneira ade
qttada. sempre que voc girar a tela, a threatl/tarefa ser executada novamente,
pois o Android vai recriar a activity
Dentro desse contexto. reter a instancia de um llragnient com mettido
setRetatnInstarice( true) pode ajudar a resolvero prolilenta, pois ao tnantero objeto viv
as th teacls que executam dentro do l'rag_nient tamhein serao preservadas. Iintzio neste
caso voc' pode t'ontrlar sc a thread ja lot iniciada t| at se o rtfstlltatlo j lol lornlo.
e no niaximo sera necessario recriar a view do l'rament no nietotlo onCreateVtew().
, esm ^
. . _ endo cha ^ '
Captulo 29 I Salvando o estado da aplicao 757
Lembre-se de que, durante a t
roca de orientao m
do fragment, o metodo aonCre t ' _ o secontinua
@V1@W() voce reter
s a instancia
precisa recriar a sua view Nesse ^ mado, POIS O fragmem
encontram os dados, e decidir se ' ' ~ - - q
momento voce ode validar o estado em ue se
~ 1_ _ _, c necessario iniciar a busca de web services ou
nao, caso o ap 1cat1vo Ja tenha as informafzs

29.5 Manter uma thread executan


do durante a troca de orientao
No captulo 7, sobre views, estudamos a classe ProgressBar que pode ser utilizada

I``|l, lLi:lll
para mostrar uma animao que ca girando, ou uma barra que pode incrementar
o progresso de uma tarefa. Isso depende do estilo que for utilizado.
O cdigo a seguir mostra como criar um ProgressBar com uma barra horizontal:
<ProgressBarandrod:id="@+d/barraProgresso" style="?androd:attr/progressBarStyleHorzontal"
androd:1ayout_width="match_parent" androd:1ayout_heght="wrap_content"
androd:na="100" androd:progress="0" />

Nota: o prximo exemplo avanado, leia quando achar necessrio.

A gura 29.2 mostra o exemplo que zemos no captul0 7, que apenas inicia uma
thread que ca incrementando um contador de O a 100 e atualizando o valor do
progresso.

1 e -e A t 1
l Barra de ProgreSSO
IITIU
| ______.__.__._.~
S_ l narefa "
SimularTarefa
- p
Barra de Progresso
3 i ---*' .
l

11l l

lI

1_
Figufd 29 2 ~ Thread executando.
758 Goglehlitlritl-*edio
Mas o que acontece se voc girar a tela durante o andamento dessa tarefa? Sabe
IUUS que a activity vai ser destruda e recriada, ento o que acontece com a tluearl
que estava executando?

No existe nada que vai parar a thread durante a troca de orientao; assim. caso
voc queira parar a thread, voc deve interrompe-la no mtodo onPause( ) ou onStop()
do ciclo de vida da activity. Mas neste caso no queremos interromper a thread. e
sim deix-la executando, pois a tarefa precisa continuar independentemente da
troca de orientao.
Portanto, no exemplo do capitulo 7, ao girar a tela. a activity destruda, porm
a thread continua viva e executando. Mas como essa thread est associada com
urna activity que ser destruda, se ela atualizar a view nada vai acontecer. Quan
do a nova activity for criada, o Progressar apenas vai mostrar o ltimo valor que
recebeu a atualizao, pois as views do Android internamente tambm salvam
seu prprio estado. Porm, a thread se perde no meio do caminho, e o problema
que ela est associada com uma activity que j no est mais sendo mostrada
ao usurio. Inclusive isso um problema, pois devido a essa referncia a activity
antiga no pode ser eliminada de memria pelo garbage Collector e tivemos um
memory leak. Mas esse um assunto mais avanado, e vamos deixar pra l.
O que ns queremos deixar a thread viva, mas, assim que a nova activity e view
forem criadas, queremos atualizar o progresso da tarefa, como se nada tivesse
acontecido. Tudo deve funcionar perfeitamente, mesmo se o usurio car brin
cando de girar o celular.
O modo indicado de solucionar esse problema utilizar um fragment e reter a ins
tncia com o mtodo setRetainInstance(true). Feito isso, a thread dentro do fragmenr
pode continuar executando sem problemas, e assim que a view do fragment for
criada depois da troca de orientao podemos atualizar o status do progresso.
A thread permanece viva, e apenas o fragment vai mostrar urna view diferente.
O cdigo a seguir mostra a soluo para esse problema, e com ele vamos conseguir
girar a tela sem parar a tarefa e a atualizao do progresso.

r: ProgressBarDemoActivity.java

public class ProgressBarDenoActlvity extends App(ompatActlvlty {


@0verride
protected void on(reate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentVlew(R.layout.actlvlty_progress_bar_deno);
tf(savedInstanceState == null) {
getSupportFragnentHanager().beginIransactlon().add(R.id.layoutFraq.
759
CaPtu|o 29 n Salvando o estado da aplicao

} new ProgressBarDemoFragment()).commit().
}

/WS/|aY0U/CVity_progress_bar_demo.xmI

<FV3meLy0U XNlS2dr0id="http://schemas.android.com/apk/res/android"
mlns:tools="htt p://schemas.android.com/tools"
andmd113)/0Ut_Wdh="P1Ch_D8I'ent" android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
a"dfd=d="@+id/layoutFrag">

Como voce pode ver, a activity no faz nada, e mais uma vez vamos delegar a
lglca para o fragment.

ProgressBarDemoFragment.java
public class ProgressBarDemoFragment extends Fragment {
private int progress;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // Deixa o fragment em memria
}
@Override
public View onCreateView(Layoutlnater inater, @Nullable Viewroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_progress_bar_demo, container, false);
Button b z (Button) view.ndViewById(R.id.bt0K);
b.set0nClickListener(new Button.0nClickListener() {
@Override
public void onClick(V@W view) i
executarTarefa();

0.
}});
return view;
}
private void executarTarefa() i
new Thread(new Runnable() i
public void run() i I _
/ A varivel Df9"e55 ca em memoria
ll O f r Comea em 0 na primeira vez ou continua de onde parou
for (int i = PF9f555 <= 100; 1++) {
I/ Salva o estado do int progress
progress = i;
updateProgressBar();
try {
Thread.sleep(56);
} catch (InterruptedEception e) {
}

// Ao terminar a tarefa zera a varivel do progresso


progress = 0;
Log.d("livroandroid","Fim");
1

}).start();
}

// Atualiza a barra de progresso


private void updateProgressBar() {
// Somente atualiza a view se o fragment est vivo e vinculado a uma activity
if(getView() != null && !isDetached()) {
getView().post(new Runnable() {
public void run() {
// Barra de Progresso
nal ProgressBar progressBar = (ProgressBar)
getView().ndViewById(R.id.barraProgresso);
Log.d("livroandroid", "> Progress: " + progress);
progressBar.setProgress(progress);
}

});
}

O arquivo de layout do fragment tem o mesmo layout do exemplo do ProgressBar


que fizemos no captulo 7, portanto verique nos exemplos do livro. O layout
apenas contm o ProgressBar e o boto para iniciar a tarefa.
Se voc executar esse exemplo e girar a tela, ver que a tarefa continuar execu
tando, pois o fragment foi retido em memria, de forma que a thread permaneceu
viva e associada com o fragment. A varivel inteira progress cou em memria,
para continuar a simulao da tarefa de onde parou.
O primeiro segredo do exemplo o loop d for no comear em i=6, e sim em
zprogress, pois queremos continuar a atualizao do ProgressBar de onde parou
antes da rotao da tela, e a varivel progress vai manter o seu estado.
CaPtuIo 29 n Salvando o estado da aplicao 751
segundo segfedo do exemplo tomar cuidado ' '
ara na
fragment Ca 1 _ _ I i p o atualizar
so e e e a view do
um mome _ o, vai chegar
o fragment
activity e
nto emcara
soc1ado
_ gment que
da a'nula
view
sero
Steja com um estadodestrudos
,dofra
` - ois
invahdo.
' g ment
oPfra a desas
ser '
durde 'orienta
ante aLembre-se
troca de que, como
` a

de condi 1 (1 - _
So ch ama os.Isso acontece depois que os metodos onDestroyView( ) e onDetach( )
ctivity
. ~ Mas como a thread contmua executando, temos de fazer um teste
o e va 1 ar se a view do fragment existe e se o fragment est associado
com a activity Portanto, adicionei esta condio no cdigo:
f(9@VeW() != null && !isDetached()) {
getView().post(new Runnable() {
// O fragment est ok, podemos atualizar a view
});
}

Se voce entendeu o que aconteceu, timo, pois esse um dos grandes benefcios
de se reter a instncia de um fragment. Se voc no entendeu neste momento, sem
problemas, mas quando achar necessrio revise este exerccio, pois com certeza
isso vai lhe ser muito til algum dia.

Nota: o mtodo geView().post(runnable) utiliza o handler que est dentro da view


para executar este cdigo na UI Thread. Outro atalho bem conhecido o mtodo
runOnUIThread(runnable) da classe Activity. Ambas as classes Activity e View esto
associadas com um handler, que por sua vez est associado com a UI Thread e
gerencia uma la de mensagens. Lembre-se de que voc no pode atualizar a view
dentro uma thread diferente da UI Thread, por isso esses mtodos so necessrios.

29.6 Como bloquear a troca de orientao


Muitos aplicativos para smartphone optam por travar a orientaao na vertical, o
que era uma prtica comum antigamente, mas atualmente isso e desencorajado
pelas boas prticas do Google.
Mas em todo o caso para travar a orientao da activity na vertical, utilize o
3

atributo android:screen0rientation no manifesto.


Qctvty and|.Od.name="_ClasseActivityAqui" android:screen0rientation="portrait" />

or p ,
P adro se voc no fizer isso, o Android vai permitir as orientaes vertical e
horizontal. Para forar que U ma activity que apenas na horizontal, basta utilizar
a constante landscape
<ac tvi ~ ' 1.35seActivityAqui" android:screen0rientation="landscape" />
t. .ty and|_od.name-"
762 wgiznmia-4-ediao
Nota: atualmente, segundo as boas prticas do Google, recomendvel deixar o
aplicativo girar a vontade, pois o usurio deve poder escolher qual a orientao
que ele prefere ao utilizar o aparelho. Lembre-se de que o usurio quem manda
no celular dele, e no voc.

Outra maneira de forar a orientao de uma activity utilizar o mtodo


setRequested0rientation(orientation) da classe Activity dinamicamente no cdigo; porm,
e recomendado que essas configuraes estejam declaradas no arquivo de manifesto.
Na maioria das vezes, o fato de o usurio girar a tela no deve ser um problema
para voc; basta salvar o estado das informaes e pronto. s vezes ser preciso
customizar telas na horizontal, como por exemplo: uma galeria de fotos ou um
grco. Nesse caso, vale a pena criar dois layouts para melhorar a experincia do
usurio na horizontal. Lembre-se de que para isso basta inserir o layout na pasta
/res/layout-land, e o Android se encarrega de tudo.

29.7 Dispositivos com teclado


Smartphones com teclado fsico como o Motorola Milestone tambm sofrem
do problema da troca de congurao, que neste caso pode ser a abertura ou
fechamento do teclado fsico.

Sempre que o usurio abrir ou fechar o teclado, a activity ser destruda e recriada
da mesma forma que ao girar a tela. Ento tudo que voc aprendeu sobre salvar
o estado da aplicao tambm se aplica neste caso.
Lembro-me at hoje de quando fui fazer uma demonstrao na Motorola e meu
aplicativo travou quando o gerente abriu o teclado do seu Milestone. Na poca
eu no conhecia esse comportamento e tambm no tinha me precupad em
salvar corretamente o estado da tela.

29.8 Congurao android:cong(hanges e o mtodo onConguration(hanged


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, isso possvel. Existe a possibilidade de controlar essas alteraes de con
figuraes de sistema manualmente, e sem a necessidade de destruir a activity.
e
IO' sa
ess
V ente_,..,_- ,,
var' . .
Mas neste caso ser

' ^ f
e conse u
~ I 1
(aPtuIo 29 u Salvando o estado da aplicao 763
manualment .A Y i
emos os responsveis or oalta 1out e atualiz-lo tudo
p erar
antagem de ter 0 controle total e que a activity nao e destruida
Sf Ci mente os objetos permanecem em memoria. Portanto, nao e neces
f os dados ou busca-los novamente. Parece uma boa 1de1a, nao e? Se
e or o seu obietivo, voce teria de ter u ma conversa mais ou menos assim com
o nosso simptico sistemapoaciona
er ~` 1odrobozinho verde:
Desenvolvedor: Fala, Android, como est?

Android: Opa, tranquilo, 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, ol? W..

E depois de alguns segundos o Android ainda tenta aconselha-lo, sendo sua


ltima chance de voltar atrs.
Android: Espero que voc saiba o que est fazendo. Tem certeza?
E voc responde conantemente:
Desenvolvedor: Sim, deixa comigo! Obrigado.
Pronto, est feito o estrago!
Brincadeiras parte, controlar a troca de orientao ou outras alteraes de sis
tema manualmente pode ser bom ou ruim, dependendo do caso. Mostraremos
aqui como fazer e caber a voc decidir quando utilizar.
Eu particularmente no recomendo fazer isso, a no ser que um dia voc encontre
um bom motivo para faz-IO!
Primeiramente, necessrio adicionar o parmetro androd:congChanges="orentaton"
dentro da tag <aCtvty> para informar ao Android que vamos controlar tudo manu
almente. Dessa forma, todas as vezes que houver uma alteraao nas configuraoes
de Sistema, 0 mtodo onConfiguratonChanged(configuration) sera chamado na activity

Existem de Conguraao
eventos
~ `torados mas es
para aV5-l0 qual configurao foi alterada.
_ , . varios que podem ser moni ,
- 350 mais
tamos interessados nO C comum,
~ ' 0 OFW"
taton
que ocorre ao trocar a
orientao do celular. Neste Ca 50, voc pode congurar a activity com o parmetro
androd:configChan9@5="e"taun '
' ' "' droid:congChanges="orentaton"
. . - . =" C1asseAct1v1tyAqU1 Em />

quanto do 6
sa'
<act1v1.ty andro1d.name .

Caso voce^queira
' brescrever
do
SO
do
O C0mP0f tamento tanto da troca de orientao
teclado isso poderia ser feito assim:
*9f\1V\\Y ndroid:n0o~".tlass|ActtvttvAqut"
"d'\df""9Ch|ng|s~"ortontatton|keyboardutdden " /
Feitiu twi, vuee eleve suhreserever u metudu on(onqur|ttonChan|d(conguratton) mt
au tivuy Se vuee der uma rztpidzi uiizilismiu mt classe Configuration. ver que 6 pusslvel
dere|1t'ir tt ettigttrzi; que muduu: se fui ai riet1ttu;u. idiumn (lueale) uu u
estudu du teeludu litftr (ubertu uu ecitatdu).
0()vr r ide

public votd on(ongurattonChanged(Conguratton newCong) {


SUOor.ontonqurationChnnged(nnw(ong);
// Mudou alguma congurabo de ststena, faa sua lgica aqui
if (CfQ.0f\OOtl0 sw Conguratton.0RIENTATION_PORTRAIT) {
tog.t(TAG. "Trocou para Verttcal");
} else if (cf9.ortentatton =~ Conguratton.0RIENTATION_LANDSCAPE) {
Lo9.t(TAG, "trocou para Hortzonta1");
1

O prulmletnt e que uguru tudu u eumpurtumentu de que u sistema du /\t'tdruid


cuidava autumntieumente uguru 6 suu respnsubiIidude. Se precisar ter um Iayuut
tiilereueiatdu entre verticui e Iiurizuntnl, voc mesmo Ser rcs|'mst'tvel pur fazer
.ilguum nuigiei nt|t1i.pis mtuduonCreate(bund1e) uu ser ehumudu, me purque
n uetivity permzmece vivu e uudu ueunteceu.
l';u'u ter uma ideia du ttttmtttlur du pruhlemu. se vue tiver uma telu curti um Edtttext
e girar at telu. u /\udruid sempre stlvu u estudu deste Edtext. assim eumu ele sulvn
u estndu de tudus us sutis views. Mas se vue sulwreserever esse eumptwtmueutu
u estndu mtu ser mais sulvu. puis tguru issu respuusulwiIidztde suu. Purtmtu.
uu suhreserever esse cm|urtuueutu, u Audruid pztssu u respuusulwilidade de
uttmlizur it telu e demais cu|rtitrrue`es para u desenvulvedur. e issu mtu hum
nar mtiurtzt dus eusus.

Nu vuu ttlur maus muitu dissu uqui. puis seguutlu us iwuus pr'tticts du /\ttdruid
HU|`(``()i`l1(`II(|.ItiH$Hi3I`(`S(`l'(`VL`|'L`S*;\'~ L`lI|1i).',ll!tI(`)t'5.C este breve t'pic lui upettts
ptrt tlitfl t lu suhre tssu.
Istuu explicuudu issu put-t|ue e eumutu eueuutt~'ur sites, lilugs etc. expliemdu
esse nssuutu. purtzmtu ieu aqui miuhn dteu ptru vue evttur uu mtximu uti|tr.:u~
esse reeursu, it mtu ser que u|u,utu diu vue tenhu um hum mutivu. e sztilm u que
estt iu/emlu.
Reeumeudu s;i|v.tr u cstndu du urrliczttivu utiiirztudu us metudus
onSavetnstaneeState(bundle)tfsetRetatnlnstauc(true) dttwuuietu.Issj hitexrdteuf
du e euuiurme vimus mtu e ttu dtiteil ussitu.
a ap icao 755
Captulo 29 n Salvando o estado d I

29.9
Salvando o estado no projeto dos carros

aberta, ver I' ' ~ - -


No projeto dos carros, se voc girar o dispositivo com a tela da lista de carros
qllfi 8 lS[ SI`2l I'C2lI`I`g2lC.l8., POIS 8 QCUVIIY fO1 I`CI`8.d2l, pOI`[2ll"ltO O
Wb SI`VlC executou I'1OV2IITlI'lt.

Enm, este breve tpico foi apenas para lembr-lo disso, j que falamos sobre
salvar o estado da aplicao. Mas salvar o estado no projeto dos carros vou deixar
para voc, caso queira fazer algum exerccio.

29.10 Links teis

Neste captulo, aprendemos detalhes importantes sobre como salvar o estado da


aplicao. Segue um link da documentao ocial para voc complementar seus
estudos.
Handling Runtime Changes

http://developer android.com/guide/topics/resources/runtime-changes.html
` cAPruLo 30
Suportando diferentes
i""~, tamanhos de telas

Neste captulo, vamos aprender os conceitos necessrios para criar aplicativos que
funcionem em diversas telas e resolues. Voc aprender o conceito de densidade
e a notao dp (density-independent pixels).
Quando estava escrevendo o livro, quei na dvida se colocava este captulo no incio,
logo depois de explicar o que uma vievug ou depois como estou fazendo agora. Ao
refletir um pouco, cheguei concluso de que existem assuntos que so delicados
de explicar, e melhor voc ler quando j tiver certo domnio da plataforma.
Este captulo vai lhe tirar muitas dvidas, ou vai deix-lo com muitas dvidas.
Tudo vai depender se voc est pronto para l-lo, ou no. Em minha opinio,
entender o assunto deste captulo um grande diferencial para qualquer desen
volvedor Android.

30.1 Unidades de medida


Frequentemente, ao definir um arquivo de layout, so utilizados os valores
match_parent e wrap_content para denir a largura e a altura de um componente.
Tambm possvel informar um valor numrico com uma notao indicando a
dimenso, como por exemplo 1Odp. Dessa forma, possvel controlar exatamente
o tamanho que o componente ocupar.
Para denir dimenses no Android, possvel usar as seguintes opes:
Opg Descrio
px (pixels) Nmero de pixels da tela. Nunca utilize a notao px.
in (polegadas) Baseado no tamanho fsico da tela.
mm (milmetros) Baseado no tamanho fsico da tela.

766
Captulo 30 . Su on d 767
P an odiferentestamanhosdetelas
QPO Descrio (cont.)
Dt (pontos) 1/72 de uma polegada, baseado no tamanho fsico da tela.
dv (density-independent pixels) Essa unidade relativa densidade da
tela. Recomenda-se seu uso para representar tamanhos de largura
e altura das views. O compilador aceita os valores dp e dip'
SD (Scale-independent pixels) Idem ao dp, mas tambm considera o
tamanho da fonte que o usurio est utilizando. recomendado
utilizar essa unidade para especicar o tamanho de uma fonte, para
que ela seia automaticamente ajustada conforme a tela do dispositivo.

Nota: nunca utilize a notao px de pixels, pois, dependendo da resoluo da


tela, ser apresentado um tamanho diferente. Sempre utilize a notao dip ou
dp. Para tamanhos de fonte, utilize a notao sp.

30.2 Tamanho de tela (screen size)


A medida screen size refere-se ao tamanho fsico da tela, medida na diagonal. A
plataforma separa os tamanhos fsicos de tela em quatro diferentes categorias:
pgquena (small), normal (normal), grande (large) e extragrande (xlarge).
Para deixar mais clara a comparao de cada nomenclatura com aparelhos reais,
veremos a seguir uma lista que exibe cada categoria e alguns celulares que se
enquadram nela.
small - Podemos inserir neste grupo o celular Sony Ericsson XPeria Mini,
que tem uma pequena tela QVGA de 24Ox32Opx.

normal - Celulares como o primeiro HTC Gl, o HTC Magic e o Motorola


DEXT tm uma tela HVGA mdia de 32Ox48Opx. Neste grupo, tambem se
s celulares um pouco maiores e com melhor resoluao, como

eamsung
X ~ '
)1;((;r;1eh1oS Nexus One, Sony Ericsson XPeria XIO, Motorola M11SI011 6
S Galaxy 5 05 quais tm uma tela WVGA que varia entre 480x800px
480 854px I-Ioje em dia este o grupo dos smartphones em geral.
large - T
21bl6t 5de 7 que tm uma tela com aproximadamente 1024x600px.
x 1arge
Tablets
de lo que tm uma tela com aproximadamente 1280x800 px.

E d`das
Alerta: essas ITl 1de tamanhos de tela so aproximadas e podem ter
variaes. ___,_,.---. ~- P S
s
es
. .
como ~ .
.
eccos
Baseado ne
ce u ar - . . Google Android - 4 edio

ses tamanhos de tela, chamados de screen srzes, podemos criar arquivos


F 1 para cada um. Por exemplo, podemos customizar a aplicaao para um
.com uma tela pequena, utilizando recursos especcos para telas pequenas,
"llageni rquivos de layouts, textos e tudo o que temos direito. Para isso,
basta criarmos pastas com a seguinte nomenclatura:

/res/drawable-small - Imagens customizadas para telas pequenas.


/res/layout-small - Arquivos de layout customizados para telas pequenas.
/res/values-small - Arquivos de textos customizados para telas pequenas.
O Android vai tomar conta do resto, e, se existir um arquivo especco para o ta
manho de tela do dispositivo, esse arquivo ser utilizado em vez do arquivo padro.
Neste caso do tamanho da tela, comum ter de customizar o layout para telas
pequenas com a pasta /res/layout-small, pois muitas vezes o contedo no cabe na
tela. s vezes, um simples Scrollvew pode resolver o problema, mas dependendo
do contedo da tela preciso customizar. Outro caso comum criar um layout
especco para tablets de 7 ou IO, com as pastas /res/layout-large e /res/layout-xlarge.

30.3 Proporo da tela (aspect ratio)


O termo aspect ratio representa a proporo entre a altura e a largura da tela. No
Android, essas telas podem ser agrupadas por aspett ratio como long ou notlong.
Uma tela denida como normal se tiver a resoluo HVGA de 320x48Opx. Neste
caso, ela caracterizada como notlong. Saiba que a tela HVGA denida como a
referncia padro para a comparao com outros tamanhos de tela.
Outro grupo que temos so os dispositivos identicados com uma tela do tipo long.
Para ser denida como long uma tela, deve ser considerada comprida quando est na
vertical e larga quando est na horizontal. Esse o caso no apenas dos smartphones
mais modernos, que tm uma tela bem comprida, como tambm dos tablets.

Nota: ao denir o aspect ratio da tela, comum vermos as notaes que esta tela
de 3:2 ou 4:3. Por exemplo, uma tela HVGA de 32Ox48Opx tem a proporo de
322, enquanto uma tela HVGA de 480x800px tem uma proporo de 4:3.

Eu particularmente nunca customizei layouts ou imagens pelo aspect ratio, portanto,


que tranquilo, pois na maioria das vezes o que interessa para o desenvolvedor C
saber se o dispositivo um smartphone, tablet de 7 ou tablet de lO'
(aPU|0 30 I Suportando difere 769
ntes tamanhos de telas

30.4 Resoluo e densidade da tela


Densidade da tela a rela `
a sua lar ur 1 ao entre a quantidade de pixels existentes na tela com
u fd e fi tura' POY den10 quanto maior a densidade, maior ser a
Cl an 1 21 E e pixels espalhados pela tela.

densidade alt (hd ' -


NO lfndmid, temos aparelhos com densidade baixa (ldp), densidade normal (mdpi)
d _d d 3 Dl), densidade extra alta (xhdpt), densidade extra extra alta (xxhdp),
ensi a e extra extra extra alta (hdp); e acho que por enquanto isso :-).
Podemos dizer que, at o Android 1.5, existiam apenas smartphones de densidade
mdia WCD, POI exemplo, o primeiro HTC G1, HTC Hero e o Motorola DEXT
Mas os smartphones seguintes, como o Nexus One, Sony Ericsson XPeria X1O,
Motorola Milestone, Samsung Galaxy S, foram lanados geralmente com uma
tela fsica maior (WVGA de 480x800px), uma densidade alta hdp, de forma que so
capazes de exibir uma quantidade maior de pixels na tela.
Apenas para conhecimento, na poca em que este livro estava sendo escrito, dois
dos mais modernos smartphones do Google eram o Nexus 5 e Nexus 6. O Nexus
5 tem uma tela de 1080x1920px com densidade extra extra alta xxhdp. O Nexus 6
tem uma tela de 144Ox256Opx com densidade extra extra extra alta xxxhdp.
Logo a seguir, vamos estudar mais detalhes sobre o conceito de densidade e a
diferena entre 1dp,mdp, hdp, xhdp, xxxhdp etc.

Ateno: o termo densidade refere-se quantidade de pixels disponveis na


tela e no ao tamanho fsico da mesma. Podem existir casos de aparelhos terem
o mesmo tamanho fsico, mas com densidades diferentes.

30.5 0 problema de utilizar pixels


At o Android 15 7somente existiam celulares Android com o mesmo tamanho de

om P . 1 - ' hos e resolues. Esses


te1a,e 05 desenvolvedores no tinham a preocupao de criar aplicaes que funcio
nassem de forma cOnS1SI1 te em telas com diferentes taman

con eci os
. - ' ' `surgm
lhor_re
e1u1areg gm Android 15 tinham uma tela HVGA de 32Ox48Opx, e alguns dos mais
c h _d - na poca eriam HTC G1 HTC Magic, HTC Hero, Motorola Dext etc.
C _omf
tem
. -do
o, foram
d 16Androi
..
do dispositivos com telas maiores e com me
. recursos para auxi iar o
foram criados
soluao, por 1550 3 Pa - ' ' le funcione perfeitamente em
desenvolvedor a organizar o aplicativo para que e
diversos tamanhos de tel21S
Pt; . -. ~ ,. - .
770 Google Android - 4' edio
tri vocc entender como isso funciona na pratica, vamos criar um novo projeto
l

com o nome Demo-Densltylndependentxels. N projeto, vamos criar um shape, que um


imllltvo XML que representa uma imagem retangular ou oval para geralmente
ser utilizada de lundo para alguma view Eu poderia mostrar os conceitos com
lllflllllfl' UUUTI VICW. mas vou mostrar utilizando shapes apenas para voc aprender
mais um recurso do Android. Um shape utilizado como se fosse urna imagem,
PUYHIIIIO, Crie-o na pasta /res/drawublc.

L1 /res/drawable/ovaI.mI

<?l VFSlOn="1'@" @nQdng:"Utf-8"?>


<shape xm1ns:androd="http://schemas.androd.com/apk/res/androd" androtd:shape="oval">
<so1td android:co1or="#cccccc" /

Feito isso, adicione no arquivo aetivity_main.xml duas imagens, cujo fundo definido
por este shape. A primeira imagem tem 100dp de largura e altura, e a segunda tem 100p.

lr'l'_l /res/Iayout/activity_main.xml

<?xn1 verson="1.0" encodng="utf-8"?


LnearLayout . . . androtd:ortentation="vertica1">
<ImageView
androd:1ayout_wdth="109dp" androtd:1ayout_heght="160dp"
androd:src="@drawab1e/oval" />
Ina9eVew
android:1ayout_width="10Gp" androd:1ayout_height="160px"
androd:layout_margnTop="16dp"
androd:src="@drawab1e/oval" /
</LnearLayout>

No layout temos duas imagens, uma abaixo da outra. A primeira deniu o tama
nho cm dp (density-inclependent pixels), e a segunda deniu o tamanho em pixels.
A gura 30.1 mostra a pre-visualizaao desse layout no editor do Android Studio
corn a opcao Preview AllsStreen Slzes. Observe que a primeira bolinha (desenhada acimal
esta correta. Porem, a segunda bolinha esta errada, e o seu tamanho sempre vai
variar dependendo da resoluo e densidade da tela do dispositivo.
Esse problema ocorreu por que utilizamos a notao px (pixel) l11\'Ss`tZl1lld' ima
gem. Para soluciona-lo e simples, basta seguir esta regra: Nunca utilize a notaao
px. sempre utilize a notao dp' Portanto, ao conhgurar ambas as imagens com
largura e altura com l0()dp, o resultado sera o mesmo em todos os dispositivos.
i1tdependentemente da resolueao da tela. coulorme mostra a hgura 30.2.
captuh 3 ' 5"P0ndo diferentes tamanhos de telas 771

Figura 30.1 - Wsualizando o layout em dispositivos com telas diferentes.


,_;-.__Y

Figura 30.2 - Layout coerente em todos os tamanhos de tela.

A nota . . .
30.6 DIP ou DP (density-independent pixel)
'd d de medida de pixel que foi criada para deixa
o dp ou dp uma um a 6
Qlvedor o problema de CXISIII diferentes resoluoes e
transparente para to desenv
densidades de 1612
2 Google
Para
77

voc^Android
_ . .-.4= edio
C entender como a notaao dp funciona, e preciso tomar como base uma
tela HVGA de 320x480 px, com densidade normal. Primeiramente, voc deve achar

eSfranho eu citar estas telas pequenas e ultrapassadas, mas a tela HVGA (320x480)
e considerada a padro (base) da plataforma, portanto, para entender como
funcionam as outras telas, voc precisa entender essa. A tela HVGA (32Ox480) tem
160dp(D0tS Def Inch) e, neste caso, a unidade dp equivalente a um pixel, portanto
100dp igual a 100px.

Ateno: no confunda dpi (dots per inch) com dp (density-independent pixel). A


notao pontos por polegada, ou como conhecida no ingls dpi (dots per inch),
uma medida de densidade amplamente utilizada, a qual de ne a quantidade de
pixels por polegada na tela. j dp (density-independent pixel), ou simplesmente
dp, uma notao utilizada pela plataforma do Android para trabalhar com
diferentes densidades de tela.

O problema que essa conta pode mudar conforme a tela do dispositivo. Por
exemplo, pode ser que 100dp seja igual a 75px ou 300px, tudo depende daquela
tal de densidade que comentamos anteriormente.
Mas, na prtica, o que densidade? Ela simplesmente um nmero que deve
ser multiplicado pelo valor em dp, para descobrir o valor em pixels. No caso
da tela HVGA (mdpi) de 320x480px, a densidade 1.0. Portanto, ao fazer a conta
100dp * 1.0 = 100px, ou seja, neste caso 100dp igual a 100px.
j em dispositivos que tm uma tela pequena QVGA (ldpi) de 240x320 pixels, a
densidade da tela 0.75. Se voc zer a conta, 100dp * 0.75 = 75px.
Citando outro exemplo, no caso de uma tela alta densidade NVGA (hdpi) com
480x800px, a densidade 1.5, portanto 100dp * 1.5 = 150px.
Essas foram as trs primeiras densidades que surgiram na plataforma: ldpi (baixa
z ()_75), mdpi (mdia/padro = 1.0) e hdpi (alta = l.5).
Mas claro que voc no vai car fazendo essas contas, pois justamente por isso
foi criada a notao dp.Ao utiliz-la, Android far a correta converso de dp para
pixels utilizando a densidade da tela do dispositivo. Concluindo o raciocnio,
como um dispositivo pode ter uma tela com mais ou menos pixels que outras.
se utilizarmos valores ein pixels, vamos obter resultados diferentes. Por exemplo,
em um dispositivo com tela pequena e baixa densidade, 100px muita coisa C
quase ocupa metade da tela. Mas 100px nos atuais dispositivos, como Nexus 5
ou Nexus , no signica praticamente nada, pois so dispositivos que tm telas
com mais de 2.000px.
,e.~.
Captulo 30 .S Uportando diferentes tamanhos de telas 773
Portanto visu l - -
Ve]a novamente -
Orm 21 mente 100px em um dispositivo com tela pequena bastante coisa,
P . m outro com tela grande de alta dens1dade,100px quase nao seria nada.

bola. Isso a z
100 21 gura 30.1 e repare na segunda bolinha que foi desenhada com
px no Nexus 6. A bolinha mais parece um pequeno pontinho do que uma
contece porque o Nexus 6 contem muitos pixels na tela. A densidade

cou bom e ~
do Nexus 6 4.0, portanto, ao desenhar uma bolinha de 100dp, o Android fez a
conta: 100dp * 4.0 = 400px. Por isso, ao utilizar dp, o resultado visual no Nexus 6
coerente com todos os tamanhos de tela (Figura 302).
E isso que a notao dp faz! Ela traz o mesmo resultado em todos os tipos de
telas. Portanto, lembre-sees a dregra
t : N
unca utilize a notao px, sempre utilize
a notao dp'

Nota: ao utilizar a notao dp (density-independent pixel), o resultado visual ser


o mesmo, independentemente da resoluo e da densidade da tela. O Android
vai usar a densidade da tela para calcular o tamanho correto em pixels.

30.7 Tabela de densidade dos dispositivos


A seguinte tabela mostra a densidade de cada tela suportada pelo Android.
Tela Densidade
ldp Baixa densidade = 0.75.
mdp Mdia densidade = 1.0. o tamanho padro para as comparaes.
hdp Alta densidade = 1.5.
xhdpi Extra alta densidade = 2.0. I
xxhdp Extra extra alta densidade = 3.0. Esta atualmente a densidade dos
Nexus 5 e Nexus 6.
Xxxhdp Extra extra extra alta densidade = 4.0.
ue, uanto maior a densidade da tela, maior
Baseado nefsa tabela, pdtlindlehrciior a quantidade de pixels que o disposi
a resoluao da mesm , tela Para ter ideia, o Nexus 5 tem uma tela de 5 com
tivo Consegue mostrr n hfj 1 (3 0). Portanto, ao fazer a conta do exemplo an
1080X192PX e denslda 6 xx 1r)()dp * 3.0 = 300px. j o Nexus 6 tem uma tela de
6' COT1 . * : _
terior no Nexus 5, fica asslm'
1440X256()px e densidade xxxhdp (4.0), conhecida como 560dpi. Portanto
a mesma conta no Nexus 6 ca. 100dp PX
7" aoagizmzmia-4-mas
A".`''-..
30.8 (ustomizando as imagens conforme a densidade da tela
gora que ja conhecemos o conceito de densidade, vamos explicar como utilizar
corretamente a pasta de imagens /res/drawable.
Voc deve ter percebido que esta pasta tem diversas variaes conforme a densidade,
por exem plo, /rcs/drawable-ldpi, /res/drau/able-mdpi, /ra/drawable-hdpi, /res/drawable-xhdpi,
e /res/drawable-xxhdpi. Para o que serve cada uma dessas pastas acredito que voc
tambm saiba, mas talvez a dvida seja: como devo utiliza-las na prtica?
Bom, assim que eu explico para os designers da minha empresa, ento talvez
voc possa fazer o mesmo.
O tamanho padro/base de tela o HVGA com 32Ox48Opx, caracterizado como
mdp (densidade 1.0). Esta a pasta /res/drawable-mdpi. Neste caso, digamos que
temos uma imagem de banner que deve car no topo da aplicao, portanto para
esta tela ela deve ter 320px de largura com alguma altura qualquer, por exemplo,
32Ox40px. Essa imagem deve ser colocada na pasta /res/drawable-mdpi.
Ao executar a aplicao em celulares com uma tela de tamanho normal e mdia den
sidade, o exemplo vai funcionar perfeitamente. Mas, ao executar a mesma aplicao
em um celular de alta densidade (hdpi), o Android vai automaticamente procurar as
imagens na pasta /res/drawable-hdpi ou superior. Neste caso, se a imagem no for en
contrada nesta pasta, a pasta padro /res/drawable-mdpi ser utilizada. Mas o problema
que neste caso o Android vai redimensionar a imagem em tempo de execuo para
se ajustar ao tamanho de tela. O resultado que a imagem vai se distorcer.
A regra simples: como em celulares de alta densidade temos mais pixels na
tela, consequentemente podemos exibir imagens maiores com mais pixels para
obter uma melhor definio e qualidade. Mas como converter a imagem? Qual
escala utilizar? Nesses casos, basta multiplicar o tamanho da imagem normal pela
densidade desejada, que a tabela que vimos anteriormente. Por exemplo, para
telas hdpi (densidade 1.5), basta multiplicar a imagem por 1.5. Portanto, 32Ox4Opx
multiplicado por 15 resulta em 48OxOpx, e esse o tamanho da mesma gura
que deve car na pasta /res/drawable-hdpi. '

Nota: voc j parou para ver o tamanho dos cones de um projeto Android?
O cone da pasta mipmap-mdpi tem 48x48px, mas 0 da pasta mipmap-hdpi tem
72x72px. Note que 48 " 1.5 = 72, onde 1.5 a densidade de uma tela hdpi.
Seguindo o mesmo raciocnio, o cone na pasta mipmap-xxhdpi tem 144x144px
pois 48 ' 3.0 = 144, onde 3.0 a densidade de uma tela xxhdpi.

E para Finalizar agora vai a dica sobre como trabalhar com imagens.
(apt"| 3 ' 5UPortando diferentes tamanhos de telas 775

wa ' - - . .
56 voc no informar
de com ila uma
~ ~imagem
. _ correta para cada densidade em tempo

ast _ ' ' - .


/res/dm P blg OZOU 5618, Se nao adicionar a imagem no projeto em cada pasta
_ ff, O ndroid vai redimensionar a imagem em tempo de execuo. Para
isso, ele utiliza a densidade da tela.
A S01U0 para a maioria dos casos
colocar imagens de alta resoluo no projeto na
p a /res/drawable xxhdpl e deixar o Android converter para baixo em telas menores.
E como os designers devem criar asgras
u ?recisa
P criar as guras para todos
esses tamanhos?

Como eu disse, costumo utilizar um tamanho nico, baseado em telas grandes


e insiro as guras na pasta /res/drawable-xxhdpi. Eu falo assim para o designer:
Cara, cria as guras baseados em uma tela de 1080x1920px, que a do Nexus 5,
e deixa o resto comigo Nesse caso o Android vai redimensionar a imagem para
baixo nos dispositivos com menor densidade e tudo vai funcionar. Eu costumo
fazer isso em meus projetos e nunca tive problemas.
Segundo a documentao do Android, as imagens para a densidade xxhdp (3.0)
j tm muita denio, ento voc no precisa utilizar a densidade xxxhdpi (4.0)
se no quiser, o que reduz o tamanho do aplicativo nal.
O Google s recomenda que o cone do aplicativo seja criado em todas as densidades,
pois ao ser redimensionado o cone apresenta problemas. Portanto, inclusive o cone
de densidade xxxhdp (4.0) precisa ser informado, que aquele arquivo ic_launchex png.

Nota' se voc tem uma imagem e por algum motivo deseja que ela no seja

oc,._
escalada utilize o qualicador nodp, criando uma pasta /res/dmwable-nodpi.
7

30.9 Trabalhando com a unidade dp no Canvas

para criar I
N aptulo 7 sobre a classe View, aprendemos a desenhar manualmente no canvas
` views customizadas. Apenas para lembrar, tudo consiste em criar uma
su bclasse de view e sobrescrever 0 metodo onDraw(canvas).
Q htrec
de oCdigo a seguir mostra como desenhar um quadrado de 100x100px.
@0verrde ) {
D
rotected void onDraw(CaVa5 Camas
super.onDraw(CV5)S _ _
canvas . drawRect(0 , Q, 1oo, 100, Dalz
}
O a f
76 Google
7
' . .
Android
mai ` f. . . _ . .-.4_
edio
P , mas muita calma nesta hora! Acabamos de utilizar pixels e violar a regra
s importante que e. Nunca utilize a notaao px, sempre utilize a notao dp`
AO utilizar a notao dp no cdigo do layout XML, o Android faz a correta con
versao de dp para pixels, baseada na densidade da tela do dispositivo. Mas agora
no codigo precisamos recuperar a densidade da tela pela API e fazer o clculo.
Felizmente isso simples, basta adicionar um mtodo como este na sua subclasse
de View:

public oat toPixe1s(oat dip) {


Resources r = getResources();
oat densidade = r.getDisp1ayMetrics().density; // Densidade da tela
int px = (int) (dip * densidade + 0.Sf); // Converte dp para pixels
return px;
}

Com esse mtodo, podemos desenhar o quadrado da maneira correta, que


sempre utilizando valores em dp. Nesse caso, o valor 100dp ser convertido para
pixels conforme a densidade da tela do dispositivo.
@0verride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(G, 0, toPie1s(100), toPixe1s(100), paint);
}

importante voc entender esse conceito para criar views customizadas que
funcionem corretamente em todos os tamanhos de telas. Por exemplo, comum
utilizar views customizadas com canvas para desenhar um grco de linha, barra
ou pizza. Nesses casos, sempre faa essa converso no cdigo.
Lembre-se de que o segredo do clculo recuperar a densidade do aparelho com
este mtodo:
oat densidade = r.getDisp1ayMetrics().density;

Recomendo que voc crie um projeto de exemplo qualquer emostre o valor des
sa varivel densidade em vrios emuladores diferentes para fortalecer o conceito.
Lembre-se de que a densidade da tela vai retornar sempre valores conforme 8
tabela que vimos no tpico Tabela de densidade dosdispositivos".
nz...
capt"l 30 ' 5UP0rtando diferentes tamanhos de telas 777
30.10 Tamanho da tela em dp
Muitas vezes no degenvglvim
de mma h d ento para Android comum encontrar denies
ue 32Od PS tela ITI CD. Por exemplo, a documentaao ocial do Android diz
Cl I . p o tamanho minimo de uma tela de smartphone, 600dp o tamanho
minimo de um tablet de 7' e 72Odp o tamanho de um tablet de 1O'
Mas como voc sabe o tamanho da tela em dp? 7
Basicamente, o clculo para descobrir o tamanho da tela em dp dividir o tamanho
da tela em pixels pela densidade. Exemplos:
A tela HVGA (32Ox48Opx) ndp tem densidade igual a 1, ento essa uma
tela de 32Ox48Odp.

A tela WVGA (480x800px) hdp tem densidade igual a 1.5, ento essa uma
tela 32Ox533dp.
O Nexus 5 tem uma tela de (1080x1920px) xxhdp com densidade 3.0, ento
essa uma tela 36Ox64Odp.
O Nexus 6 tem uma tela de (144Ox256Opx) xxxhdp com densidade 4.0, ento
essa uma tela 36Ox64Odp.

30.11 Dimenses (dimen)


Para fechar o assunto sobre converso de dp para pixels com chave de ouro, vamos falar
sobre os famosos dmens, que so constantes que cam no arquivo /res/values/dimensxml.

A vantagem de utilizar constantes porque podemos reaproveit-las em um nico


lugar No caso dos dnens ainda existe outra vantagem, pois um valor de dimenso
(dimen) automaticamente converte o valor de dp para pixels. Por exemplo, digamos
que temos as seguintes constantes para definir a largura e altura de um quadrado,
O qual Ser desenhado pela API utilizando canvas.

d__1
/res/values/dimens.xm|

resources> .
public class ConverterPxe1DP1Actvty @te"d5 Activity {

< <dmen name="quadrado_wdth">100dp


. n d'ren>
<dirnen nz.pzz~'q~dfd-h@1@'t >1O9dp</ 1

- " o cod1gO
Ao ler cada dimenso 11
f ' lor retorna o automaticamente em pixe s.
0 va
778 Google Android - 4 edio
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// L a dimenso. 0 valor ser retornado em pixels conforme a densidade da tela
t l3f9UFP = getContext().getResources().getDinension(R.dimen.quadrado_width);
oat a1turaPx = getContext().getResources().getDimension(R.dinen.quadrado_height);
// Desenha o quadrado
Ca"Va5-df3WR@Ct(0, G, larguraP, a1turaPx, paint);
}

Pronto! Alm de utilizar constantes, que uma boa prtica de programao, no


precisamos fazer nenhum clculo, pois um dimen j retorna o valor em pixels correto.
Dimenses, ou apenas dimens como so popularmente conhecidos, tambm so muito
utilizados em arquivos de layout XML para denir as configuraes de espaamen
to e margens. Por exemplo, atualmente o wizard do Android Studio ao gerar um
arquivo XML cria as configuraes de padding (espaamento) da seguinte forma:

/res/layout/fragment_main.xmI
<Re1ativeLayout . . .
android:paddingBottom="@dimen/activity_vertica1_margin"
android:paddingLeft="@dimen/activity_horizonta1_margin"
android:paddingRight="@dimen/activity_horizonta1_margin"
android:paddingTop="@dimen/activity_verticai_margin" >
<TextView . . . />

Essas dimenses so referenciadas com a sintaxe @dimen/nome e cam no arqui


vo /res/values/dimensxml.

/res/values/dimens.mI

<dimen name="activity_horizonta1_margin">16dp
<dimen name="activity_vertica1_margin">16dp
</resources
As dimenses so utilizadas para especicar espaamentos, tamanhos de fOnI,
tamanhos de view etc. e, dependendo do tamanho da tela, elas podem ser custo
mizadas. Por exemplo, a notao /res/values-w820dp sobrescreve a dimenso para
tablets de 7 ou 1Ocaso estejam na horizontal. O tablet de 7 na horizontal tem
uma tela de aproximadamente 960dp e o tablet de IO tem uma tela de l28Odp.
Bas portando
d ~ ~diferentes
~ tamanhos de telas 779
(|)U|0 30 I Su

Seretigesas
a mffma0S, a notaao /res/values-w820dp indica que esta pasta
. _ . a caso a largura da tela tenha pelo menos 82Odp. O "w" neste caso
518n1Ca Width (largura).

/res/vaIues-w820dp/dimens.xmI
<|'SOU`CS>

<dinen name:"actvty_horizonta1_margn">64dp</dmen>

Criar dimenses tambm muito comum para manter aplicativos compatveis


com smartphones e tablets. Por exemplo, podemos ter um Textvew com o tamanho
de fonte 14sp desta forma:

<TextView ... android:tet="..." androd:tetSze="@dnen/text_size" />

A notao @dmen/text_sze vai ler a dimenso denida no arquivo /res/values/dimensxml.

/res/values/dimens.xm|

_
<dmen name="tet_size">14sp</dnen>

Mas para os tablets de 10 polegadas podemos sobrescrever esta dimenso e au


mentar a fonte do texto, basta criar o seguinte arquivo:

/res/vaIues-xlarge/dimens.xmI

<dmen name="tet_size">40SD</d1e">

Ou pgdemos utilizar a notao sw de srnallest width (menor largura), a qual


compatvel com o Android 3.2 ou superior e ser explicada no prximo tpico.

/res/vaIues-sw720dp/dimS-Xml

|| ' " ll)


<dnen nane= tet_s1ze >40SP</dme

Nota: sempre u1d.


t`lize a notao sp para tamanho de fontes, da mesma forma que
dp para tamar11'l0 e espaamento
views.
__ _ - _-
78 Google Android - 4= edio
AQ ' ~ . , . . .
30.12 Qualicadores de recursos para tablets
desenvolver aplicativos para Android, e preciso customizar a interface para
os tablets. Utilizar o m esmo layout do smartphone e apenas esticar ou aumentar
0 conteudo pode no ser o ideal, pois a experincia ao utilizar o aplicativo no
otimizada para tablets.
Um exemplo clssico de layout para tablets a tela dividida em duas partes,
para aproveitar melhor o espao disponvel na tela. Porm, no captulo 8, sobre
fragments, tambem estudamos vrios tipos de layouts que podem ser utilizados.
Para criar um layout especco para tablets, podemos usar as seguintes pastas:
Pasta _ K _ ,___ _Descrio
/res/layout-large Para tablets de 7 (large = grande). _~'
/res/layout-xlarge Para tablets de IO (xlarge = extragrande).
Mas a partir do Android 3.2 foram criados outros qualicadores, que comparam
o tamanho da tela em dp (largura ou altura) para identicar se o layout para
smartphone ou tablets.
Identicador Descrio
hat Pdpl P Altura di5nv-liaiii{Hm`iz,P}ii2 pdilizadaf
conforme a orientao.
width dp Largura disponvel - Idem ao anterior, mas compara a largura
da tela.
smallest width dp Menor largura da tela, sem levar em considerao a orientao.
Utilizando esses qualificadores, possvel criar pastas com a notao
/res/layout-h600dp para denir a altura desejada, /res/layout-w600dp para denir a
largura e /res/layout-sw600dp para denir a largura mnima da tela, independen
temente se estiver na vertical ou horizontal.
Por exemplo, se o layout exige que a menor dimenso da tela tenha pelo menos
600dp, sempre (independentemente da orientao), podemos usar 0 qualicador
/res/layout-sw600dp/ para criar os recursos de layout.
Desses identicadores, o smatiest width dp (menor largura) o mais utilizado C
substitui as notaes large e xiarge. O indicador smattest width dp baseado na
largura da tela, pois a largura frequentemente utilizada como base para criao
de um layout. Na altura (vertical) geralmente no temos muitos problemas para
criar layouts, pois possvel criar uma tela com rolagem. Mas a largura um fawf
decisivo para decidir a quantidade de contedo que cabe na tela. Muitas vezes,
dependendo desse contedo, podemos decidir entre criar layouts diferentes 110
Cap't"| 3 ' 5UPortando diferentes tamanhos de telas 731
aplicativo ara
podemos at asdividi-la em d l - - , g
P smartphones e para tablets Por exemplo se a tela bem lar a
PaffS (esquerda e direita).
E ual a lar ' ~
_ q _ gufa dos dispositivos? Como vou saber qual largura utilizar? Para
isso existe esta tabelinha da d ocumentao, que mostra a largura mnima em dp
para cada tipo de tela.
Lfgura Descrio
329dD Esta largura mnima dos smartphones (24Ox32Oldpi, 32Ox48Omdpi,
480x800hdpi etc.).
69@dD Tablet de 7 (600x1024px).
729dP Tablet de IO (72Ox128O, 800x1280px).
Baseado nesses seletores por tamanho da largura da tela, ca fcil criar layouts
especcos para smartphones ou tablets, como por exemplo:
Pasta Descrio
/res/layout Pasta de layout para smartphones.
/res/layout-sw600dp Pasta de layout para tablets de 7 e 1O
Caso voc precise criar layouts diferenciados para tablets de 7 e 1O basta utilizar
estas pastas:
Pasta Descrio _
/res/layout Pasta de layout para smartphones.
/res/layout-sw600dp Pasta de layout para tablets de 7'
/res/layout-sw720dp Pasta de layout para tablets de 1O'
Basicamente, essas duas pastas indicam o tamanho da tela na horizontal; a no
tao sw de smallest width (menor largura). Neste caso, os tablets de 7 tm pelo
mens 600dp de largura e os de 1O tm pelo menos 720dp de largura.
Mas como eu j disse antes, ainda sou adepto de utilizar principalmente a pasta
7

/res/layout-xlarge para tablets de 1O' pois essa notao funciona desde o Android
3_0 e no apenas a partir do Android 3.2.

30.13 . -Links
z rincipais-conceitos
' ` desenvoteisver ap1icativos
sobre como
1
z manhos de te as, _
e' lexocomp
e or isso P 1
Este Caipltulo exphcou Os pl corn resolues e densidades diferentes. Mas o tema
para vamos ta ' recomendo que voc complemente a leitura com textos da
documentao ocial e blog do 6003 6
732 Google Android - 4 edio
Como eu disse na introduo, este captulo iria lhe tirar muitas dvidas, ou deixa
-lo com muitas dvidas. Portanto, caso voc no tenha entendido muito bem algo,
no tem problema, pois o assunto complicado mesmo. Continue lendo o livro e
mais tarde leia novamente esse assunto se precisar. Eu espero que este livro sirva
de guia no para voc somente aprender o bsico sobre o Android, mas para ser
uma fonte de consulta quando quiser relembrar algum conceito.
Mais do que nunca, importante complementar a leitura com a documentao
ocial.

Providing Resources

http://developerandroid.com/guide/topics/resources/providingresources. html
' Supporting Multiple Streens

http://developen android. com/guide/pmctices/screens_support. html


Supporting Tablets and Handsets

http://developer android.com/guide/practices/tablets-andhandsets.html
New Tools for Managing Screen Sizes

http://android-developers.blogspot.com.br/2011/07/newtools-for-managing-screcm
-sizes. html

Getting Your Apps Ready for Nexus 6 and Nexus 9

http://android-developers.blogspot.com.br/2014/10/getting-you r-apps-ready-for
-nexus-6-and.html
'\ CAPTULO 31
p o Threads avanado
* j AsyncTask e Loader
z- \ _,_,,(

Neste cap1tulo,vamos estudar alguns exemplos mais avanados da classe AsyncTask,


assim como a nova API criada no Android 3.0, chamada de Loader.

O objetivo deste captulo explicar situaes e erros comuns que geralmente


costumam dar trabalho para os desenvolvedores Android. O assunto deste tpico
avanado, portanto leia quando achar necessrio.

31.1 0 problema com o ProgressDiaIog


Nesta altura do campeonato, voc j sabe a importncia de utilizar threads e
j conhece a classe AsyncTask, a qual facilita a utilizao de threads no Android.
Portanto, vamos direto ao assunto e estudar alguns assuntos avanados e erros
comuns enfrentados por desenvolvedores.
Apenas para lembrar, eu particularmente sempre utilizo em meus projetos uma

tagens encapsu _
pequena biblioteca que encapsula a classe AsyncTask, pois, conforme expliquei no
captulo 17, sobre Web services, e vimos no projeto dos carros, tivemos vrias van
lando o cdigo da AsyncTask. Nestes prximos exemplos, utilizarei
diretamente a classe AsyncTask apenas para demonstrar alguns problemas classicos
e assuntos avanados sobre o ciclo de vida da aplicao.
Abra 0 projeto de exemplo deste captulo e procure a activity que demonstra
como fazer o down load de uma imagem. O exemplo de download o mesmo
que fizemos no captulo 10, sobre threads e handler, porm, em vez de utilizar
um ProgressBar para fazer a animao, foi usado um alerta com o ProgressDa1og.
justamente isso nos trar um problm

783
784
Google Android - 4 edio

! DownloadlmagemAsyncTaskActivity.java

Dublic class DownloadInagemAsyncTaskActivity extends AppCompatActivity {


Dfivate
static nal String URL = "httpz//livroandroid.com.br/ings/livro_android.png";
private ProgressDialog progress;
private Imageview ingview;
private Bitmap bitmap;
@0verri de
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_download_imagen);
imgview = (lmageview) ndViewById(R.id.img);
if(icicle != null) {
// Recupera o estado
bitmap = icicle.getParcelable("img");
}

if(bitmap == null) {
// Faz o download
downloadImagem(URL);
} else [
// Atualiza a imagem se recuperou o estado
imgview.setImageBitmap(bitmap);
}

// Faz o download da imagem em uma nova thread


private void downloadImagem(nal String urllmg) {
// Cria uma AsyncTask
DownloadTask task = new DownloadTask();
// Executa a task/thread
task.execute(URL);
}

private class DownloadTask extends AsyncTask<String,Void,Bitmap {


@0verride
protected void onPreExecute() {
super.onPreExecute();
// Mostra o ProgressDialog antes de iniciar a task
progress = ProgressDialog.show(getContext(),"Aguarde", `
"Fazendo o download... Gire a tela durante o download para fazer BO0M!");
}

@Override
protected Bitmap doInBackground(String... params) {
// Faz o download da imagem
try {
Captulo 31 1 Thfead 5 aVa"ado - AsyncTask e Loader

bmD = Download.downloadBitnap(URL);
} Cth (Exception e) {
Log.e("livroandroid",e.getMessage(), Q);
}

return bitmap;
}

Protected void onPostEecute(Bitmap bitmap) {


if(bitmaD != null) {
TaS ~ f'\\<@T@t(9etBSeC0nt@t( ), "OK", Toast. LENGTH_SHORT) . show()
// Esconde o progress
closeProgress();
// Atualiza a imagem
imgview.setImageBitmap(bitmap);
}

@0verride
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Salva o estado da tela
outState.putParcelable("ing",bitnap);
}

private void closeProgress() {


if(progress != null && progress.isShowing()) {
progress.dismiss();
progress = null;
}

public Context getContet() {


return this;
}

O layout desta activity simples e contm apenas o Imageview.

/res/Iayout/activity_downIoad_ima9@m-m|
"utf-8"?>
<?ml version="1.0" encodin9=
<FrameLayout .
<ImageView
android'id="@+id/img" android:layout_width="wrap_content"
android
_ t h 9ht="wrap_content"
: ayo _ '
android 'ayot-gavty-"center" android:scaleType="tCenter" />
786 Google Android - 4 edio
) resultado deste exemplo pode ser visto na gura 31.1, e a princpio tudo esta
funcionando bem.

. V -zaf
l

. _. .ira .
, ..

Google
5

DR|D 1*
w1:w
Aprenda: mu aomxacoes ou: ovsposetos menus ll
f!"\0|f0 SDI .
ii

'WOVQC KYCI1!l.LU
tl

Figura 31.1 - Download da imagem realizado com sucesso.

Esse cdigo mostrou como fazer algumas coisas:


1. O download foi feito em uma thread com a classe AsyncTask.
2. Estou utilizando um ProgressDa1og para mostrar o problema de girar a tela
(veja prximo tpico). Na prtica, prero utilizar o Progress8ar conforme
mostrado em outros exemplos, mas quero reforar um problema clssico
com o ProgressDa1og.

3. Se, depois do download, o usurio girar a tela, a activity vai salvar o Btmap
no mtodo onSaveInstanceState(bundle) e recuperar esse mesmo Btmap depois
quando for recriada_
4. Se durante o download o usurio girar a tela, a aplicao vai travar.
Sobre o ltimo item explicado acima, vamos testar para simular o erro. Execute
o aplicativo e, enquanto o Progressalog est aberto, gire a tela. O resultado um
erro conforme a figura 31.2.
CHPUO 31 I Threads avanado - AsyncTask e Loader 787

Figura 31.2 - Erro ao girar a tela durante o download.

No LogCat podemos ver a exceo detalhada (stack trace), conforme a gura 313.

-' )
Ia NQG
Cl Matchgase Regex Wozds gx -0

31

Jr 09-27 19:17 :31.2<l4 1807-1807/br.livroandroid.11v.r:android\:apl2_chzeadas E/Androzdluncizc FATAL EXCEPTION: mala 7 7 l


j .lang.Illegalkxguzaentixceptionz View nos ancached na vindo anager
at mzzoid.view.indo\.*KanagerIx;>1.tindViewLo:ked(;%`;! ' .;~~zz_;;>,. ;5_ ~z, 1 ;~_{)
-s at am1roid.vie\.liindowanagexlmpl.rem:weView(,;; :z ,__z"I : =,, :=_1~L ; _)
L,
F, at an:roid.vi
at .iindcwiiocaliiindowanaqer.xemoveviewg
a.nd:oid.app 15lQQ.dil!1i3Ui&1OQ(2.;. :.\ ,z)
at android.app.Dialog.access$000 (T. 1 z;,;; iv)
ac androi<.i.app.Dialoq$1 .run
fi Q: G.I0i.[.'Q.U1BlCQ.dililfiiii-;2f)
at br . 1 ivroandroid . 1iv:oand:oidcap12_'chzeada: . Down1oadImagemaync3'askctivity. closehogress (Dow: l_q_z;dIz1a.;_g|_;yncTaskAc:1vi ty. zgv : 94)
t. br . livroandred . 1i'~/randridcdp2__Chreada5 . Down1.oadImagcmZsyncTaskAct:iviey . access O (pavnloadnaqggggzgsfa sgct vz cv,_java ;2_Q)
L- 1.---...,....:. 1.........-1..-.4....-Q s.......a n....-1-`.u..-...........--r_..a.\-^-.. r\...-1,.-.rr-.=- -v\-...r...,-.-z.1r\~...v..n.......:....-.n-..:.\.....

Figura 31.3 - Erro ao girar a tela durante 0 download.

Esse erro acontece porque a tarefa da AsyncTask continua executando em segundo


plano durante a rotao da tela, e quando o download da imagem termina a
aplicao te nta fechar o ProgressDalog. Como o ProgressDalog associado com a
activity que o criou, esse erro ocorre justamente porque essa activity foi destruda
Cll.1I'3I1l 3 tI`OC8 de OI`I'lt21O.

Para solucionar esse p roblema, temos de fechar o ProgressDalog quando a activity for
encerrada mesmo antes de a AsyncTask terminar, conforme demonstrado a seguir:
3

DownloadImagemAsyncTaskAttivity.java

public class DownloadImagemAsyncTaskActvty extends AppCompatActvty {


, _ _ // Tudo igual aqui
@Override
protected void onDestroy() f
788 Google Android - 4 edio
super.onDestroy();
c1oseProgress();
}

Note que o mesmo cdigo mostrado anteriormente, mas adicionei o mtodo


onDestroy(). Feito isso, a aplicao no vai mais travar, pois o Progressalog en
cerrado junto com a activity Naturalmente, o ideal tambm seria parar a tarefa
da AsyncTask chamando o mtodo cance1(boo1ean).

Porm ainda temos um problema, pois, quando a nova activity for criada depois
da rotao da tela, o download da imagem vai iniciar novamente. Neste caso
precisamos dar um jeito de girar a tela sem interromper o download e reutilizar
a primeira thread que est fazendo o download da imagem.

Nota: estou mostrando esses problemas ou situaes porque com certeza isso
algum dia vai acontecer em algum aplicativo seu, e assim voc j estar preparado.
Se precisar, releia este captulo depois, pois estamos dando um passo alm do
tradicional, que mostrar como consultar um web service com uma thread ou
AsyncTask.

31.2 (ontrolando a troca de orientao ao executar uma task


O exemplo anterior faz o download da imagem e recupera o estado da activity
caso o usurio gire a tela depois de o download terminar. Mas caso o usurio gire
a tela durante o download temos um problema.
Quando o Android recriar a activity do jeito que est o cdigo, no mtodo
onCreate(bundle) ser disparada uma nova AsyncTask para fazer o download da ima
gem, sendo que a primeira thread cou perdida em memria. Isso signica que se
voc car girando a tela vrias vezes o download nunca vai terminar, pois vrias
threads sero disparadas, mas nenhuma delas vai conseguir atualizar a interface
da nova activity
Para solucionar esse problema, vamos utilizar um fragment com o mtodo
setRetanInstance(true) para deixar a instncia do fragment viva, ou seja, reter
0 fragment em memria. Dessa forma, a AsyncTask continuar viva dentro do
fragment, e quando ela terminar basta atualizar a interface. Somente a view do
fragment ser recriada, mas a AsyncTask e o download sero mantidos e reutilizados.
O seguinte cdigo mostra a activity que apenas vai adicionar o fragment no layout.
Captulo 31 u Threads avan d
0 - AsyncTask e Loader 789
DownloadImagemFragmentActivity.java
public cl
ass DownloadInagemFragnentActivit
@0verride td
y ex en s AppCompatActivity {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_download_inagen_fragnent);
if(icicle == null) {
9@t5UDDortFragmentManager().beginTransaction().add(R.id.layoutFrag,
new DownloadInagenFragment()).connit();
}

/res/Iayout/activity_downIoad_imagem_fragment.xmI
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.con/apk/res/android"
android:layout_width="natch_parent" android:layout_height="natch_parent"
android:id="@+id/layoutFrag">

Como podemos ver, a activity no faz nada, pois a lgica ca toda no fragment

DownloadlmagemFragment.java
public class DownloadInagemFragment extends Fragment {
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
private ProgressDialog progress;
private Bitmap bitmap;
private Imageview imgview;
// A task ca como atributo para car viva durante a rotao da tela
private DownloadTask task;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); // Salva 0 estado do ffget
}

@0verride
public View o nCreateView(Layoutlnater inater, @Nullable ViewGroup container,

ie - =
@Nullable Bundle savedInstanceState) l
V' w view - inater inate(R.layout.fragment_download_inagem, container, false);
mgview = (ImageView) view.ndViewById(R.id.ing);
L9-d("livroandroid","frag onCreateView()");
if(bitmap == HU11) {
// Faz o download
downloadImagem(URL);
} else {
// Atualiza a imagem se recuperou o estado
setBitmap(bitmap);

return view;
}

// Faz o download da imagem em uma nova thread


private void downloadImagem(nal String urllmg) {
// 0 atributo task ca retido em caso de rotao, assim podemos saber
// se a task j est executando
if(task == null) {
Log.d("livroandroid","> DownloadTask.execute()");
// Cria uma AsyncTask
task = new DownloadTask();
// Executa a task/thread
task.execute(URL);
} else {
Log.d("livroandroid","DownloadTask j est eecutando.);
// Mostra novamente o dialog para acompanhar o download
showProgress();
}

private class DownloadTask extends AsyncTask<String,Void,Bitmap> {


@Override
protected void onPreExecute() {
Log.d("livroandroid","task onPreExecute()");
showProgress();
}

@Override
protected Bitmap doInBackground(String... params) {
Log.d("livroandroid","task doInBackground()");
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e("livroandroid",e.getMessage(), e);
}

return bitmap;
Captulo 31 u Threads avanado -sync
A Task e Loader
}

@Override

protected void onCancelled() {


task : null;
Super.onCancelled();

} L0g.d("livroandroid","task onCancelled()");

protected void onPostEecute(Bitmap imagem) {


task = null;
L0g.d("livroandroid","task onPostEecute()");
if(imagen != null) {
setBitnap(inagen);
}

private void setBitmap(Bitmap inagem){


Log.d("livroandroid","setBitnap()");
this.bitmap = imagem;
// Fecha o progress
closeProgress();
// Atualiza a imagem
imgview.setInageBitnap(inagem);
}

@Override
public void onDetach() {
super.onDetach();
Log.d("livroandroid","frag onDetach()");
// Fecha o progress antes de desassociar o fragment da activity
closeProgress();
}

private void showProgress() {


Log.d("livroandroid","showProgress()");
// Mostra o dialog e permite cancelar
progress = ProgressDialog.show(getActivity(),"Aguarde","Fazendo o download
progress.setCancelable(true);
progress setOnCancelListener(new Dialoglnterface.0nCancelListener() {
@Override
P ublic void onCancel(Dial09It@ff3C@ d109)
// Cancela a ASYCTa5k
task.cancel(tFU@);
}

});
}
792 Google Android - 4 edio
private void closeProgress() {
Log.d("livroandroid","closeProgress()");
if(progress != null && progress.isShowing()) {
progress.dismiss();
progress = null;
}

Como podemos ver neste exemplo, o segredo deixar a AsyncTask dentro de um


fragment que ca retido em memria. Neste caso, se voc girar a tela o dovvnload
vai continuar executando normalmente. O segredo do cdigo manter o atributo
task na classe para controlar se a AsyncTask j est executando ou no.
Este cdigo mostrou como fazer algumas coisas:
1. O dovvnload foi feito em uma thread com a classe AsyncTask.
2. O usurio pode girar a tela durante o dovvnload sem interromper a task.
3. O estado da tela ca salvo, pois o fragment retido em memria.
4. Foi implementado o listener Dialoglnterface.0nCancelListener no Progressialog
para permitir que ele seja cancelado. Para fechar a janela do ProgressDialog,
pressione o boto voltar do Android. Ao cancelar a janela, a AsyncTask tambm
cancelada chamando o mtodo task.cancel(true).

Nota: quando a AsyncTask cancelada com a chamada do mtodo cancel(true), o


mtodo onPostExecute() no ser chamado, no seu lugar ser chamado o mtodo
onCancelled(). Se em qualquer momento voc precisar saber se a tarefa est
cancelada, basta chamar o mtodo isCancelled().

Para ajud-lo a estudar e entender o cdigo, recomendo fazer estes dois testes e
vericar os logs do LogCat. O primeiro teste fazer o dovvnload e aguardar at
a imagem ser exibida. Neste caso, teremos os seguintes logs:
D/livroandroid: frag onCreateView() // fragment criado
D/livroandroid > DownloadTask.eecute() // task iniciada
D/livroandroid task onPreEecute() // pr-executa na UI Thread
D/livroandroid showProgress() // mostra o progress dialog
D/livroandroid task doInBackground() // faz o download em background
D/livroandroid task onPostExecute() // m do download na UI Thread
D/livroandroid setBitmap() // atualiza a imagem
D/livroandroid closeProgress() // fecha o progress dialog
O se undanad
' ' -~^SVntTask e Loader 793
(aPtuIo 31 u Threads av
_
O r g 1 o teste e iniciar o download e girar a tela antes de o download terminar
esu tado dos logs devera ser assim:
_ // fragmentum dO
D/livroandroid:
D/livroandroid: frag onCreateView()
> DownloadTask.execute() // task iniciada
D/livroandroid: task onPreExecute() // pr-executa na UI Thread
D/livroandroid: showProgress()
// mostra o progress dialog
D/livroandroid: task doInBackground() // faz o download em background
// Aqui eu girei a tela
D/livroandroid: frag onDetach()
// O fragment ser desassociado da activity
D/livroandroid: closeProgress()
// fecha o progress dialog antes de a activity
// ser destruida
// A activity e o fragment so recriados
D/livroandroid: frag onCreateView() // depois da rotao da tela, vamos recriar
// a view do fragment
D/livroandroid: DownloadTask j est executando. // como o fragment foi retido, vimos
// que o download ainda est executando
D/livroandroid: showProgress() // mostra o progress dialog novamente, pois o
// download ainda est executando
D/livroandroid task onPostExecute() // m do download
D/livroandroid: setBitmap() // atualiza a imagem (vai utilizar a nova
// view do fragment)
D/livroandroid: closeProgress() // feh 0 DFOQFGSS dl09
Neste tpico, voc aprendeu a implementar uma AsyncTask corretamente com um
Fragment e tratar os problemas de troca de orientao da tela. Isso costuma ser um
problema para muitos desenvolvedores Android, mas com este exemplo voc ter
uma boa base para construir seus aplicativos.

. - de forma serial
7I.
31.3 Executando a AsyncTask de forma serial ou paralela
da,todas as tarefas/ tasks executavam
Quando a AsyncTask foi cria
f ~ teInternamen
f ma unica thread.
(uma apos outra) 1T1 U
a AsyncTask controla essa

A .artir do
. foi
d 1,6 esse Androi
la e 0 pool de threads.
alterado
comportamento , _ _que. as
para permitir
P em aralelg No caso de um aplicativo para tablet com diversos
tasl<s executassgldoslela tela isso pode ser til, pois a task de cada fragment
d cutarenlpaf
ffagmems espa 81610 sem depender da resposta de outro. Porem, em aplica
~ is a or 6
PO e exe d m da execuo das tarefas importante, esse comportamento
goes nas qua
pode no ser o ideal.
tP)d~
` - ,. .. ..'
794 Google Android - 4' edio
or isso o Google a partir do Android 3.0 voltou ao comportamento original e
as as tarefas executam em uma unica thread serialmente. Mas pela API pode
mos escolher se dese
Jamos iniciar a AsyncTask de forma serial (padro) ou paralela.
O seguinte codigo mostra como executar a tarefa de forma serial (padro):
AsyncTask task = .._;
task.eecute(parmetros aqu);
// Isso a mesma coisa que...
task.eXeCUte0nEXeCut0r(AsyncTask.SERIAL_EXECUTOR, parmetros aqu);

E para executar a tarefa de forma paralela assim:


AsyncTask task = ...;
task.eecute0nEecutor(AsyncTask.THREAD_PO0L_EXECUTOR, parmetros aqu);

Podemos ver nestes cdigos que utilizado um Executor da linguagem Java. A


API de Executor do Java foi criada para gerenciar um pool de threads facilitando a
implementao de programao concorrente. Caso queira estudar mais detalhes
de como isso funciona internamente, pesquise sobre java Executor.

Ateno: o mtodo eecute0nEecutor(eecutor,params) foi criado no Android 3.0,


portanto s pode ser utilizado em dispositivos com API Level 11 ou superior. Se
voc executar esse cdigo em verses anteriores do Android, o aplicativo vai lanar
uma exceo em tempo de execuo. De qualquer forma, importante entender
que no Android 1.6 at o 2.x as tarefas executam em paralelo por padro, mas no
Android 3.0 ou superior as tarefas executam de forma serial por padro.

31.4 Loader
Antes de voc ler sobre loaders, gostaria de ressaltar que este tpico ligeiramente
avanado. Talvez a leitura possa ser um pouco pesada, portanto leia quando achar
necessrio.
Loaders foram criados no Android 3.0 como a forma oficial 'de executar tarefas
assncronas no Android. A interface Loader faz parte do pacote androd.content.Loader.
mas para funcionar em todas as verses do Android podemos utilizara interface
androd.support.v4.content.Loader da biblioteca de compatibilidade v4.
Um loader pode ser utilizado dentro de uma activity ou fragment e utilizad
para executar tarefas assncronas. Uma das suas principais funcionalidade C
monitorar a fonte de dados para que, em caso de mudanas, a interface da tcl
possa ser atualizada rapidamente. Essa funcionalidade no inuito utilizada um
C.
apltuh 31 ' Threads aVa"ado - AsyncTask e Loader 795

da
eressa - .
1vo o . . .
a enda
.
precisamos faz ' ' ~ - .
aPl1CaUV0S que utilizam web ser
V1C5, pois para saber se a fonte de dados mudou
pode ser im er requisioes no servidor Web. Mas existem casos em que isso
me' POI exemplo, Se o seu aplicativo mostra a lista de contatos

Mas talveza a~` -' z. .~


a hcf > Smpfe QUE o usuario alterar as informaes de algum contato, o

mudan
anteri ~ ~ A , . .
P P e ser avisado, para atualizar a lista com as informaes corretas.
Pf1HC1pal vantagem do loader e que ele sobrevive durante uma
` de conguraao do SISICITIQ, como a troca de orientaao. Como vimos
u ormente, podemos utilizar um fragment e rete-lo em memoria para solu~
clonar esse problema com a classe AsyncTask, mas um loader j faz esse gerencia
mento de forma automtica. Caso o usurio gire a tela, por exemplo, o sistema
vai simplesmente reconectar a tarefa no loader que j existe.
Eu particularmente me vejo bem satisfeito com a soluo mostrada anteriormente,
com um fragment retido em memria. No entanto, utilizar loaders a maneira
recomendada pelo Google para executar tarefas assncronas, ento vamos l!
Para utilizar um loader, precisamos implementar a interface Loader. Um loader
responsvel por executar determinada tarefa em segundo plano e retornar o
resultado. O melhor de tudo que o loader tambm pode ser desvinculado da
activity ou fragment.
Como o Loader uma interface, existem duas implementaes nativas padro da

I'a 1 *. , .
plataforma, as classes CursorLoader e AsyncTaskLoader. A classe CursorLoader faz a leitu
ra de um content provider, que utilizado para ler a agenda de contatos, dentre
outras coisas. j a classe AsyncTaskLoader implementa a interface Loader e utiliza uma
AsyncTask internamente para executar a tarefa em background, portanto veja a
importncia de termos estudado a classe AsyncTask. O Loader simplesmente delega
o trabalho para a AsyncTask!

dados ara , , . _
&ca' uma das principais funcionalidades de um loader monitorar a fonte de
ue em caso de mudanas, a interface da tela possa ser atualizada
`da1lf1entl No captulo 32 sobre provedores de conteudo, vamos utilizar a

Neste captulo,
clpsse
vamos f ~ estu _*
a CursorLoader para ler os contatos da agenda e ver essas vantagens na pratica.
_ .~+-- dar os conceitos sobre a API de Loaders.
i__ii_}____ -~ -*__

der
Para executar um 103 fe' 'utilizada a classe
LoaderManager , uma classe
a qual
da com a activity ou o fragment que est executando. O
abstrata que est aSSOC21
mo iniciar um loader:
. . ~ , Hb k);
cdigo a seguir mostra CO
9etLoaderMana9eF()-1-1tLader(1d' args ca ac
O metodo
. ~ llback)
, - der(1d,
tnttLoa
contm os seguintes parmetros:
args, CH
796 Google Android - 4 edio
int id
l
denticador nico para o loader. Se existe apenas um loader, o identi
cador pode ser O (zero).

Bundle args

Argumentos para construir o loader ou null se no for necessrio.

LoaderCallbacks callback

Implementao da interface Loaderrlanager.LoaderCallbacks que ser respons


vel por criar o loader e receber o resultado retornado pelo loader. como
se esta interface gerenciasse o ciclo de vida do loader, sendo responsvel
por cri-lo, receber todos os eventos e depois receber o resultado.
Para entendermos isso na prtica, podemos criar um loader que faz o download
de uma imagem. Veja que a classe ImagemLoader herda de AsyncTaskLoader e,
como o tipo Bitmap foi utilizado no argumento genrico, o mtodo loadInBackground( )
deve retornar um Bitmap.

Imagemloadetjava
public class ImagemLoader extends AsyncTaskLoaderBitmap {
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
public ImagemLoader(Context context) {
super(context);
}

@0verride
protected void onStartLoading() {
super.onStartLoading();
Log.d("livroandroid", "loader onStartLoading()");
// Precisa chamar o forceLoad() para executar o loader "loadInBackground"
forceLoad();
}

@0verride
public Bitmap loadInBackground() {
Log.d("livroandroid", "loader loadInBackground()");
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
L0g.e("livroandroid",e.getMessage(), e);
}
Captulo 31 1 Thfgads avan d 797
0 - AsyncTask e Loader

.A3
return bitmap;
}

oade
Ao
}

, ,' . .
chama
o loader ` ` ` ~ - ,
1 r f) metodo 9etLoaderManager().1n1tLoader(id,args,ca11back) para iniciar o
r O mewdo "5taftLad19() e chamado. Sua responsabilidade decidir se
iniciado ou nao. Podemos dizer que no metodo onStartLoading() voc
deve dar o comando para iniciar a aret af edverdade, e isso feito chamando o
metodo forceLoad().
Om
todo 1oadInBackground() executado em segundo plano por uma thread e
aqui que voce deve fazer a busca no web service ou banco de dados.

Nota: observe como a classe ImagemLoader independente da activity ou fragment. A


unica tarefa que ela faz buscar um Btmap de um endereo URL. Ento podemos
dizer que o Loader somente a classe responsvel por executar determinada tarefa
e retornar um objeto como resultado.

Mas o loader sozinho no faz nada, pois algum precisa cham-lo. Por isso utiliza
mos a classe LoaderManager e o mtodo getLoaderManager() .ntLoader(id,args,ca11back).
Neste caso, o terceiro parmetro caliback deve implementar a interface LoaderManager.

1.
LoaderCa11backs que contm estes trs mtodos:

Loader onCreateLoader(nt id, androd.os.Bund1e bundle);


Mtodo que retorna alguma implementao da interface Loader, como
CursorLoader ou AsyncTaskLoader.

void onLoadFnshed(Loader loader, D d);


Quando o loader terminar de carregar os dados, este mtodo chamado
na UI Thread para atualizar a view

vod onLoaderReset(Loader loader);

como
"o..~~
Este mtodo chamado caso o loader seja limpo (feito o reset). Para limpar
um loader, basta cl'1aIT1
d ev
dem
ar o mtodo reset(), o que signica que o loader
e eliminar os dados para economizar recursos e memria.
os dizer que um template bsico para executar um loader seria
Entao P ,d. O demonstrado a seguir. VeJa que a implementao da interface
O CO g ' Q se ks
redoeda
8 chamada. Este callback
LoaderManager.LoaderCa1.1bac __ I . deve
_ criar o
. da execucao no metodo onLoadF1n1shed()_
loader e depois rece ber a resposta
Wi una mgnn {
uam viu awratuiulquthhtu tchau. Unllkc viumn cantar.
lllkble bao sudlnstamsml {
Fins mu = iuhm.ivl|t(l.lga`z~quo_innXui,ingn. totain-. feia);
fff Erich Q inter um manta c un ia existam
viu;
gamiu-nungu-().i|ma|zr\. uu. um u&r(l\hs());
E

urina am loutillhtis uma mz|uuu~.\uuau:|=\u;{


nvfi
|lil||hf=lit||nutnuuu|lur(htid.|noQ){
`i!:uckua~pafracriroladurp|hnecessrio
FM:-Qu~iareretorara:hsse0Laer
nun uu I|qui.a1=(guu:ttviU());
E

pltz void a|.ati|lshd(lo|llrllt|\|p Iuhr. t\t|vQhh){


llthnalnpiraatregurarespustadnlonizreatalizucvieu
IE\\!cI|tn\Il'fkQi
iihstecmhdu|nloI.recahennsoBiuu:i
}
Glnfridz
plicvuld `tnQlar){
Iiuvicasuolaiarteahasidnirlifeittwrest)
}
E

Depois des: tenlpiate basico para xuc ~m~nde~r .1 idv:iz` muros msmir un*
exemplo ootrnpktn O obietivu timer 0 :Imm|mad da nnugeut mwamtnl. Ntsk
um vamos mrilimr : mesma ac:i\~ity do exemplo anterior. pois uma zpuur
zherar a dasse do fragmcm. que encapsula toda wa lgica dc d\m1\Iuad da imagem
Lembresr de que wi- pod: abrir o pmjem dv: ~x~mpln dvzsu cpmln no Amhi
Smdio para fadtar aus andas

public das ununldhugutonierrragnnt extends Frirm {


muto Pri www:
privntt lml *NVQI
Qitffi
C3PU|0 31 I Threads avanado - AsyncTask e Loader

pubs Void Cfea@0DtionsMenu(Menu menu, Menulnater inater) [


Der.onCreateOptionsMenu(nenu, inater);

} nater-"@(R.menu.menu_refresh,nenu);
@0verride

Dublic boolean on0ptionsItemSelected(Menultem item) {


f(t@W-9@W1d() == R.id.action_refresh) {
progress();
Log.d("livroandroid", "restartLoader()");
9etLoaderManager().restartLoader(0, null, new LoaderCallbacks());
}

return super.onOptionsItemSelected(item);
}

@0verride
public View onCreateView(LayoutInater inater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inater.inate(R.layout.fragment_download_imagem, container, false);
imgview = (lmageview) view.ndViewById(R.id.ing);
Log.d("livroandroid","frag onCreateView");
setHasOptionsMenu(true);
return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("livroandroid","frag onActivityCreated");
progress();
// Inicializa o loader ou reconecta a um existente
Log.d("livroandroid", "initLoader()");
getLoaderHanager().initLoader(0, null, new LoaderCallbacks());
}

p' V'_ . tInageBitmap(null); H


private void DF0Qf55() {

0 ride ,
ltg::SS pr0gressDialog.show(getActivity(), "Aguarde", "Fazendo o download... )
progress.setCancelable(true);
r(new Dialoglnterface.0nCancelListener() {
progress.setOnCancelLiSt@E

ublic void onCancel(Dal09Interface dialog) {


getL0aderManager().getLoader(9)50DL3d9();
}

});
}
300 Google Android - 4 edio
private void setBitmap(Bitmap imagen) {
Log.d("livroandroid","setBitmap");
// Esconde o progress
closeProgress();
// Atualiza a imagem
imgview.setImageBitmap(inagem);
}

private class LoaderCallbacks implenents Loaderanager.LoaderCallbacks<Bitnap {


@0verride
public LoaderBitnap> onCreateLoader(int id, Bundle args) {
Log.d("livroandroid", "onCreateLoader");
return new InagemLoader(getContet());
}

@0verride
public void onLoadFinished(Loader<Bitnap loader, Bitnap data) {
Log.d("livroandroid", onLoadFinished");
setBitmap(data);
}

@0verride
public void onLoaderReset(LoaderBitnap loader) {
Log.d("livroandroid", "onLoaderReset");
}

@Override
public void onDetach() {
super.onDetach();
Log.d("livroandroid", "frag onDetach");
closeProgress();
}

public Context getContet() {


return getActivity();
}

private void closeProgress() {


if(progress != null && progress.isShowing()) {
progress.dismiss();
progress = null;
}

Para este exemplo, vamos adicionar um boto de atualizar na action bar.


'ea 5 aVa"d0 - AsyncTask e loader 801
Captulo 31 1 Th d

/res/menu/menu_refresh.xm|
<menu xmlnszand
roid="http://schemas.android.com/apk/res/android"
f'\1S=|>r="httpz //schemas.android.com/apk/res -auto"

AI''I-,
. xmlns:tools="http://schemas.android .com/tools" tools:contet=".MainActivity" >
<lte a"dr1d3d="@+id/action_refresh"
androidzt tle="Refresh" android:icon:"@drawable/ic_action_refresh"
app:showAsAction="always" />

o executar esse codigo, o download da imagem sera feito e o resultado sera o


mesmo de antes. Novamente recomendo que voc execute o exemplo e verique
as mensagens no LogCat, que devem ser como mostradas a seguir:
D/livroandroid? frag onCreateView() // Cria a view do fragment
D/livroandroid? frag onActivityCreated() // Neste mtodo do ciclo de vida, o fragment
// sabe que o mtodo onCreate() da activity
// terminou. Aqui um bom lugar para iniciar
// o loader
D/livroandroid? initLoader() // O loader ser criado ou reconectado se
// j existir
D/livroandroid? onCreateLoader() // Se o loader no foi criado ainda, este
// mtodo deve retornar o loader
D/livroandroid? loader onStartLoading() // Dentro da classe do loader, este mtodo
// chamado para iniciar a tarefa
D/livroandroid? loader loadInBackground()
// Executa em background para buscar os dados
// e deve retornar o resultado
// Recebe o retorno do loader na UI Thread
D/livroandroid? onLoadFinished()
// Atualiza o Imageview
D/livroandroid? setBitmap()

Esses logs m ostram a execuo perfeita do loader. Agora faa o seguinte teste.
Depois de carregar a imagem na primeira vez, gire a tela para provocar uma mu
dana nas configuraes do sistema, assim sabemos que a activity e o fragment
Sero destrudos e recriados. Ao girar a tela, as mensagens do LogCat devem ser
como mostradas a Seguf
// 0 fragment ser desassociado da activity
D/livroandroid: frag onDetach() // Depois de girar a tela, cria a view do
D/livroandroid: frag onCreateView() // fragment
// O fragment sabe que o mtodo onCreate() da
D/livroandroid: frag onActivityCreated() // activity terminou
// Reconecta ao loader, pois ele j existe
initLoader()
D/livroandroid: // Recebe o retorno do loader na UI Thread
onLoadFinished()
D/livroandroid: // Atualiza o Imageview
setBitmaD()
D/livroandroid:
802 Google Android - 4 edio
Veja que o interessante deste exemplo que, depois de girar a tela, 30 Chamar O
mtodo initLoader(), o LoaderManager sabe que o loader j retornou os dados e sim
plesmente entrega o resultado no mtodo onLoadFinished(). O interessante que
no foi necessrio salvar o estado manualmente nem reter um fragment.

Nota: o mtodo initLoader(id,args,callback) vai iniciar o loader ou reconectar em


um loader j existente. Caso esse loader j tenha executado, ele simplesmente
vai entregar o resultado para a UI Thread chamando o mtodo onLoadFinished().
Caso voc queira executar o loader novamente, deve chamar o mtodo
restartLoader(id,args,callback).

Outro teste que voc deve fazer girar a tela durante o download. Neste caso, as
seguintes mensagens so impressas no LogCat. Vou comentar apenas as partes
ainda no explicadas.
D/livroandroid? frag onCreateView()
D/livroandroid? frag onActivityCreated()
D/livroandroid? initLoader()
D/livroandroid? onCreateLoader()
D/livroandroid? loader onStartLoading()
D/livroandroid? loader loadInBackground()
// Aqui eu girei a tela, durante o download
D/livroandroid: frag onDetach() // A activity e o fragnent sero destrudos
D/livroandroid: frag onCreateView() // Cria a view do fragnent novamente
D/livroandroid: frag onActivityCreated()
D/livroandroid: initLoader() // Reconecta ao loader, pois ele j existe
// Aguarda a execuo do loader
D/livroandroid: onLoadFinished() // Recebe o retorno do loader na UI Thread
D/livroandroid: setBitmap()

Note que o interessante de utilizar loaders que o LoaderManager faz o gerenciamento


do estado do loader, e inclusive pode reconectar no loader depois de uma troca
de conguraes do sistema.
Podemos ver que o mtodo getLoaderManager() .initLoader(id,args.callback) vai criar o
loader chamando o mtodo onCreateLoader(id,args) na interface de callback, mas, se
0 loader com esse identicador j existir, ele ser reutilizado. Por isso, caso voc
queira executar novamente a tarefa, preciso explicitamente no cdigo chamar
o mtodo getLoaderManager().restartLoader(id,args,callback). Neste exemplo qu
criamos adicionei um boto de atualizar na action bar conforme a gura 31.4.
que mostra o resultado:
Captulo 31 1 Thfe ads aVa"d0 - AsyncTask e Loader 803

J2
,_
~ ,.
\..J

i Lai' l
lir3'diz
n: *:`y~
tr.\
-W;,, . 1
za '
U,:','-1_
P *-.~ W; ,- l
=z:H='_z.=.:af

1 Goo Ie
z'f1,au<;
Q
11;
zf

ROID
^9"lf|U'I0||9apou1osI|Iw|m6vul1
unolldlil

1 0\/319 namllmm

Figura 31.4 - Ao de atualizar na action bar

Nota: o mtodo initLoader(id,args,callback) vai iniciar o loader ou reconectar a um


loader j existente. Se o loader j tiver executado, ele simplesmente vai entregar
o resultado para a UI Thread chamando o mtodo onLoadFinished(). Caso voc
queira executar a tarefa do loader novamente, necessrio chamar o mtodo
restartLoader(id,args,callback)

Ainda sobre a classe de loader que criamos anteriormente, vamos sobrescrever


mais alguns mtodos referentes ao ciclo de vida do loader. Altere o cdigo con
forme demonstrado a seguir e preste ateno nos comentrios.

ImagemLoader.java
public class ImagemLoader extends AsyncTaskLoader {

g ., .
private static nal String URL = "httpz//livroandroid.com.br/imgs/livro_android.png";
public ImagemLoader(Contet context) {
super(contet);
}

@Override
protected void onStartLoading() f
super.onStartLoadin9(); _
Lo .d("livroandroi d", "loader onStartLoading()");
d ara iniciar o loader ou reconectar a um Ja existente
reci
Ehamiisg hamar 0 forceLoad() para executar o loader "loadInBackground"
forceLoad() 3
}
Google Android - 4 edio

@0verride
protected void onForceLoad() {
super.onForceLoad();
// Chamado ao executar o mtodo forceLoad()
Log.d("livroandroid", "loader onForceLoad()");
}

@0verride
public Bitmap loadInBackground() {
Log.d("livroandroid", "loader loadInBackground()")
// Faz o download da imagem
Bitmap bitmap = null;
try {
bitmap = Download.downloadBitmap(URL);
} catch (Exception e) {
Log.e(livroandroid",e.getMessage(), e);
1

return bitmap;
}

@0verride
public void deliverResult(Bitmap data) {
// Executa depois do loadInBackground(). Chamado antes de enviar o resultado
// para a UI Thread
Log.d("livroandroid", "loader deliverResult(), isStarted(): " + isStarted());
if(isStarted() && !isReset()) {
super.deliverResult(data);
}

@Override
protected void onStopLoading() {
// Chamado quando o loader ser parado
// Acontece quando o mtodo onStop() chamado na activity ou fragment,
// ou se voc executou o stopLoading()
super.onStopLoading();
Log.d("livroandroid", "loader onStopLoading()");
}

@0verride
protected void onReset() {
super.onReset();
// Chamado caso o loader tenha sido cancelado pelo mtodo reset()
Log.d("livroandroid", "loader onReset()");
}

@Override
(aPU|0 31 I Threads avanado - AsyncTask e Loader 805
Dublic void onCanceled(Bitmap data) {
5UDr.onCanceled(data);

} L9~d("lVF0android", "loader onCanceled()");


@0verride

public void onContentChanged() {


super.onContentChanged();
// Chamado se a fonte de dados utilizada pelo loader foi alterada
/ este caso a interface deve ser alterada para reetir os novos dados
// Nao e utilizado muito com web services, estude o CursorLoader para entender isso
Log . d( "livroandroid", "loader onContentChanged( ) " );
l
}

Nota: este assunto avanado. O objetivo do livro no lhe proporcionar apenas


uma primeira leitura, ento espero que voc leia novamente o assunto sempre
que necessrio.

Para voc entender o que signica cada mtodo do ciclo de vida, vamos mais
uma vez mostrar na prtica. Faa o download da imagem e depois pressione a
tecla home do Android. Isso vai deixar a activity e o fragment em background.
Ao clicar na tecla home, a seguinte mensagem deve aparecer no LogCat:
D/livroandroid: loader onStopLoading()

Dessa maneira, podemos ver que o mtodo onStopLoading() est associado com o
ciclo de vida da activity/fragment, e isso muito interessante.
Agora clique no cone da aplicao na tela Home do Android para trazer a activity
novamente para o topo da pilha. Neste caso o mtodo onResune() ser chamado na
activity/ fragment, e as seguintes mensagens devem aparecer no LogCat:
D/livroandroid? loader onStartLoading()
D/livroandroid? loader loadInBackground()
D/livroandroid? loader onForceLoad()
D/livroandroid? loader deliverResult, isStarted(): true
D/livroandroid? onLoadFinished()
D/livroandroid? setBitmap()

eu e *
Ento podemos vericar que quando a activity volta a executar o loader reini
` d oIsso feito automaticamente pelo LoaderManager. Porm aqui se voc perce
ga t- mos um problema uma VZ que 0 mtodo onStartLoading() simplesmente
esta, dandg
man executar o loader porque o mtodo forceLoad() chamado. E por
806 Google Android - 4' edio
isso, conforme os logs, a tarefa do loader executou novamente e uma consulta foi
realizada na internet para buscar a imagem.
@0verride
protected void onStartLoading() {
super.onStartLoading();
Log.d("livroandroid", "loader onStartLoading()");
forceLoad();
l

Acabamos de aprender um detalhe importante sobre o funcionamento dos loaders


Para solucionar esse problema, temos de manualmente salvar o estado do loader
e decidir se ele deve recarregar ou no os dados, de forma parecida com o que
j estudamos sobre salvar o estado de uma activity ou fragment. O cdigo-fonte
a seguir mostra como fazer isso. Veja que 0 objeto Bitmap armazenado em um
atributo da classe InagenLoader, e no mtodo onStartLoading() tomada a deciso
sobre executar a tarefa ou no.
public class InagenLoader extends AsyncTaskLoader {
private static nal String URL = "httpz//livroandroid.com.br/ings/livro_android.png";
private Bitnap bitmap;
public InagemLoader(Contet context) {
super(context);
}

@0verride
protected void onStartLoading() {
super.onStartLoading();
if(bitnap == null) {
Log.d("livroandroid", "loader onStartLoading() forceLoad()");
// Executa o loader
forceLoad();
} else {
Log.d("livroandroid", "loader onStartLoading() deliverResult()");
// J contm os dados, apenas atualiza a interface l
deliverResult(bitnap);
}

lsso vai resolver o problema de recarregar o loader quando a activity que est em
segundo plano voltar a ocupar o topo da pilha. Portanto, faa 0 teste novamente:
depois do download, clique no boto Home para sair da activity Depois clique
no cone da aplicao para abrir a activity novamente. Desta vez podemos ver as
seguintes mensagens no LogCat:
e 1'
Captulo 31 u Threads avan 307

m
D
ado - AsyncTask e Loader

/livroandroid' loader onStartLoad'


D/lvroa d ` I . 1"9() >> deliverResult()
n roid. loader deliverResult, isStarted(): true
D/livroandroid: loader super.deliverResult()

chamand ' '


Conforme_essas
g, pomensagens
emos ver de que
lo d o loader no executou nova
nf, po1s o Bitnap esta em memoria, portanto ele apenas entregou o resultado
O 0 metodo deliverResult(). Outro detalhe importante do loader como
cancela-lo. Se voc vericar o cdigo do fragment que zemos, foi adicionado o
listener para cancelar o Pro ressD` l
do loader. g ia og, e neste caso chamamos o mtodo stopLoading( )

private void progress() {


imgview. setInageBitmap( null);
progress = ProgressDialog.show(getActivity(), "Aguarde", "Fazendo o download...");
prog ress . setCancelable( true);
progress.set0nCancelListener(new Dialoglnterface.0nCancelListener() {
@0verride
public void onCancel(DialogInterface dialog) {
getLoaderManager( ) .getLoader(0) . stopLoading( );
}

});
}

Para testar o cancelamento da execuo do loader, faa o download e enquanto


o ProgressDialog estiver aberto clique no boto Voltar. Feito isso, podemos ver que o
mtodo stopLoading() chamado, o que coloca o loader em modo de parado/stop.

d.
Vale ressaltar que existe o mtodo isStarted() do loader, que pode colntrolar Jus
tamente este estado para vericar se o loader est iniciado ou se esta parado.
problema que, mesmo depois de parar o loader, o mtodo deliverlQesult() sera
Chamado quando o mtodo loadInBackground( ) terminar a tarefa. Isso slgnlca que,
mes mo cancelando a tarefa, o resultado ainda entregue para a UI Thread atu
a1'1zar
view a novamente temos de controlar isso manualmente, portanto
Ento
altere este cdigo na classe ImageL0ad@F2

@0verride
public void deliverResult(BtaP data) {
// E xecu a do loadInBackground(). Chamado antes de enviar o resultado
t depois

Lgg,
H " 'LV`O
pgal UI Tgzd.. "loader deliverResult, isStarted(): " + isstrte-d());
iv
if(isStarted() W sneseto) {
L0g.d(|| 1_ roandrod.. "loader super.deliverResult(): " + bitmap);
super.deliverResult(d3ta)5
}

}
808 Google Android - 4= edio
Com isso, antes de entregar o resultado do loader, feita a vericao se ele est
executando com o mtodo sStarted() e tambm vericado se ele no foi inva
lidado com o mtodo sReset(). Depois de adicionar estas linhas de cdigo na
classe Imageloader, teste 0 cancelar no ProgressDialog.

Outro detalhe importante que um loader pode ser invalidado, ou como se


diz no ingls fazer o reset. Isso feito chamando o mtodo reset(). Neste caso, o
mtodo onReset() do loader deve eliminar os objetos de memria, pois no sero
mais utilizados.
@0verride
protected void onReset() {
super.onReset();
// Chamado caso o loader tenha sido cancelado pelo mtodo reset()
Log.d("livroandroid", "loader onReset()");
this.bitmap = null;
}

Ento, fazendo um resumo, podemos dizer que os mtodos onStartLoading() e


onStopLoadng() so chamados quando o loader conectado e desconectado da
activity/ fragment, pois ele ca atrelado ao ciclo de vida da activity
O mtodo onReset() chamado se o mtodo reset() for chamado e significa que
este loader deve eliminar os dados de memria. O conceito parecido como o
onDestroy() da activity Para exemplicar, se voc zer o download da imagem e
depois pressionar o boto voltar do Android, a activity ser destruda, e neste
caso as seguintes mensagens devem aparecer no LogCat:
D/livroandrod: loader onStopLoading()
D/lvroandrod: onLoaderReset()
D/livroandrod: loader onReset()
D/livroandroid: frag onDetach()

Nesses logs, podemos ver que ao destruir a activity o loader tambm invalidado,
pois o mtodo onReset() foi chamado. Nesse momento, a aplicao deve limpar os
recursos para liberar a memria.

31.5 Opinio do autor


Vimos que a interface Loader contm duas classes que a implementam: CursorLoader
e AsyncTaskLoader. Nos exemplos deste captulo, estudamos a classe AsyncTaskLoader.
j a classe CursorLoader vamos estudar no captulo 32, sobre content provider.
Captuh 31 ' Thfeads aVa"ado - AsyncTask e Loader 809
Eu particularmente
conhecimentos avandemaisdVimos
acho loaders complicados - f uei qnecess
no`
a os para domina-los de verdade.
lano eu ` ~ - ,
Nvamenre, para consultar We
b services ou executar qualquer tarefa em segundo
P O Q8ftlCularmente utilizo aquele metodo startTask( task), conforme zemos
no aplicativo dos carros.mp E si lf
es e unciona. E exatamente assim que fao no
meu dia a dia.

Se for necessario manter o estado de uma thread que est executando durante a
f0ta<a0 da fla, creio que voc consiga resolver isso com um fragment retido em
memoria, conforme explicado neste captulo.
Mas, enm, cabe a voc decidir se utilizar loaders ou no em seus aplicativos.

31.6 Links teis

Neste captulo, estudamos alguns assuntos mais avanados sobre como executar
tarefas em segundo plano e gerenciar o estado da aplicao.
Loaders foram criados para funcionar de forma integrada ao ciclo de vida da
aplicao, porm voc precisa entender o seu funcionamento para tirar total pro
veito e usufruir do potencial dessa API. j no caso da AsyncTask, ela simplesmente
fornece uma biblioteca simples de threads que facilita algumas tarefas, mas voc
deve controlar o estado da aplicao manualmente.
Ambas as APIs funcionam e so muito utilizadas, ento cabe a voc estud-las e
vericar qual vai atender as necessidades do seu projeto. Separei alguns links da
documentao ocial para complementar seus estudos.
Processes and Threads

http '//developex android. com/guide/components/processes-andthreads.html


Loaders

http.//developer android.com/guide/components/loaders.html
* cAPruLo 32
i Agenda de contatos e
`'. content provider

Content providers, ou simplesmente provedores de contedo, fornecem uma


interface padro para que aplicaes possam expor e consultar informaes. Um
exemplo de provedor de contedo a API da agenda de contatos.
Neste captulo, aprenderemos a consultar os contatos da agenda, assim como
criar nosso prprio provedor de contedo no projeto dos carros.

32.1 Por que utilizar a classe ContentProvider"provedor de contedo"


O Android permite armazenar informaes de diversas formas diferentes utili
zando banco de dados, arquivos e o sistema de preferncias. Contudo, geralmente
essas informaes cam salvas dentro do pacote da aplicao e somente a aplicao
que criou o banco de dados ou o arquivo pode ter acesso s informaes. Ou seja,
a informao privada da aplicao e ningum pode acess-la.
Se for necessrio expor essas informaes para outras aplicaes, preciso criar
um provedor de contedo (content provider), que representado pela classe
android.content.ContentProvder. Um provedor de contedo tem as seguintes carac
tersticas:

1. Permite que aplicaes compartilhcm informaes com outras aplicaes.


2. Apresenta uma interface padro de consulta e insero.
3. Encapsula o acesso ao SQLite ou a outra fonte de dados.
muito importante entender que voc s precisar criar um provedor de contedo
caso tenha necessidade de expor informaes para outras aplicaes. Se voc prC
cisar de um banco de dados privado que seja acessado apenas pelo seu aplicativo.
utilize um banco com SQLite, como j mostramos.

810
carmos um , . .
(apt"l 32 ' A9enda de contatos e content provider 311
Talvez a melhor forma de entender O que um provedor de contedo seja brin
POUCO com algum provedor de conteudo nativo; por isso, vamos
comear este captulo construind o exemplos para lermos os contatos da agenda.

O Android tem uma srie de provedores de contedos nativos, como por exemplo
consultar os contatos da agenda, visualizar os arquivos, imagens e vdeos dispo
nveis no celular. Independentemente de se esse provedor nativo do Android ou
se criado pelo desenvolvedor, a forma como a aplicao se comunica com um
provedor de contedo padronizada, ou seja, apresenta uma interface padro.
Para ter uma ideia sobre com o que vamos brincar neste captulo, a figura 32.1
mostra o projeto de exemplo He|Io(ontatos executando no emulador. No aplicativo
ser possvel inserir na agenda os contatos Mickey; Pateta e Donald, para depois
list-los na lista. Ao clicar em algum contato, ele ser mostrado com o aplicativo
padro do Android.

_
,`.,.l 1
DonaldI
M ,~4
'Q MlCkEy
, E
.
'z '.

l 11
li
YIY ~
1.

B y
PBGIH

1 q K. 999999999 E
Y

I q Home

Figura 32.1 - Lista de contatos.

32 2 URI - Immutable URI reference


n`unto com um provedor de contedo para
d od net Ur utilizada em CO J
A dasfse an F l itfg Qu informao com uma string nica e um formato amigvel.
ldenucar um reg - - d os contatos cadastrados na agenda do celular,
P exemp ' p " to/ OS
10 ara identificar t t / Para con
_ _ ;//co1n.androzd.contacts selecionar
ac s . somen
t1l1zar a URI content
Podemos u _ __ 'I' ar a URI content://comandroid.contacts/contacts/1.
te O con tato com 1d-1, basta un IZ
812 Google Android - 4' edio
Com uma U RI, possivel disparar uma consulta (query) para o Android, que
por sua vez retornar um objeto do tipo androd.database.Cursor, o qual permite ler
o resultado da consulta. j estudamos a classe Cursor no captulo 18, sobre persis
tncia e SQLite, portanto ela no novidade. Este captulo ser moleza.
Veja este exemplo: o trecho de cdigo demonstrado a seguir pode ser utilizado para
buscar todos os contatos cadastrados na agenda. Utilizando o cursor retornado,
possvel percorrer todos os contatos para ler informaes.
Url ur = Url.parse("content://com.androd.contacts/contacts/");
// query( Url, projecton, selection, selectionArgs, sort0rder)
Cursor cursor = getContentResolver().query(uri., null, null, null, null);

Se estamos interessados apenas no contato de id=1, podemos usar o seguinte


trecho de cdigo, que dessa vez retornar um cursor com apenas um registro:
Uri. url = Uri.parse("content://com.androd.contacts/contacts/1"); // i.d=1
Cursor cursor = getContentResolver().query(uri., null, null, null, null);

Simples, no ? A ideia muito interessante, e o mais impressionante que tudo


isso segue um padro bem denido. No importa se voc est utilizando um
provedor de contedo nativo, um provedor customizado desenvolvido por voc
ou de um terceiro, a maneira de buscar os registros a mesma. Neste captulo,
criaremos um provedor de contedo para carros. Dessa forma, podemos dizer
que a URI ser content://br:comlivroandroid.carros/carros/. Qualquer aplicao que
conhea essa URI poder acessar a lista de carros com o seguinte trecho de cdigo:
Uri url = Uri.parse("content://br.cormlvroandrod.carros/carro/");
Cursor cursor = getContentResolver().query(ur, null, null, null, null);

A classe androd . net.Uri representa um endereo em geral e neste caso ela identi ca
o endereo utilizado para fazer a consulta no provedor de contedo.

32.3 Exemplos de provedores de contedo nativos


Conforme dito anteriortnente, o Android tem uma srie de provedores de contedo
nativos que permitem consultar os contatos da agenda, visualizar os arquivos.
imagens e vdeos disponveis no celular, por exemplo.
A seguir, veja uma lista de algumas URls que podem ser utilizadas para ler infor
maes pblicas do Android:
content://com.android.contacts/contacts/
URI que retorna todos os contatos cadastrados na agenda do celular.
URI ue retor
n-.
(apt"l 32 ' ^9enda de contatos e content provider 813
content://comandroid.contacts/contacts/1

d q 3 IPCUHS O Contato com o 1d=1, se o mesmo estiver cadas


tra o na agenda do celular.

content://media/interna!/images/media
URI que retorna todas as imagens disponveis no celular.
content://media/external/images/media

URI que retorna todas as imagens disponveis no carto de memria.


Para que no seja necessrio decorar cada string URI, possvel usar as classes
ut1l1tar1as que fazem parte do provedor desejado. A lista a seguir exibe as constantes
que podem ser usadas no lugar de cada string descrita anteriormente:

ContactsContract.Contacts.CONTENT_URI

Constante equivalente a content://com.android.contacts/contacts/_

MediaStore . Images .Media . INTERNAL_CONTENT_URI

Constante equivalente a content://media/intemal/images/media.

MediaStore . Images .Media . EXTERNAL_CONTENT_URI

Constante equivalente a content://media/extema!/images/media.


O seguinte cdigo mostra como buscar todos os contatos da agenda utilizando
a ccnlstarite ContactsContract.Contacts.CONTENT_URI.

Cursor c = getContentResolver().query(ContactsContract C0ntaCtS.CONTENT_URI, null,


null, null, null);
Entretanto para selecionar um nico registro, como por exemplo selecionar o contato
de id-1
_ a ,necessrio utilizar o mtodo withAppendedId(uri,id), o qual recebe a URI

r='
padro 6 O id do registro. O cdigo a seguir mostra como selecionar o contato de id= 1.
// Equivalente a "content://com.android.contacts/contacts/1"
U i uri ContentUris withAppendedId(ContactsContract.Contacts.CONTENT_URI, 1);
Cursor Q = getContentResolver().query(uri, null, null, null, null);
1 droid provider ContactsContract representa o provedor de COf1fClO da
A C asse an l uma Elaclasse
conteminterna chamada Contacts que dene a
g d' ' ` ara tos
a enda
consulta
de contatos.
Porisponiveis
e as colunas
URI de consulta de conta
exemplo p-
enda de contatos e obter o cursor, podemos ler o nome
depois de consultar a ag
do contato desta format
814 Google Android - 4 edio
Cursor c = . . . ;
String nome = c.getString(c.getColumnInde0rThrow(ContactsContract.Contacts.DISPLAY_NAME));

32.4 Lendo os contatos da agenda


Para demonstrar o bsico sobre a utilizao de provedores de contedo, vamos
criar um exemplo para ler os contatos da agenda. Crie um projeto no Android
Studio chamado He|Io(ontatos ou abra o projeto de exemplo do livro.
O projeto que vamos fazer vai ler e escrever na agenda de contatos, portanto
declare as seguintes permisses no arquivo de manifesto:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.wRITE_CONTACTS" />

O cdigo a seguir mostra como fazer um rpido exemplo de como ler os contatos
da agenda, portanto s para aquecermos.

ListaContatosPrintActivity.java
public class ListaContatosPrintActivity extends AppCompatActivity {
private static nal String TAG = "livroandroid";
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_contatos);
printContatos();
}

private void printContatos() {


// Uri: "contentz//com.android.contacts/contacts"
Uri contatos = ContactsContract.Contacts.CONTENT_URI;
Cursor cursor = getContentResolver().query(contatos, null,
ContactsContract.Contacts.HAS_PHONE_NUMBER +" = 1 ", null, null);
int count = cursor.getCount();
Log.i(TAG,"Foram encontrados "+count+" contatos.");
while(cursor.moveToNet()){
String nome = cursor.getString(cursor.getColumnInde(
ContactsContract.Contacts.DISPLAY_NAME));
Log.i(TAG,"Nome: " + nome);
}

// importante fechar o cursor no final


cursor.close();
}

}
u b . .
con 1 ,, H A _
No cdi o esto
. . .
Captulo 32 . A end d 815
9 a e contatos e content provider

d_ __ g USCando todos os contatos que tem telefone, pois adicionei a


ce1u1a21gLenta_cts.HAS_PHONE_NUMBER + = 1 .Se voce quiser, execute o cdigo no seu
IUSIFG alguns contatos de exemplo no emulador para testar.

?dresultaddo
El OI` CU 8 deste. exemplo pode ser visto nos logs do LogCat. No caso do emu
1C1OI'l1 I`S COTIEIOS, qu fOI'8ITl lITlpI`SSOS COI'lfOI`ITl O SpI'2lClO.
NF/D(24G): Foram encontrados trs contatos
NF0/1D(240): Nome: Mickey
INFO/ID(240): Nome: Pateta
INFO/ID(240): None: Donald

Conforme visto neste exemplo, a classe android.database.Cursor permite que as


informaoes retornadas sejam lidas da mesma forma que zemos quando estu
damos o SQLite. Para recuperar o cursor dos contatos, foi utilizado o mtodo
getContentResolver().query(uri, projecao, selecao, selecaoArgs, orderBy),que recebe os
seguintes argumentos:
Argumento Descrio
Uri uri URI para fazer a consulta; o nico parmetro obrigatrio.
String [] projecao Array com os nomes das colunas para selecionar. Se for informado
um array nulo, todas as colunas sero retornadas.
String selecao String com uma clusula SQLNHERE opcional para buscar alguma
informao especca.
String[] selecaoArgs Array com os argumentos utilizados pelo parmetro selecao, se
necessario.
String orderBy String com um SQL para fazer o order by.
Veja que o mtodo getContentResolver() retorna um objeto do tipo ContentResolver, que
basicamente contm os mtodos para acessar um provedor de contedo. Neste caso,

e,
oe..
utilizamos 0 mtodo query(...), mas existem outros como insert(...), update(...)
e delete( . . .). Na prtica, qu ando formos implementar nossa classe de provedor
~ ~ de contedo.
d contedo vamos implementar todos esses mtodos, pois o ContentResolver faz
a ponte entre a apl1caaO 0 Pmvedor

__: J ~.O Heuoontatos de exemplo deste captulo, coloquei um boto na


Dic? ng pr ira adicionar alguns contatos na agenda. Isso facilita os testes deste
acnon ar P O ontatos cadastrados so o Mickey, Pateta e Donald.
Captulo--_,._.-J
nO IT1Ulad0f~ 5 C A S _ ...__ ..-e -..--
816 Google Andrdid - 4 edio
32.5 Como ler todos os telefones e a foto de um contato
Conforme demonstrado anteriormente, para ler um nome de um contato uti
lizada a constante contaccs.o1sPLAv_NAME.

O problema que na classe Contacts no existe nenhuma constante para buscar


os telefones ou a foto. Isso porque internamente essas informaes so salvas em
tabelas diferentes e consequentemente so outras classes que encapsulam esse
acesso. Dessa forma, para ler todos os telefones de um contato, pode ser utilizado
um mtodo como este:
private List getFones(Contet context, long id) {
List fones = new ArrayList();
Cursor cursor = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.ConnonDataKinds.Phone.CONTACT_ID + " = " + id,
null, null);
try {
while (cursor.noveToNext()) {
int coluna = cursor.getColunnInde(ContactsContract.CommonDataKinds.Phone.NUHBER);
String fone = cursor.getString(coluna);
fones.add(fone);
}

} nally {
cursor.close();

return fones;
}

Note que a classe utilizada a ContactsContract.Com|nonDataKinds.Phone.CONTENT_URI, que


por sua vez contm a URI que internamente guarda a tabela do banco de dados
com os telefones. Observe que como parmetro da busca informado o id do
contato, com a constante CONTACT_ID.

Seguindo o mesmo raciocnio, podemos ler a foto de um contato, fazendo uma


busca no provedor de contedo especfico que guarda essa informao. Mas para
ler uma foto existe um mtodo utilitrio dentro da classe Contacts que facilita a
leitura. O trecho de cdigo a seguir demonstra como ler a foto e obter um objeto
Bitmap como retorno.

private Bitnap getFoto(Contet context, long id) {


// Cria a URI para o id fornecido
Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
ContentResolver contentResolver = context.getContentResolver();
Captulo 32 1 A Qenda de contatos e content provider 817
InputStrean i H = ContactsContract.Contacts.
openContactPho toInputStrean(contentResolver, uri);
if ( != null) {
Bitnap foto = BitnapFactory.decodeStrean(in);
return foto;
}

return null;
}

Agora que j sabemos como1er o nome do contato, seus telefones e a sua foto
vamos ]untar os conceltos nas classes Contato e Agenda:

Contatojava
public class Contato {
public long id;
public String none;
public Bitnap foto;
public List fones;
// Retorna a URI deste contato, ex.: "contentz//com.android.contacts/contacts/1"
public Uri getUri() {
Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
return uri;
}

// L a foto de un contato
public Bitnap getFoto(Contet context) {
// Cria a URI para o id fornecido
Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
ContentResolver contentResolver = context.getContentResolver();

if (in != null) { _
InputStream in = Contacts.openContactPhotoInputStream(contentResolver, uri);

Btmap foto = BitnapFactory.decodeStrean(in);


return foto;
}
return null;
}

public void show(Contet context) {


Uri uriContato = 9@'CU()3
cont ex
t tartActivity(new
.S Intent(Intent.ACTION_VIEw,uriContato));
}

}
(Fm Agendajava
public class Agenda {
/I content://con.androld.contacts/contacts
private static nal Uri URI = Contacts.CONTENT_URI;
private static nal String TAG = "agenda;
private Context context;
public Agenda(Contet context) {
this.contet = context;
}

public List getContatos() {


// Recupera o cursor para percorrer a lista de contatos
Cursor c = getCursorContatos();
return getContatos(c);
}

public Cursor getCursorContatos() {


return context.getContentResolver().query(URI, null, ContactsContract.Contacts.
HAS_PHONE_NUMBER + " = 1 ", null, Contacts.DISPLAY_NAHE);
}

public List getContatos(Cursor cursor) {


List contatos = new ArrayListContato();
try {
while (cursor.moveToNet()) {
Contato a = getContato(cursor);
contatos.add(a);
1

} nally {
// Fecha o cursor
cursor.close();

I'tU|'I'l contatos;
}

public Contato getContatoById(Lon9 id) {


Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
Cursor cursor = context.getContentResolver().query(uri, null, has_phone_number=1
null, null);
if (cursor.noveToNet()) {
Contato c = getContato(cursor);
return c;
1

return null;
.c.nome
=1; '
Captulo 32 1 Agenda de C ontatos e content provider 819
public Ct3t 9etContato(Cursor cu rsor) {
COWGO C = new Contato();
// Id e none
.l0l .dz
St ` z = none; '
C ig 1 _d Cursor-9etL"9(CUFS0r.9etColunnIndex0rThrow(Contacts. ID));

ring none cursor.getString(cursor.getColunnInde0rThrow(Contacts.DISPLAY NAME))

// Fone

boolean temFone = "1".equals(cursor.getString(cursor.getColunnInde0rThrow(


Contacts.HAS_PHONE_NUMBER))) ;
if (temFone) {
List fones = getFones(id);
c.fones = fones;
}

return c;
}

// Busca os telefones na tabela 'ContactsContract.ConmonDataKinds.Phone'


private List getFones(long id) {
List fones = new ArrayList();
Cursor cursor = context.getContentResolver().query(
ContactsContract.ConmonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.ConnonDataKinds.Phone.CONTACT_ID + " = " + id,
null, null);
try {
while (cursor.moveToNet()) {
int coluna = cursor.getColunnInde(ContactsContract.ConmonDataKinds.
Ph0ne.NUMBER);
String fone = cursor.getString(coluna);
fones.add(fone);
}

} nally {
cursor.close();
}
return fones;

ublc boolean add(0ntato(String nome, String telefone, int ingFoto) {

ista de operaes para executar no provedor de contedo


try ? L_
ArrayL5t<ContentProvider0P@fat"> perat" :
new ArrayLS t<ContentProviderOPerat0>();
int backRefIndex = 0;
// Adiciona o contat0
Google Android - 4= edio

operation.add(
ContentProvider0peration.newlnsert(ContactsContract.RawContacts.CONTENT_URI).
withValue(ContactsContract.Rawtontacts.ACCOUNT_TYPE, null).
withValue(Contactstontract.RawContacts.ACCOUNT_NAME, null).build());
// Nome do contato
operation.add(
ContentProvider0peration.newlnsert(ContactsContract.Data.CONTENT_URI).
withValueBackReference(Contactstontract.Data.RAH_CONTACT_ID, backRefIndex).
withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredNane.CONTENT_ITEH_TYPE).
withValue(ContactsContract.ComnonDataKinds.StructuredName.DISPLAY_NAME,
nome).build());
// Associa o telefone do tipo "Home" ao contato
operation.add(
ContentProvider0peration.newInsert(
ContactsContract.Data.CONTENT_URI).
withValueBackReference(ContactsContract.Data.RAH_CONTACT_ID,backRefIndex).
withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.ConmonDataKinds.Phone.CONTENT_ITEM_TYPE).
withValue(ContactsContract.ComnonDataKinds.Phone.NUHBER, telefone).
withValue(ContactsContract.ConmonDataKinds.Phone.TYPE,
ContactsContract.ConmonDataKinds.Phone.TYPE_HOME).build());
// Foto do contato
Bitmap fotoBitmap = BitmapFactory.decodeResource(context.getResources(), imgFoto);
ByteArray0utputStream stream = new ByteArrayOutputStream();
fotoBitnap.compress(Bitmap.CompressFormat.PNG, 75, stream);
operation.add(
ContentProvider0peration.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAH_CONTACT_ID, backRefIndex)
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(Contactstontract.CommonDataKinds.Photo.PHOT0, stream.toByteArray())
.build());
// Executa a lista de operaes em batch
context.getContentResolver().applyBatch(
ContactsContract.AUTHORITY, operation);
return true;
} catch (Exception e) {
Log.d(TAG, "Erro ao inserir contato: " + e.getMessage(), e);
}

return false;
(I
ap't"l 32 ' ^9enda de contatos e content provider 821
public int delete(Contato c) {
return delete(c.id);
}

u''z-.
public int delete(Long id) {
fl fl C"te"tUS-W1th^Drended1d(c<ntcts.coNrENr_uR1, id);
int count = contet.getCont tR lver().delete(uri,null,null);
en eso
return count;
}

co 1 _ ' ` f ~ - , .
metodos da classe Agenda esto comentados, portanto faa uma boa leitura no
go Utilizar essa classe e simples, o seguinte codigo mostra como ler os contatos:
AQGHB 8 = new Agenda(this); // this = context
List contatos = a.getContatos();
j para ler um contato pelo id, basta este cdigo:
Agenda a = new Agenda(this); // this = context
Contato c = a.getContatoById(id);

Como eu disse antes na dica, o projeto de exemplo HelloContatos est adicionando


trs contatos na agenda para testar. A seguir, podemos ver como a classe Agenda que
criamos facilita esse trabalho. Mais uma vez, veja que gosto de criar classes utilitrias
para tudo, pois acredito que seja mais simples uliz-las em vez de chamar a API nativa.

}.
public boolean on0ptionsItemSelected(Menultem item) {
if (item.getItemId() == R.id.action_add) {
Agenda a = new Agenda(this);
a.addContato("Mickey","999999999",R.drawable.nickey);
a.addContato("Pateta","888888888",R.drawable.pateta);
a.addContato("Donald","777777777",R.drawable.donald);
Toast.nakeText(this,"Contatos adicionados com sucesso.",Toast.LENGTH_SHORT).show();
return true;

return super.on0ptionsItemSelected(item);
}

- l im rimiu os nomes dos


32.6 Mostrando os contatos em um Listview

nta o - . .
- exemplo
Para melhorar o primeiro p que zemos, o qua p
LO Cat vamos crl
ar um layout com ^ I a fim de mostrar a foto
um Listview
contatos no 8 t ga lista Ao clicar no contato, voce podera fazer algo com ele, mas
e nome do co S disparar uma intent para visualizar os detalhes do contato.
l'1O IIOSSO C215 0 vamo
822 Google Android - 4= edio
Vamos comear pelo layout da activity o qual deve conter o Listview.

/res/layout/activity_Iista__tontatos.xmI
<Re1ativeLayout . . .>
<ListView android:id="@+id/listview"
android:1ayout_width="natch_parent" android:1ayout_height="natch_parent" />
</Re1ativeLayout

No cdigo da activity vamos ler todos os contatos e mostr-los na lista.

Lista(ontatosPrintActivity.java
public class ListaContatosActivity extends AppCompatActivity
inpienents Adapterview.0nItemC1ickListener {
@0verride
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1ista_contatos);
nai Listview iistview = (Listview)
ndViewById(br.com.1ivroandroid.contatos.R.id.1istView);
iistview.setOnItemC1ickListener(this);
// Lista os contatos
nal Agenda a = new Agenda(this);
nal List contatos = a.getContatos();
nai ContatoAdapter adapter = new ContatoAdapter(getBaseContext(), contatos);
Iistview.setAdapter(adapter);
}

@0verride
public void onItemCiick(AdapterView<?> parent, View view, int position, long id) {
Agenda a = new Agenda(this);
Contato c = a.getContatoById(id);
Toast.makeTet(this, "Nonez " + c.none, Toast.LENGTH_SHORT).show();
c.show(this); // Mostra o contato disparando uma intent
}

Para o cdigo compilar, precisamos criar o adapter que vai mostrar o contato.

Ci /res/layout/adapter_tontato.xm|

<LinearLayout mins:android="http://schenas.android.com/apk/res/android"
android:iayout_width="match_parent"
android:iayout_height="?android:attr/iistPreferredItemHeight"
android:gravity="center_vertica1" android:orientation="horizonta1">
Captulo 32 I Agenda de contatos e content provider

<QuickContactBadge android:id="@+dmg
- ' I outyh`ht="72d
<TextView "
elg D />
a<1f<1=1av<ut width-"7zp" anrz la

a"drld=d="@+d/tNome" android:layout_marginLeft="4dp"
adrld313V0U_W1dth="wrap_content" android:layout_height="wrap_content"
android:gravty="enter" />

ContatoAdapter.java
public class ContatoAdapter extends BaseAdapter {
private nal Layoutlnater inater;
private Context context;
private List contatos;
public ContatoAdapter(Context context, List contatos) {
this.context = context;
this.contatos = contatos;
this.inater = Layoutlnater.from(context);
}
@Override
public int getCount() {
return contatos != null ? contatos.size() : 0;
}

.d .
@0verride
public Object getItem(int position) {
return contatos.get(position);
}

@Override
public long getItenId(int position) {
Contato c = contatos.get(D0St0);
return c.id;

pu
}

ic i '
@0r1Vew getVew(nt position View convertview, VtewGroup parent) {
tew v = ~ t to, arent, falS); _
V' ew inater inate(br.com.livroandroid.contatos.R.layout.

Tex tView
adapteicon one view
(T;tVew) = ndViewById(br.com.livroandroid.contatos.R.id.tNome)
'
- ' f8dViewById(br.com.livroandroid.
. ' = (QuickContactBadge) vtew.|n
QuickContactBadge 19
contatos.R.id.in9) _ _
Qontato Q = contatos.get(position);

.--Ft
Uri uriContato = C~9efUf1()5
tNome.setText(C-e)5 // Nome
img.assignContactUrt(Uf1c"tat)' /l O O
324 Google Android - 4 edio
Picasso . with(context) . load(uri.Contato) . nto(ing);
return view'|
}

O interessante deste exemplo que, ein vez de utilizar um Inagevew para mostrar
a foto do contato, estamos utilizando a view QuickContactBadge no layout que fa
cilita atribuir a foto do contato por meio de uma URI e ainda mostra o contato
automaticamente ao clicar na foto. Dessa forma, no precisamos nos preocupar
ein como obter o Bitmap da foto do contato, pois o QuckContactBadge faz isso auto
maticamente, com uma ajudazinha da biblioteca Picasso, claro.

i.`
Ao executar o cdigo, o resultado deve ser como a figura 32.2. O lado direito da
gura mostra os detalhes do contato logo depois de seleciona-lo.

E:
A c lrixiti `
i

i lv- i `
, -
. \`.i. tsy
6 .
^
._(..)Q mw LX, .-~_ i

999999999 D
*C
[1 in.

Figura 32.2 - Lista de *r1m!s.

32.7 Utilizando um (ursorAdapter


A implementao que fizemos tem um problema, pois ela carrega todos os conta
tos de uma vez s em memria; assim, caso existam muitos contatos na agenda,
a aplicao pode demorar em exibir a lista.
Outro probleina que temos que eu li a agenda de contatos diretamente na Ul
Thread, sendo que o correto seria abrir uma thread ou async task para isso. Mas
para simplificar o cdigo deixei assim mesmo. lzm outro tpico, vamos praticar
a API de Loaders e resolver esse problema de forma siinples.
C.

que )
,, isso. e aruim ume
. S C
I _soIvez
CC) '
ap't"| 32 ' ^9enda de contatos e content provider 825
Mas vamos voltar ao problema inicial de t ' '
_ . mo
em mem orla eduma _ pico,

1vr`- ,' .z, dlda


.
C0 que era carregar toda a lista
Sena faz _ , ma forma de resolver o problema
o usuar
er consultas 81113
IO
pa d HS que fossem buscando mais contatos ` `
demonstrar n lzesse o scroll na lista. Porem, isso seria um pouco complexo para

dados de um . 0, entao vamos deixar para lajustamente para solucionar esse


problema existe a classe C ursorAdapter, que facilita criar um adapter plugado nos
cursor, e uma de suas subclasses mais famosas a SmpleCursorAdapter.
O codigo a seguir mostra como exibir o s contatos em um Lstvtew de forma simples
utilizando o S1mpleCursorAdapter. Nos parmetros, note que precisamos passar o
COYIXQ C) layout do adapter, o cursor, os campos que devem ser lidos do cursor
e os identicadores das views que esto no layout.
Llstvew lstvlew = . . .;
Cursor cursor = getContentResolver().query(. . .);
nal SnpleCursorAdapter adapter = new SmpleCursorAdapter(
getBaseContet(), // contexto
R.layout.adapter_contato, // layout
cursor, // cursor
new Strng[]{ContactsContract.Contacts.DISPLAY_NAME}, // campos
new nt[]{R.d.tNone}, // campos no layout
G);
llstvtew.setAdapter(adapter);

Se voc quiser testar esse cdigo, que vontade. Porm, no gosto de fazer dessa
forma, ento vamos logo ao prximo exemplo. Um dos motivos pelos quais no
gosto de fazer assim porque temos de car mapeando tudo utilizando arrays.
Outra questo que nesse caso no tem como mostrar a foto do contato, pois,
como j estudamos, ela ca em uma tabela diferente da do contato.
Uma soluo simples criar nossa prpria subclasse de CursorAdapter, a qual vamos
chamar de ContatoCursorAdapter.

ContatoCursorAdapter.java
r extends CursorAdapter {
public class ContatoCursorAdapte
public ContatoCursorAd8P@f(C"tet Context' Cursor C) {
5uper(contet, C, @)5
}

@0veprld? wVew((0ntet context, Cursor cursor, Vewroup parent) {


publlc VIEW ne text).lnate(R.layout.adapter_contato, parent,false);
View View z LayoutInater.from(C"
return view;
}
826 Google Anafii-4- edio
@0ver ride

public void bindView(View view, Context context, Cursor cursor) {


Textview tNone = (Textview) view.ndViewById(R.id.tNome);
QuickContactBadge img = (QuickContactBadge) view.ndViewById(R.id.ing);
int idxld = cursor.getColunnIndex(ContactsContract.Contacts._ID);
Long id = cursor.getLong(idxId);
Contato c = new Agenda(context).getContatoById(id);
Uri uriContato = c.getUri();
tNone.setText(c.none);
img.assignContactUri(uriContato);
Picasso.with(context).load(uriContato).into(img);
}

Basicamente, esse adapter precisa implementar dois mtodos: o mtodo nem/iew(. . .)


c o mtodo bindView( . . . ). O mtodo newView( . . .) precisa inar a view utilizada pelo
adapter e o mtodo bindView(. . .) vai preencher as views com cursor recebido no
parmetro, sendo que o cursor j estar posicionado no ndice do contato corrente
na lista. A vantagem de utilizar a classe CursorAdapter ou uma de suas subclasses
que ela faz uma utilizao eficiente do cursor e otimiza a rolagem. Os resultados
sero lidos rapidamente, assim como a rolagem da lista ser uida.
Para utilizar esse novo adapter, atualize o cdigo da activity conforme demonstrado
a seguir. Desta vez, estamos utilizando a classe Agenda apenas para obter o cursor
de contatos, logo depois estamos criando o adapter com esse cursor.

ListaContatosPrintActivity.java
public class ListaContatosActivity extends AppConpatActivity inplenents Adapterview.
0nItemClickListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_contatos);
nal Listview listview = (Listview)
ndViewById(br.con.livroandroid.contatos.R.id.listView);
listview.set0nItemClickListener(this);
// Lista os contatos
Agenda a = new Agenda(this);
Cursor cursor = a.getCursorContatos();
nal ContatoCursorAdapter adapter = new ContatoCursorAdapter(this,cursor);
listview.setAdapter(adapter);
}

@0verride
Captu|o32 A ' gend d 827
e contatos e content provider
public void onItenClick '
(AdapterView<?> pa.-em Vie ~ - . . .

}
exe - , . _ ,
} // Mesmo cdigo de antes aqui , W new, mt posltwn' long Id) {

resultado ser be ' ' . .


Ao executar esse
mplo. novamente a lista de contatos sera exibida, porem o
m mais eficiente do que o exemplo anterior, pois o CursorAdapter
faz um melhor uso dos recurso s e memria; portanto, no caso de provedores de
conteudo, recomendado utiliz-lo.

32.8 Utilizando um Cursorloader


Junto com a API de Loaders que j estudamos no livro, foi criada a classe
CursorLoader, que e uma subclasse de AsyncTaskLoader, que por sua vez uma sub
classe de Loader. Se precisar relembrar o conceito de loader, leia o captulo anterior.
A classe CursorLoader uma implementao de Loader que facilita a leitura dos da
dos de um cursor preenchendo automaticamente a view do adapter. A principal
vantagem que utilizando loaders tudo feito em segundo plano, de forma que
a interface da tela no fica travada. Logo, sobre todos os exemplos que zemos
anteriormente, podemos dizer que estavam errados, pois acessaram a agenda de
contatos na UI Thread.] a API de Loaders resolve isso de forma simples e eciente.
O cdigo a seguir mostra como mostrar a lista de contatos utilizando o
CursorLoader. Veja que o Listview utiliza o mesmo adapter que zemos no tpico
anterior, porm foi informado um cursor nulo no construtor. Quando o mtodo
getSupportLoaderManager() .initLoader( . . .) chamado, o loader iniciado. No mtodo
onCreateLoader( . . .) o loader criado, e aqui que retornamos o CursorLoader. j o
mtodo onLoadFinished( . . .) chamado quando o loader terminou de carregar os
dados, ou seja, o cursorj _ foi lido. Neste caso, O mtodo adapter.swapCursor(cursor)
asggia Q adapter com o cursor para mostrar os dados na lista.

Lista(ontatosPrintActivity.java

imp e
ublic class ListaContatosActivity @
tends AppCompatActivity

p ' l ments Adapterview OnItemClickListener, Loaderanager.LoaderCallbacks<Cursor {


Private static final String TAG = "uVrand'-old ;
private Listview listview;
private CursorAdapter adapter?
@Override
t (Bundle savedInstanceState) {
protected void onCrea e
. tivity_ is a_con a tos
super.onCreate(SV@dI"5ta"CeStateii t t );
setContentView(R-1HYUt-ac
328 aoogienamia-4~za;a
listview = (Listview) ndViewById(R.id.listView);
listview.setOnItemClickListener(this);
// Congura o Listview com um adapter (cursor nulo)
adapter = new ContatoCursorAdapter(this,null);
listview.setAdapter(adapter);
// Inicia o loader
getSupportLoaderManager().initLoader(1, null, this);
}

@0verride
public void onItemClick(AdapterView<? parent, View view, int position, long id) {
// Igual antes ...
}

@0verride
public Loader<Cursor onCreateLoader(int id, Bundle args) {
// Retorna o CursorLoader para carregar os contatos
Uri uritontatos = ContactsContract.Contacts.CONTENT_URI;
return new CursorLoader(getApplicationContet(),uriContatos,
null,ContactsContract.Contacts.HAS_PHONE_NUMBER +" = 1 ",null,
ContactsContract.Contacts.DISPLAY_NAME);
}

@0verride
public void onLoadFinished(LoaderCursor loader, Cursor cursor) {
// Carrega o adapter com o cursor
adapter.swapCursor(cursor);
1

@0verride
public void onLoaderReset(Loader<Cursor loader) {
// Limpa o adapter
adapter.swapCursor(null);
}

Ao executar esse cdigo, novamente a lista com os contatos ser exibida, mas
desta vez utilizamos loaders, deixando a aplicao mais ecaz.

32.9 Monitorando a fonte de dados com um Ioader


Na explicao do captulo 31, sobre AsyncTask e Loader, eu defendi a ideia de que
loaders eram ligeiramente complexos, pelo menos na minha humilde opinio.
Mas como o CursorLoader j est pronto, uma grande verdade que ele realmente
facilita nossa vida. Agora vamos entender o porqu. Com o prximo exemplo.
voc tambm entender o real significado de utilizar um loader,
CaPU| 32 I Agenda de contatos e content provider 829

IS uti
' '-..
Um dos ma iores benefcios da API de L d

ue
ue e oa ers monitorar a fonte de dados para
q m 02150 de mudanas, a interface d a tela possa ser atualizada rapidamente.
Para entend
er esse conceito na prtica si a
na t a g OS SgL1iI'l[S p2ISSOS. Mostre OS contatos
.leonio fizemos anteriormente com qualquer um dos exemplos, menos o
qd 1221 oaders. Clique no contato para visualizar a tela de detalhes, depois
e 1te o contato e exclua-o da agenda. Depois de excluir o contato, o Android vai
voltar para a lista de contatos , porm o contato ainda estar l! Voc precisar
fechar a tela e abri-la novamente para atualizar a lista, ou teramos de imple
mentar algum procedimento de a tualizao (refresh). Algo parecido com o que
zemos no projeto de carros, quando exclumos um carro do banco de dados e
foi necessrio atualizar a lista.

Agora refaa o mesmo teste com esta ltima implementao que utiliza loaders.
Logo depois de excluir o contato e voltar lista, automaticamente a lista estar
atualizada, pois internamente o loader percebeu a mudana e informou ao adapter
que era necessrio atualizar a lista. Tudo acontece de forma transparente e simples.
Enfim, essa uma das principais vantagens da API de Loaders. Para fechar este
tpico e apenas revisando, seguem as principais caractersticas e benefcios de
um loader:

.
1. Encapsula a AsyncTask, portanto executa com uma thread em segundo plano.
2. Facilita a atualizao da view na UI Thread, pois a prpria AsyncTask encap
sula o uso de um handler.
3 Conforme j estudamos, o loader est atrelado ao ciclo de vida da activity
e seus fragments.
4 O loader mantm o estado mesmo durante a troca de conguraes de
sistema, como a troca de orientaao.

.no/ 1- '~'
. Conform acabamos de aprender, um loader consegue monitorar a fonte
de caso
5 em dados para C1Ue
de mudanas ' da tela possa ser
a interface

6.
e nO P1'0J . - ~
atualizada rapidamente.
S ' to dos carros tivssemos um provedor de contedo com um
l der resolveramos de forma simples o problema que tivemos ao excluir
oa , rojeto Na poca tivemos que fazer um articio tecnico para
um carrO 1_ a depgis de excluir um carro do banco de dados. Se voce tem
atualizr prender como um provedor de conteudo e criado, continue
curiosi a 1T1a
lendo o prximo tpico.
33 Google Android - 4 edio
32.10 Criando um provedor de contedo customizado
Ag0ra que ja sabemos o que um provedor de contedo, vamos criar um no
projeto dos carros. O objetivo expor informaes para outras aplicaes, pois
do contrario no vejo muita necessidade de criar um provedor de contedo de
vido a sua complexidade. A no ser que voc queira resolver de forma elegante o
problema 6 da lista do tpico anterior.
Ento, mos obra! Para comear, vamos primeiro revisar a sintaxe de uma URI.
Sabemos que para buscar os contatos da agenda so usadas as seguintes URIs:
' content://comandroid.contacts/contacts/
content://com.android.contacts/contacts/1

De forma similar, o provedor de contedo dos carros ter as seguintes URIs:


content://bxcom.Iivroandroid.carros/carros
content://bxcomlivroandroid.carros/carros/1

Observe que a sintaxe da URI segue o padro content://authority/path/id, e na lista


a seguir podemos vericar a explicao sobre cada parte:
Parte Descrio
prex Incio padro, devendo ser sempre content: //.
authority Nome nico que identifica um provedor de contedo. recomendado
que seja uma string com o pacote completo, para evitar duplicatas. A
mesma string do authority declarada no arquivo AndroidManifest.xml,
no atributo androdzauthorites.
path Caminho que o provedor de contedo usa para determinar o tipo do
contedo que est sendo requisitado. No exemplo que ser criado, o
caminho para acessar o contedo /carros. O caminho pode inclusive
conter subcategorias, como /carros/marca/modelo/, desde que faa sentido
para sua aplicao.
record id Esta parte da URI opcional e deve ser utilizada quando necessrio
selecionar apenas um nico registro. O id do registro, que corresponde
coluna _id do banco de dados, fornecido nesse caso.

Algo importante sobre o provedor de contedo a segunda parte, chama


da de authority, pois quando formos declarar o provedor de contedo no
AndroidManiest.xml precisamos inform-la, mas veremos isso no prximo tpico.
CaPtulo 32 I Agenda de conta tos e content provider 831
32.11 Classe (ontentProvider
Che ou o r .
do 8 ande momento. cnaremos uma classe que ser o provedor de conte
e carros, sendo assim vamos continua
r o desenvolvimento do nosso projeto
Crie um a classe Carro
Provider conforme demonstrado a seguir.
CarroProvder.java
public class CarroProvider extends ContentProvider {
@Override

public String getType(Uri uri) {


return null;
}

@Override
public boolean onCreate() {
return false;
}

@0verride
public Cursor query(Uri uri, String[] projection, String selection,String[]
selectionArgs, String sortOrder) {
return null;
}

@Override
public Uri insert(Uri uri, Contentvalues values) {
return null;

@Override _ _ _
}

O
} 'd , .
public int update(Uri uri, Contentvalues values, String selection,String[]
selectionArgs) {
return 0;

pu ic '
@ glrlt de1et@(Uri uri String selection, String[] selectionArgs) {
return 0;
}

Antes de implementar os mt0 dos, segue a explicao de cada um:


332 Google Android - 4 edio
lVll0glQ_g___ Descrio
String getType(Uri uri) Deve retornar o mime type para a URI informada. O valor
retornado deve iniciar com vnd.android.cursor.item para um
nico registro, ou vnd.android.cursor.dir/ para uma lista de
registros. No exemplo que ser construdo para os carros,
os tipos sero vnd.android.cursor.item/vnd.google.carros e
vnd.android.cursor.dir/vnd.google.carros, respectivamente.
boolean onCreate() Mtodo chamado quando o provedor de contedo criado.
Deve retornar true se a inicializao foi realizada com sucesso,
caso contrrio, false.
Cursor query(Uri uri, String[] projecao, String selecao, String[] selecaoArgs, String orderBy)
Mtodo usado para executar uma consulta, recebendo uma
srie de parmetros. O mtodo retorna um objeto Cursor que
utilizado para percorrer os resultados.
Uri insert(Uri uri, Contentvalues valores)
Mtodo utilizado para inserir um novo registro. O objeto
Contentvalues um tipo de HashMap que armazena os nomes
das colunas e seus respectivos valores para insero.
int update(Uri uri, Contentvalues valores, String selecao,String[] selecaoArgs)
Mtodo utilizado para atualizar um registro. A URI fornecida
deve ter o caminho completo para atualizar, incluindo o id do
registro, por exemplo: content://com.android.contacts/contacts/1.
int delete(Uri uri, String selecao, String[] selecaoArgs)
Mtodo utilizado para excluir um registro. A URI fornecida
deve ter o caminho completo para excluir, incluindo o id, por
exemplo: con tent://comandroid. contacts/contacts/1 _

Depois que a classe criada, necessrio configurar a tag no arquivo


AndroidManiest.xml.

AndroidManifest.xmI

<provider
android:name=".CarroContentProvider"
android:authorities="@string/provider" android:eported="true" />

Para o cdigo compilar, vamos criar o recurso @string/provider; mas, em vez de usar
0 arquivo stringsxml padro, utilize o arquivo strings_cong.xmI, pois assim temos
separadas as strings referentes s conguraes.
(.
aP'tUl 32 I Agenda de contatos e content provider 833
/res/vaIues/strings_cong.xm|
<?ml version="1_@" e ncoding="utf-8"?>

<strin n =" ' - _


9 ane PI'0V1der">br.con.l1.vroandroid.carros/string>
<string name="A
PI_KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn0oTQ

A lista a seguir explica o signicado


cadea ditem
` d a tag que adiciona
mos no arquivo de manifesto.

O atributo androidznane recebe o nome da classe do tipo ContentProvider, da


mesma forma que declaramos activities, receivers e services.
O atributo androidzauthorities deve ser nico e representa o pacote da URI
do provedor de contedo.

O atributo androidzexported indica que o provedor de contedo pode ser


acessado por outras aplicaes, ou seja, voc est expondo os dados do seu
aplicativo. Na verdade, esse o objetivo de um provedor de contedo.
O atributo android:enabled opcional e indica se o provedor de contedo
est ativado ou no. O padro obviamente true.

Nota: podemos dizer que agora voc j conhece o quarteto fantstico do Android.
As classes Activity, BroadcastReceiver, Service e ContentProvider so todas cadastradas
no arquivo de manifesto.

Agora vamos nalizar a implementao da classe CarroProvider. No cdigo vamos

car .
deletar o trabalho de persistncia para a classe CarroDB que j existe no projeto dos
ros Lembre se de que no necessariamente o provedor de contedo precisa
P ersistir os dados no SQLite Pois isso no importa'

CarroProvider.java
Ubl glass CarroContentProvider extends ContentProvider {
P // Classe que contm bm de ddS d "
private CarroDB db;
// Colunas para selecionar. . I
Private static HashMaP<5t9* Strmg> C0 unas;

. - - 1 ' t CARRO _ =
Private static nal int CARRO :Oii Z.
Prlvate Staticvalidar
na asI?expressoes
' e ularesF9
sobre a URI
// Utilizada Para
Google Android - 4 edio

private Uriatcher uriCarro;


// Para criar a URI: content://br.con.livroandroid.carros/carros
private static String getAuthority() {
return "br.con.livroandroid.carros";
}

public static nal class Carros inplenents BaseColunns {


// No pode instanciar esta Classe
private Carros() { }
// content://br.com.livroandroid.carros/carros
public static nal Uri CONTENT_URI = Uri.parse("contentz//"+getAuthority()+"/carros");

// Hime Type para todos os carros


public static nal String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.carros";
// Mine Type para um nico carro
public static nal String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.carros";
// Ordenao default para inserir no order by
public static nal String DEFAULT_SORT_ORDER = "_id ASC";
public static nal String NOME = "none";
public static nal String DESC = "desc";
public static nal String URL_INFO = "url_info";
public static nal String URL_FOTO = "url_foto";
public static nal String URL_VIDEO = "url_video";
public static nal String LATITUDE = "latitude";
public static nal String LONGITUDE = "longitude";
public static nal String TIPO = "tipo";
// Mtodo que constri uma URI para um carro especico, com o seu id
// A URI no formato "contentz//br.livro.android.provider.carro/carros/id"
public static Uri getUriId(long id) {
// Adiciona o id na URI default do /carros I
Uri uriCarro = ContentUris.withAppendedId(Carros.CONTENT_URI, id);
return uriCarro;
}

@Override
public boolean onCreate() {
// Uri Matcher para fazer as expresses regulares
uriCarro = new UriMatcher(UriMatcher.NO_MATCH);
// content://br.livro.android.provider.carro/carros
uriCarro.addURI(getAuthority(), "carros", CARROS);
// content://br.livro.android.provider.carro/carros/id
uriCarro.addURI(getAuthority(), "carros/#", CARROS_ID);
// Colunas para selecionar
colunas = new HashMap<String, String>();
Capt"| 32 ' Agenda de contatos e content provide;
colunas .put(
Carros._ID, Carros._ID);
colunas. put(
Carros.NOME, Carros.NOME);
colunas. put(
Carros.DESC, Carros.DESC);
colunas. put
[Carros.URL_INFO, Carros.URL_INFO);
colunas .put(
Carros.URL_FOT0, Carros.URL_FOT0);
colunas .put(
Carros.URL_VIDEO, Carros.URL VIDEO);
colunas. put
[Carros.LATITUDE, Carros.LATITUDE);
colunas. put(
Carros.LONGITUDE, Carros.LONGITUDE)
1
colunas .put(
Carros.TIPO, Carros.TIPO);
db = new CarroDB(getContet());
return true;
}
@0verride
// Retorna o MIME type correto
public String getType(Uri uri) {
switch (uriCarro.match(uri)) {
case CARROS:

return Carros.CONTENT_TYPE;
case CARROS_ID:
return Carros.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgunentException("URI desconhecida: " + uri)
}

@Override
public Uri insert(Uri uri, Contentvalues initialvalues) {
// Valida se a URI de /carros
if (uriCarro.natch(uri) != CARROS) {
throw new IllegalArgunentEception("URI desconhecida: " + uri)
}
Contentvalues values;
if (initialvalues != null) {
values = new Contentvalues(initialvalues);
} else {
Values z new ContentValues();
}

long id = db.inset(values);
if (id > 0) {
// Inseriu
Uri urCarr0 = Carro5.getUriId()5
R 01ver().notifyChange(uriCarro, null)
getContet().getContent eS
// Retorn 3 a URI com o id do carro inserido
return uriCarro;
Google Android - 4' edio

throw new SQLException("Falhou ao inserir " + uri);


}

@0verride
public Cursor query(Uri uri, String[] projection, String selection, String[]
selectionArgs, String sort0rder) {
// Classe utilitria para criar queries no SQLite
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
if(projection == null) {
projection = new String[] {"_id",Carros.NOME,Carros.DESC,Carros.URL_INFO,
Carros.URL_FOTO,Carros.URL_VIDEO,Carros_LATITUDE,Carros.LONGITUDE,Carros.TIPO};
}

// Congura a tabela para busca e a projeo


switch (uriCarro.match(uri)) {
Case CARROS:
builder.setTables("carro");
builder.setProjectionMap(colunas);
break;
case CARROS_ID:
builder.setTables("carro");
builder.setProjectionMap(colunas);
// where _id=?
builder.appendwhere(Carros._ID + "=" + uri.getPathSegnents().get(1));
break;
default:
throw new IllegalArgumentException("URI desconhecida: " + uri);
}

// Ordenao
String orderBy;
if (TetUtils.isEmpty(sort0rder)) {
orderBy = Carros.DEFAULT_SORT_0RDER;
} else {
orderBy = sort0rder;
}

I/ Query
SQLiteDatabase db = this.db.getReadableDatabase();
Cursor c = builder.query(db, projection, selection, selectionArgs, null, null, orderBy);
C

// Notica o content provider


c.setNoticationUri(getContet().getContentResolver(), uri)
return C;
}

@0verride
Captulo 32 . A
Qenda de contatos e content provide;
public int update(Uri uri, Contentvalues Values, String where, String[] whereArgs) {
int count;
switch (uriCarro.natch( uri)) {
case CARROS:

count = db.update(values, where, whereArgs);


break;
case CARROS_ID:
// id do carro
String id = Ur_ getPathSegnents().get(1);

// Atualiza o where se informado com o "_id=?" para atualizar


String whereFinal = Carr05,_ID + "=" + id + (!TetUtils.isEnpty(where)
? " AND (" + where + ')' z "")
count = db.update(values, whereFinal, whereArgs);
break;
default:

} throw new IllegalArgunentEception("URI desconhecida: " + uri);


// Notica o content pr ovider
getContet().getContent Resolver().notifyChange(uri, null);
return count;
}

@0verride
public int delete(Uri uri, String where, String[] whereArgs) {
int count;
switch (uriCarro.natch(uri)) {
case CARROS:
count = db.delete(where, whereArgs);
break;
case CARROS_ID:
// id do carro
String id = ur. getPathSegnents().get(1);
// Atualiza o wh ere se informado com o "_id=?" para atualizar
String whereFina 1 = Carros._ID + "=" + id + (!TextUtils.isEnpty(where)
2 " AND (" + where + ')' 1 "");
count = db.delet e(whereFinal, whereArgs);
break;
default:
mentEception("URI desconhecida: " + uri);
throw new IllegalAF9U
}
1 r().notifyChange(uri, null);
9etContext()-9tC"te"tRe5 ve
return count;
}

}
833 Google Android - 4 edio
O codigo extenso, mas no se assuste. Leia-o com calma e atentamente, pois
basicamente foram implementados os mtodos query(. . . ), insert( . . . ), update( . . _)
e delete( . . .), delegando a lgica para a classe CarroDB.
Os mtodos da classe ContentProvider sempre recebem a URL que em nosso exemplo ser
content://bncomlivroandroid. carros/carros ou content://bxcomlivroandroid.carros/carros/id
caso algum id seja fornecido para selecionar um carro em especco. Conforme

88P
a URI recebida, podemos descobrir se a informao que precisa ser retornada
uma lista de todos os carros ou apenas um carro especfico. Para isso, a classe
android.content.UriHatcher utilizada. Observe que quase todos os mtodos usam
essa classe para vericar o tipo do registro a ser retornado.
Repare que em al uns locais do cdi o referenciada a arte authority da URI,
pois apenas para relembrar a URI composta das seguintes partes:
content://authority/path/id
A parte authority da URI deve ser cadastrada exatamente igual ao atributo
androidzauthorities no arquivo de manifesto.
</span></span> <span class='ocr_word' id='word_1_179' title="bbox 800 1851 974 1893"><span class='xocr_word' id='xword_1_179' title="x_wconf -2">Content</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1005 1849 1202 1893"><span class='xocr_word' id='xword_1_180' title="x_wconf -4">Provider</span></span> <span class='ocr_word' id='word_1_181' title="bbox 1238 1865 1304 1888"><span class='xocr_word' id='xword_1_181' title="x_wconf -1">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 672 1933 897 1987"><span class='ocr_word' id='word_1_182' title="bbox 672 1933 897 1987"><span class='xocr_word' id='xword_1_182' title="x_wconf -2"><provider</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 764 2017 1667 2062"><span class='ocr_word' id='word_1_183' title="bbox 764 2017 1667 2062"><span class='xocr_word' id='xword_1_183' title="x_wconf -3">android:name=".CarroContentProvider"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_21' title="bbox 763 2099 2651 2154"><span class='ocr_word' id='word_1_184' title="bbox 763 2101 1972 2147"><span class='xocr_word' id='xword_1_184' title="x_wconf -3">android:authorities="br.con.livroandroid.carros"</span></span> <span class='ocr_word' id='word_1_185' title="bbox 2003 2100 2572 2154"><span class='xocr_word' id='xword_1_185' title="x_wconf -2">android:exported="true"</span></span> <span class='ocr_word' id='word_1_186' title="bbox 2606 2099 2651 2153"><span class='xocr_word' id='xword_1_186' title="x_wconf 0">/></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 582 2373 1398 2448">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_22' title="bbox 582 2374 1398 2448"><span class='ocr_word' id='word_1_187' title="bbox 582 2379 748 2448"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">32.12</span></span> <span class='ocr_word' id='word_1_188' title="bbox 767 2375 947 2446"><span class='xocr_word' id='xword_1_188' title="x_wconf -2">Classe</span></span> <span class='ocr_word' id='word_1_189' title="bbox 966 2374 1196 2446"><span class='xocr_word' id='xword_1_189' title="x_wconf -3">esttica</span></span> <span class='ocr_word' id='word_1_190' title="bbox 1216 2375 1398 2445"><span class='xocr_word' id='xword_1_190' title="x_wconf -1">Carros</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 582 2510 2898 2758">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_23' title="bbox 582 2515 2897 2591"><span class='ocr_word' id='word_1_191' title="bbox 582 2520 719 2591"><span class='xocr_word' id='xword_1_191' title="x_wconf -1">Algo</span></span> <span class='ocr_word' id='word_1_192' title="bbox 751 2525 1081 2590"><span class='xocr_word' id='xword_1_192' title="x_wconf -1">importante</span></span> <span class='ocr_word' id='word_1_193' title="bbox 1111 2518 1266 2572"><span class='xocr_word' id='xword_1_193' title="x_wconf -1">sobre</span></span> <span class='ocr_word' id='word_1_194' title="bbox 1298 2538 1390 2571"><span class='xocr_word' id='xword_1_194' title="x_wconf -1">um</span></span> <span class='ocr_word' id='word_1_195' title="bbox 1422 2517 1680 2586"><span class='xocr_word' id='xword_1_195' title="x_wconf -1">provedor</span></span> <span class='ocr_word' id='word_1_196' title="bbox 1709 2516 1774 2570"><span class='xocr_word' id='xword_1_196' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_197' title="bbox 1805 2516 2074 2570"><span class='xocr_word' id='xword_1_197' title="x_wconf -1">contedo</span></span> <span class='ocr_word' id='word_1_198' title="bbox 2106 2520 2132 2570"><span class='xocr_word' id='xword_1_198' title="x_wconf -1"></span></span> <span class='ocr_word' id='word_1_199' title="bbox 2163 2536 2267 2586"><span class='xocr_word' id='xword_1_199' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_200' title="bbox 2298 2515 2375 2569"><span class='xocr_word' id='xword_1_200' title="x_wconf -1">ele</span></span> <span class='ocr_word' id='word_1_201' title="bbox 2405 2534 2609 2584"><span class='xocr_word' id='xword_1_201' title="x_wconf -2">sempre</span></span> <span class='ocr_word' id='word_1_202' title="bbox 2640 2530 2742 2567"><span class='xocr_word' id='xword_1_202' title="x_wconf -2">tem</span></span> <span class='ocr_word' id='word_1_203' title="bbox 2775 2531 2897 2565"><span class='xocr_word' id='xword_1_203' title="x_wconf -1">uma</span></span></span>
<span class='ocr_line' id='line_1_24' title="bbox 583 2603 2897 2680"><span class='ocr_word' id='word_1_204' title="bbox 583 2612 750 2667"><span class='xocr_word' id='xword_1_204' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_205' title="bbox 776 2616 981 2665"><span class='xocr_word' id='xword_1_205' title="x_wconf -2">interna</span></span> <span class='ocr_word' id='word_1_206' title="bbox 1007 2630 1113 2680"><span class='xocr_word' id='xword_1_206' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_207' title="bbox 1140 2625 1434 2679"><span class='xocr_word' id='xword_1_207' title="x_wconf -1">representa</span></span> <span class='ocr_word' id='word_1_208' title="bbox 1461 2628 1516 2661"><span class='xocr_word' id='xword_1_208' title="x_wconf -1">as</span></span> <span class='ocr_word' id='word_1_209' title="bbox 1541 2607 1762 2661"><span class='xocr_word' id='xword_1_209' title="x_wconf -1">colunas</span></span> <span class='ocr_word' id='word_1_210' title="bbox 1787 2627 1892 2677"><span class='xocr_word' id='xword_1_210' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_211' title="bbox 1916 2627 1972 2661"><span class='xocr_word' id='xword_1_211' title="x_wconf -2">as</span></span> <span class='ocr_word' id='word_1_212' title="bbox 1998 2607 2288 2676"><span class='xocr_word' id='xword_1_212' title="x_wconf -3">aplicaes</span></span> <span class='ocr_word' id='word_1_213' title="bbox 2314 2606 2511 2675"><span class='xocr_word' id='xword_1_213' title="x_wconf -1">podem</span></span> <span class='ocr_word' id='word_1_214' title="bbox 2538 2603 2805 2658"><span class='xocr_word' id='xword_1_214' title="x_wconf -1">consultar</span></span> <span class='ocr_word' id='word_1_215' title="bbox 2827 2621 2897 2655"><span class='xocr_word' id='xword_1_215' title="x_wconf 0">ou</span></span></span>
<span class='ocr_line' id='line_1_25' title="bbox 584 2701 1065 2758"><span class='ocr_word' id='word_1_216' title="bbox 584 2708 776 2758"><span class='xocr_word' id='xword_1_216' title="x_wconf -2">inserir</span></span> <span class='ocr_word' id='word_1_217' title="bbox 796 2722 858 2756"><span class='xocr_word' id='xword_1_217' title="x_wconf -1">os</span></span> <span class='ocr_word' id='word_1_218' title="bbox 880 2701 1065 2755"><span class='xocr_word' id='xword_1_218' title="x_wconf -1">dados.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 582 2817 2899 2994">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_26' title="bbox 585 2818 2898 2888"><span class='ocr_word' id='word_1_219' title="bbox 585 2835 673 2885"><span class='xocr_word' id='xword_1_219' title="x_wconf -2">No</span></span> <span class='ocr_word' id='word_1_220' title="bbox 695 2849 821 2884"><span class='xocr_word' id='xword_1_220' title="x_wconf -1">caso</span></span> <span class='ocr_word' id='word_1_221' title="bbox 842 2828 943 2882"><span class='xocr_word' id='xword_1_221' title="x_wconf -2">dos</span></span> <span class='ocr_word' id='word_1_222' title="bbox 964 2843 1224 2888"><span class='xocr_word' id='xword_1_222' title="x_wconf -1">contatos,</span></span> <span class='ocr_word' id='word_1_223' title="bbox 1244 2842 1352 2879"><span class='xocr_word' id='xword_1_223' title="x_wconf -2">esta</span></span> <span class='ocr_word' id='word_1_224' title="bbox 1374 2824 1538 2878"><span class='xocr_word' id='xword_1_224' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_225' title="bbox 1560 2829 1761 2877"><span class='xocr_word' id='xword_1_225' title="x_wconf -1">interna</span></span> <span class='ocr_word' id='word_1_226' title="bbox 1783 2844 1868 2877"><span class='xocr_word' id='xword_1_226' title="x_wconf -1">era</span></span> <span class='ocr_word' id='word_1_227' title="bbox 1890 2844 1916 2877"><span class='xocr_word' id='xword_1_227' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_228' title="bbox 1938 2832 2609 2882"><span class='xocr_word' id='xword_1_228' title="x_wconf -2">ContactsContract.Contacts,</span></span> <span class='ocr_word' id='word_1_229' title="bbox 2628 2818 2898 2873"><span class='xocr_word' id='xword_1_229' title="x_wconf -2">conforme</span></span></span>
<span class='ocr_line' id='line_1_27' title="bbox 583 2915 1558 2994"><span class='ocr_word' id='word_1_230' title="bbox 583 2922 854 2994"><span class='xocr_word' id='xword_1_230' title="x_wconf -1">podemos</span></span> <span class='ocr_word' id='word_1_231' title="bbox 878 2918 1165 2973"><span class='xocr_word' id='xword_1_231' title="x_wconf -2">relembrar</span></span> <span class='ocr_word' id='word_1_232' title="bbox 1186 2933 1329 2970"><span class='xocr_word' id='xword_1_232' title="x_wconf -1">neste</span></span> <span class='ocr_word' id='word_1_233' title="bbox 1351 2915 1558 2984"><span class='xocr_word' id='xword_1_233' title="x_wconf -1">cdigo:</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 673 3054 2862 3199">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_28' title="bbox 673 3064 1122 3107"><span class='ocr_word' id='word_1_234' title="bbox 673 3064 1122 3107"><span class='xocr_word' id='xword_1_234' title="x_wconf -1">Cursorc=...;</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_29' title="bbox 675 3127 2862 3198"><span class='ocr_word' id='word_1_235' title="bbox 675 3145 817 3198"><span class='xocr_word' id='xword_1_235' title="x_wconf -3">String</span></span> <span class='ocr_word' id='word_1_236' title="bbox 846 3155 941 3188"><span class='xocr_word' id='xword_1_236' title="x_wconf -3">nome</span></span> <span class='ocr_word' id='word_1_237' title="bbox 968 3159 990 3177"><span class='xocr_word' id='xword_1_237' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_238' title="bbox 1018 3127 2862 3194"><span class='xocr_word' id='xword_1_238' title="x_wconf -3">c.getString(c.getColumnIndex0rThrow(ContactsContract.Contacts.DISPLAY_NAHE));</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 583 3246 2898 3426">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_30' title="bbox 583 3246 2897 3319"><span class='ocr_word' id='word_1_239' title="bbox 583 3269 707 3319"><span class='xocr_word' id='xword_1_239' title="x_wconf -2">Esta</span></span> <span class='ocr_word' id='word_1_240' title="bbox 730 3262 897 3317"><span class='xocr_word' id='xword_1_240' title="x_wconf -2">classe</span></span> <span class='ocr_word' id='word_1_241' title="bbox 920 3264 1124 3313"><span class='xocr_word' id='xword_1_241' title="x_wconf -1">interna</span></span> <span class='ocr_word' id='word_1_242' title="bbox 1147 3256 1276 3310"><span class='xocr_word' id='xword_1_242' title="x_wconf -1">deve</span></span> <span class='ocr_word' id='word_1_243' title="bbox 1297 3274 1378 3308"><span class='xocr_word' id='xword_1_243' title="x_wconf -1">ser</span></span> <span class='ocr_word' id='word_1_244' title="bbox 1399 3273 1523 3306"><span class='xocr_word' id='xword_1_244' title="x_wconf -1">uma</span></span> <span class='ocr_word' id='word_1_245' title="bbox 1547 3252 1816 3306"><span class='xocr_word' id='xword_1_245' title="x_wconf -1">subclasse</span></span> <span class='ocr_word' id='word_1_246' title="bbox 1838 3251 1904 3305"><span class='xocr_word' id='xword_1_246' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_247' title="bbox 1925 3253 2671 3312"><span class='xocr_word' id='xword_1_247' title="x_wconf -3">android.provider.BaseColumns.</span></span> <span class='ocr_word' id='word_1_248' title="bbox 2692 3248 2753 3298"><span class='xocr_word' id='xword_1_248' title="x_wconf -2">Se</span></span> <span class='ocr_word' id='word_1_249' title="bbox 2774 3246 2897 3296"><span class='xocr_word' id='xword_1_249' title="x_wconf -2">voc</span></span></span>
<span class='ocr_line' id='line_1_31' title="bbox 584 3340 2897 3426"><span class='ocr_word' id='word_1_250' title="bbox 584 3374 794 3426"><span class='xocr_word' id='xword_1_250' title="x_wconf -2">reparar</span></span> <span class='ocr_word' id='word_1_251' title="bbox 811 3372 883 3406"><span class='xocr_word' id='xword_1_251' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_252' title="bbox 902 3349 1094 3419"><span class='xocr_word' id='xword_1_252' title="x_wconf -1">cdigo</span></span> <span class='ocr_word' id='word_1_253' title="bbox 1113 3347 1180 3402"><span class='xocr_word' id='xword_1_253' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_254' title="bbox 1199 3345 1363 3400"><span class='xocr_word' id='xword_1_254' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_255' title="bbox 1381 3349 1719 3397"><span class='xocr_word' id='xword_1_255' title="x_wconf -2">CarroProvider</span></span> <span class='ocr_word' id='word_1_256' title="bbox 1738 3346 1899 3395"><span class='xocr_word' id='xword_1_256' title="x_wconf -1">existe</span></span> <span class='ocr_word' id='word_1_257' title="bbox 1917 3361 1943 3394"><span class='xocr_word' id='xword_1_257' title="x_wconf -2">a</span></span> <span class='ocr_word' id='word_1_258' title="bbox 1962 3340 2125 3394"><span class='xocr_word' id='xword_1_258' title="x_wconf -1">classe</span></span> <span class='ocr_word' id='word_1_259' title="bbox 2144 3344 2344 3392"><span class='xocr_word' id='xword_1_259' title="x_wconf -2">interna</span></span> <span class='ocr_word' id='word_1_260' title="bbox 2363 3342 2897 3391"><span class='xocr_word' id='xword_1_260' title="x_wconf -2">CarroProvider.Carros.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_14' title="bbox 674 3473 2179 3716">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 674 3474 2178 3549"><span class='ocr_word' id='word_1_261' title="bbox 674 3492 823 3549"><span class='xocr_word' id='xword_1_261' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_262' title="bbox 854 3490 1001 3535"><span class='xocr_word' id='xword_1_262' title="x_wconf -1">static</span></span> <span class='ocr_word' id='word_1_263' title="bbox 1031 3485 1128 3531"><span class='xocr_word' id='xword_1_263' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_264' title="bbox 1159 3483 1280 3529"><span class='xocr_word' id='xword_1_264' title="x_wconf -1">class</span></span> <span class='ocr_word' id='word_1_265' title="bbox 1311 3485 1457 3526"><span class='xocr_word' id='xword_1_265' title="x_wconf -2">Carros</span></span> <span class='ocr_word' id='word_1_266' title="bbox 1488 3479 1733 3533"><span class='xocr_word' id='xword_1_266' title="x_wconf -3">implements</span></span> <span class='ocr_word' id='word_1_267' title="bbox 1763 3477 2034 3522"><span class='xocr_word' id='xword_1_267' title="x_wconf -2">BaseColumns</span></span> <span class='ocr_word' id='word_1_268' title="bbox 2067 3474 2083 3529"><span class='xocr_word' id='xword_1_268' title="x_wconf -2">{</span></span> <span class='ocr_word' id='word_1_269' title="bbox 2120 3509 2129 3518"><span class='xocr_word' id='xword_1_269' title="x_wconf -1">.</span></span> <span class='ocr_word' id='word_1_270' title="bbox 2171 3509 2178 3518"><span class='xocr_word' id='xword_1_270' title="x_wconf -2">_</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_33' title="bbox 765 3563 1767 3631"><span class='ocr_word' id='word_1_271' title="bbox 765 3575 913 3631"><span class='xocr_word' id='xword_1_271' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_272' title="bbox 945 3572 1092 3618"><span class='xocr_word' id='xword_1_272' title="x_wconf -2">static</span></span> <span class='ocr_word' id='word_1_273' title="bbox 1121 3567 1218 3613"><span class='xocr_word' id='xword_1_273' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_274' title="bbox 1250 3567 1395 3618"><span class='xocr_word' id='xword_1_274' title="x_wconf -2">String</span></span> <span class='ocr_word' id='word_1_275' title="bbox 1425 3567 1522 3607"><span class='xocr_word' id='xword_1_275' title="x_wconf -3">NOME</span></span> <span class='ocr_word' id='word_1_276' title="bbox 1551 3580 1572 3598"><span class='xocr_word' id='xword_1_276' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_277' title="bbox 1605 3563 1767 3613"><span class='xocr_word' id='xword_1_277' title="x_wconf -3">"nome";</span></span></span>
<span class='ocr_line' id='line_1_34' title="bbox 765 3646 1767 3716"><span class='ocr_word' id='word_1_278' title="bbox 765 3660 913 3716"><span class='xocr_word' id='xword_1_278' title="x_wconf -2">public</span></span> <span class='ocr_word' id='word_1_279' title="bbox 945 3657 1092 3702"><span class='xocr_word' id='xword_1_279' title="x_wconf -2">static</span></span> <span class='ocr_word' id='word_1_280' title="bbox 1121 3651 1219 3697"><span class='xocr_word' id='xword_1_280' title="x_wconf -2">nal</span></span> <span class='ocr_word' id='word_1_281' title="bbox 1250 3651 1395 3702"><span class='xocr_word' id='xword_1_281' title="x_wconf -1">String</span></span> <span class='ocr_word' id='word_1_282' title="bbox 1425 3650 1521 3691"><span class='xocr_word' id='xword_1_282' title="x_wconf -1">DESC</span></span> <span class='ocr_word' id='word_1_283' title="bbox 1552 3664 1572 3682"><span class='xocr_word' id='xword_1_283' title="x_wconf -1">=</span></span> <span class='ocr_word' id='word_1_284' title="bbox 1606 3646 1767 3696"><span class='xocr_word' id='xword_1_284' title="x_wconf -2">"desc";</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_15' title="bbox 673 3779 882 3886">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 674 3832 692 3886"><span class='ocr_word' id='word_1_285' title="bbox 674 3832 692 3886"><span class='xocr_word' id='xword_1_285' title="x_wconf -2">}</span></span></span>
</p>
</div>
</div>
</body>
</html>
vtd . , .
a l` - .
(aPtU| 32 I Agenda de contatos e content provider 839
para cada
er.Carros carro.
A classe CarroPro
Ob 1 d bfI p
exem 1 arro, porpossibilita acessar as co unas a ta e a dis oniveis
geturdu 'd Serve tambem que a classe CarroProvtder.Carros dene o mtodo
"9 1 ) L1Sado para construir uma URI informando o id do c
p O content://bx com.lz1/roandroid.carros/carros/id.
Concluindo, com as cla
sses CarroProvder e CarroProvtder.Carros, podemos consultar
ista de carros do content provider, conforme demonstrado a seguir:
// Busca os carros do nosso content provider: content: //br.com.lvroandroid.carros/carros
Cursor cursor = getContentResolver().query(CarroProvder.Carros.CONTENT_URI, null,
null, null, null);
// L os carros do cursor
List carros = new ArrayLst();
while (cursor.moveToNext()) {
Carro c = new Carro();
c.nome = cursor.getStrng(cursor.getColumnInde(Carros.NOME));
c.desc = cursor.getStrng(cursor.getColumnInde(Carros.DESC));

carros.add(c);
}

// Fecha o cursor ao terminar de ler


cursor.close();

Como podemos ver, a forma de ler os dados no provedor de con.tedo segue o


mesmo padro, independentemente de qual seja.
Acredito que nossos estudos neste captulo esto quase chegando ao m. O
provedor de contedos est criado no projeto dos carros, e agora uma forma de
testar se tudo est funcionando criar outro projeto separado e testar as con
sultas. Lembrando que o maior objetivo de um provedor de contedo expor as

Nao

Apen
as ara au ~ ,
informaes para outra aplicacao.
~ -farei
lvimenro aqui,
esse desenvo

projeto,Pcriei
pois este um timo exerccio para voce.
xili lo deixei pronto o projeto de exemplo (arros(ontentProvider. Nesse
ta da lis
carros consultando o provedor de conteudo, da mesma
forma que zem os com o exemplo da agenda de contatos. Poderamos at con
ara inserir
tinuar o desenvolvimento P 'editar e excluir carros, mas isso ca como
exerccio para VOCC
32.13 Lili: teis
Conforme estudamos ncsu: apimlqcaso seja mamrio acpor as inormab do
nosso aplicativo para outras aplicaes, podemos criar um pmvcdor de conlcdo
O cxcmplo mais famoso dc provedor dc contedo a agcnda de contatos pois
CSS: aplicao pcnmrc quc qualquer outra faa a leitura em sua basc dc dados.
Lcmbnc-sc dc que. caso voc no precise expor as inormacs do scu aplicativo
recomenda-se dcix-las privadas Ou sqn. sucienu: utilizar um bancodc dados
com SQLite, ou qualquer outro modclo dc
Para complementar seus estudos, scpnrci alguns links da documcnu.\ ocial.
II! !! - Cld Hilda!
http;//dcvclopcmndroizicom/gide/tpics/providcrs/crm:n-prcmdnlIlu|
- uma nau; - uma quzywu a anmzu
utpx//dcvdopmndrdamdUairng/lmd-dam-bakgmmd/kmploadrzhnJ
KL cAPTuLo 33
\

_,__.,.__,/' S
\- 4

O Android permite enviar e receber uma mensagem SMS pelo aplicativo, e isso
pode ser utilizado para fazer com que aplicaes se comuniquem.
Embora o mundo mobile esteja cada vez mais conectado e dependente da conexo
com a internet, s vezes enviar uma mensagem de SMS pode ser uma boa soluo,
pois no depende de conexo e existe a garantia de entrega da mensagem.
Neste captulo, aprenderemos com exemplos prticos como enviar e receber
mensagens por SMS.

33.1 Enviando SMS por intent ou pela API


Para aprendermos como enviar uma mensagem SMS, vamos continuar o exemplo
do captulo anterior que mostrava a lista de contatos, com nossos amigos: Mickey
Pateta e Donald.
Eu criei uma cpia do projeto anterior e chamei de HeIIoContatosSMS. Neste projeto
deixei a ManActvty com o menu que insere os contatos Mickey Donald e Pateta
na agenda A lista de contatos mostrada pelo fragment LstaContatosFragrnent,
que por sua vez utiliza o Listvew com o CursorLoader, conforme aprendemos no
captulo anterior.
' 's e e sera p de partida para os exerccios deste captulo.
1 ' onto
. f- -' `"eber
S no arquivo de
Abra esse proleto, pol ultado, o projeto HeIIo(ontatosSMSOK contm a soluo
Se quiser apenas verificar o rS
SMS.
nal. Para comear, 3 dicione as permissces SEND_SMS e RECEIVE_SM
. - ~ essarias para enviar e rec
manifesto, pois sao ne

AndroidManifest.xmI
<manifest ..
> ndrod ' name="android . permission _ READ_CONTACTS" />
<uses-permsson a
841
842
Google Android - 4' edio

<U5S~Dermission android:name="android.permission.HRITE_CONTACTS" />


uses-permission android:nanen|ndrold.permission.SEND_SHS" /
uses-permission android:nane="|ndroid.pernission.RECEIVE_SHS' /
application . . . /
/application
/manifest

Poclenios eiiviar uina niensageni SMS por intent ou pela APL A classe IntentUtils
da biblioteca android-utils contm um mtodo para enviar o SMS por intem.

to IntentUtiIs.java
public class IntentUtils [
public static void sendSms(Context context, String fone,String msg) {
Uri uri = Uri.parse("sms:"+fone);
Intent intent = new Intent(Intent.ACTION_SENDTO,uri);
intent.putExtra("sms_body", msg);
context.startActivity(intent);
1

Para enviar o SMS pela API, crie a classe Sms com o seguinte cdigo-fonte:

ii Sms.java
public class Sms {
private static nal String TAG = "livro";
// Envia um SMS para o nmero indicado
public void send(Context context, String to, String msg) {
try {
Smsanager smsanager = SmsManager.getDefault();
Pendinglntent plntent = Pendinglntent.getBroadcast(context, 6, new Intent(), 0);
smsanager.sendTextMessage(to, null, msg, plntent, null)
Log.d(TAG,"Sms.send to["+to+"] msg["+msg+"]");
} catch (Exception e) {
Log.e(TAG, "Erro ao enviar o SMS: " + e.getMessage(),e);
}

lhnawzquewnumocodgopmaenwmwnnSMSpormnmtoupdaAPLwmum
alterar o exemplo que mostra a lista de contatos, para que, ao selecionar um
contauxsepiexndoinnrxnnnconiesmuduasopes
A lista de opes ser criada como um array em XML.
Captulo 33 | SMS 843
/res/values/strings.xmI
<I`SOU`CS>

<string nane="action_sns_intent">por Intent


<string nane="action_sns_api">pela API

/res/values/arrays.xmI
<?xnl version="1.0" encoding="utf-8"?>

<array nane="popup_sns">
@string/action_sns_intent
@string/action_sms_api

Esse array pode ser inflado para String[], sendo assim melhor denir esses re
cursos em XML, pois facilitam a manuteno e a internacionalizao.
Com o objetivo de inar o array e mostrar um popup com as opes para o
usurio escolher, vamos utilizar um AlertDialog. Siga em frente e altere o mtodo
onItenClick() que trata o evento de seleo da lista conforme demonstrado a seguir:

ListaContatosFragment.java
public class ListaContatosFragment extends Fragnent . . . {

@0verride
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Agenda a = new Agenda(getActivity());
nal Contato c = a.getContatoById(id);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("SMS para: " + c.n0ne);
builder.setItems(R.array.popup_sms,new Dialoglnterface.0nClickListener() {
@0verride
public void onClick(DialogInterface dialog, int which) {
String fone = c.fones.get(0);
if(which == 0) { // envia SMS por intent
IntentUtils.sendSms(getActivity(), fone, "Ol "+c.nome+", tudo bem?");
} else { // envia SMS pela API
Sms sms = new Sms();
844 Google Android - 4 edio
sns.send(getActivi.ty(),fone,"0I "+c.nome+", tudo ben?");
Toast.rfiakeTet(getActvty(), "SMS enviado para: " + c.nome,
Toast . LENGTH_SHORT) . show( );
}

});
AlertDia1og dialog = bu1der.create();
dia1og.show();
}

77' l
}

Feito isso, execute 0 projeto no emulador e clique em algum contato. A gura 33.1

~ l
mostra o alerta com as opes.

~ v . `ll
'rf i
ir
'3M
, A l*f.\"vi

( ..vi ;

Figura 33.1 - Alerta ao selecionar o (ontato.

A gura 33.2 mostra o resultado do envio do SMS. O lado esquerdo da gura


mostra a mensagem sendo enviada com uma intent. A vantagem de utilizar
intents que o aplicativo nativo e chamado, sendo assim o usuario pode redigir
a mensagem e enviar o SMS com o aplicativo ao qual j est acostumado. O lado
direito da figura mostra um toast logo aps o SMS ter sido enviado pela API. A
vantagem da API que ela permite enviar mensagens sem interagir como usuario.
Lembre-se de que o usurio quem paga pelo envio das mensagens. justamente
por isso, e necessrio declarar as permisses no AndroidManifest.xm1, para avisa-lo
de que aplicativo pretende enviar mensagens.
Captulo 33 . sMs 845
'r = -f _ *
_._ _ A
' si E t
*_ 'z " . .ff-'it ~ _ la '- ra-:fzz ...s 4Qe.;- _ _ Fr
i | l2'5b

,.Mickey
_
V
G .az
Mickey , Donald
*_f,_ _

-, A
1 "\

' _ Pateta
l; A

SMS enviado para: Mickey

FOlmf' zztudo4bemll
Mickey,

Figura 33.2 - Alerta ao selecionar 0 contato.

33.2 Criando um BroadcastReceiver para receber um SMS


Para receber um SMS no aplicativo, basta interceptar a mensagem de broadcast
SMS_RECEIVED, a qual enviada pelo sistema sempre que uma nova mensagem SMS
recebida pelo sistema.
Ento, mos obra: declare a permisso RECEIVE_SMS no AndroidManifest.xml e
congure um broadcast receiver para interceptar a ao SMS_RECEIVED.

AndroidManifest.xmI
<manifest

<uses-permission androd:name="android.permssion.RECEIVE_SMS" />


<app1caton . . . />
<actvty . . . >
receiver androd:name=".SMSRecever">
ntent-1ter
<action androd:name="androd.provider.Te1ephony.SMS_RECEIVED" /
<Categ0ry androd:name="androd.intent.category.DEFAULT" /
/intent-1ter>
/recever>
</app1caton>
</manfest>

A classe SMSRecever pode ser visualizada a seguir:


846
Google Android - 4' edio

[ SMSReceiver.java
public class SHSReceiver extends BroadcastReceiver {
private static nal String TAG = "livroandroid";
@Override
public void onReceive(Contet context, Intent intent) {
L9-d(TAG, ">" + intent.getAction());
Sms sms = new Sms();
// L a mensagem
Smsessage msg = sms.read(intent);
String celular = msg.getDisplay0riginatingAddress();
String mensagem = msg.getDisplayMessageBody();
Log.d(TAG, "SHSReceiver: sms[" + celular + "] -> " + mensagem);
Noticationtil.create(contet,1, new Intent(contet,MainActivity.class),
R.mipmap.ic_launcher,"Novo SMS de: " + celular,mensagem)
}

Esse receiver l a mensagem SMS e a imprime no LogCat. Logo depois, mostra


uma noticao ao usurio. Para ler a mensagem SMS, vamos criar o mtodo
read(intent) na classe Sms que criamos anteriormente.

Sms.java
public class Sms {

// L uma mensagem da intent interceptada pela ao


public Smsessage read(Intent intent) {
SmsMessage[] mensagens = getMessagesFromIntent(intent);
if (mensagens != null) {
return mensagens[0];
}
return null;
}
private SmsMessage[] getMessagesFromIntent(Intent intent) {
Log.d(TAG, "Sms.getMessagesFromIntent: " + intent.getAction());
Object pdus[] = (Object[]) (Object[]) intent.getSerializableExtra("pdus");
SmsMessage msgs[] = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
String celular = msgs[0].getDisplay0riginatingAddress();
String mensagem = msgs[0].getDisplayMessageBody();

`9tUI`I'l NSQS

}
Captulo 33 n SMS 847
Este cdigo l a mensagem SMS que est no formato PDU (Protocol Description
Unit) e retorna um array de objetos do tipo SmsMessage, o qual contm o remetente
e o texto da mensagem recebida.
Com o objetivo de testar se o broadcast receiver est corretamente configurado
para interceptar a mensagem SMS, podemos simular o envio de um SMS para o
emulador. Para isso, abra a ferramenta Tools > Android > Android Device Monitor, selecione
o emulador na janela Devices e abra a janela Emulator Control. Por m, digite o nmero
do telefone fictcio no campo lncoming Number, selecione o item SMS e escreva algum
texto. Quando tudo estiver pronto, clique no boto Send, conforme podemos ver
na gura 33.3.
Q Android Device Monitor - 5
File Edit Run Window Help

Ti vs l l l < lllli ^ j
Devices
.tl
sf.z._
x _. _,_ ,P:'
s . ,~.I-az)
1 ? JJ =
lquick Aczi _ _~ v v sb [5 Qi v
s, Ll$>_T_h':;_i__@.l`S9l._@. 'ri ' Emul" E ` :X$t:_f-.

Telephony Status
_ ,.

Name ^` Voicezlhome VlSpeed: lFulI V


1 motorola-nexus_6ZXlG-'l ` ' . . '
br.com.livroandroid.cor V Data' home , i/zLatenCy:.Nne
4 Android [emulator-55541 1-eephonyActOnS
com andmfd mpmmef nncmmg number: 987654321
com.android.systemu {_\ g ~ ' ~' Y f f ~
com.andro:d.prntspool r Volce

> V. 'Send
" ' Han
V~a
com.android.phone . (9 SMS _ fg________H_____NH__________~_*_________
com.google.process.loc , MSSQE l Opa tudo bem?
com.androd.desl<clock L
com.google.process.ga
com.google.android.gn " ~

j A ru of566M H

Figura 33.3 - Enviando um SMS para o emulador

Isso vai simular o envio de um SMS. Sendo assim, caso o broadcast receiver esteja
corretamente congurado, a aplicao vai interceptar a mensagem SMS_RECEIVED e
vai imprimir no LogCat o remetente e o texto da mensagem.
D/lvroandrodz SMSRecever: sms[987654321] -> Opa, tudo bem?

A figura 33.4 mostra que existem dois cones de notificao no emulador indi
cando que a mensagem foi recebida. O primeiro cone da aplicao nativa de
mensagens e o segundo cone da nossa aplicao. Como a mensagem enviada
por broadcast, todos os receivers recebem a mensagem, portanto no tem como
evitar que a aplicao nativa tambm a receba. Ao abrir a noticao (lado direito
da figura), podemos ver as duas notificaes, sendo que criei a noticao do
livro seguindo o mesmo modelo da notificao nativa. O ttulo o nmero do
remetente, e o contedo do texto a mensagem recebida por SMS.
7
848 Google Android - 4 edio
ve. 5 ' u 2 18
. '`.L,_;
^ - _ * -4 ~I z
. 'I> .z7..r..z,,_

\
.)r,T;:
; `_~_ _. _.,$ .:Vz_;
*
r _ -; ifg \ ;=.;*z, .i'
* V 'I z Q" x~,
.,\z;
5. 'Ifzzm
z~-.^' ~__z__,
JZ '- "L;.*' ,
.X .

*fm

987654321 . .
_ Qe7s4a2i

W731

Figura 33.4 - Noticao ao receber 0 SMS.

33.3 Links teis

Neste captulo, estudamos como enviar e receber mensagens SMS. Seguem alguns
links para continuar seus estudos.
API Reference - Classe BroadcastRecever

http://det/el oper: and mid. com/ refereme/cmd roid/corztent/Broadcas Reeei irei: html

API Reference - Classe SmsManager

Ii ttp://det/el open and roid. com/re*rence/android/teleph ony/Sms M uizuger: Ii mil

API Reference - Classe SmsMessage

http://developer:android.com/reererzce/andrnd/telephony/SmsMessuge.html
CAPTULO 34
Gestos
\_q
1.
.KI.
\\ ,<`^

Gestos podem ser utilizados para reconhecer assinaturas digitais, desbloquear


telas com um movimento cadastrado ou at reagir a simples gestos de swipe
lateral ou zoom.
Neste captulo, vamos estudar os conceitos principais para implementar esse tipo
de funcionalidade.

34.1 Introduo
O reconhecimento de gestos pode ser realizado de duas maneiras:
A primeira maneira comparar o gesto efetuado pelo usurio corn algum gesto
previamente cadastrado. Esse o caso de uma assinatura digital, na qual o usurio
faz o cadastro da assinatura, a m de repetir o gesto para validar a assinatura.
AAPI permite comparar se o gesto feito pelo usurio segue o mesmo padro da
assinatura cadastrada e retorna uma pontuao (escore), informando se o gesto
parecido com o padro salvo. Baseado na pontuao, a aplicao deve decidir
se o gesto vlido ou no.
A segunda maneira de reconhecer gestos monitorar o deslocamento nos eixos
x e yr para reconhecer gestos bem populares, como o swipe lateral e zoom.

34.2 Reconhecendo gestos previamente cadastrados


Antes de eomearmos a brincar com gestos, vamos visualizar o exemplo que
iremos criar assim ficar mais fcil de entender o cdigo.
7

Ac executar 3 aplieaeo pela primeira vez, voce ver uma tela vazia, conforme a
figura 34.1. Nesta tela voc podera desenhar um gesto, porm esse gesto s ser
reconhecido caso ele esteja cadastrado na biblioteca de gestos.

849
85 Google Android - 4 edio

R score 12 l50?O13QZ5lS30'I ~

1 Biblioteca de gestos lida com sucesso

Figura 34.1 - Gesto reconhecido com sucesso.

Para cadastrar um gesto, clique no boto (+) na action bar. Isso vai abrir a tela para
cadastrar um gesto, conforme a figura 34.2. Nessa tela, desenhe o gesto, digite um
nome qualquer para ele e clique no boto Salvar. A gura 34.2 mostra um gesto
com a letra R sendo salva na biblioteca.

R
l SALVAR

Gesto salvo; R, path: Istoragelsdcard/


gestores

Figura 34.2 - Cadastrando um gesto.

Quando voc j tiver cadastrado algum gesto em arquivo, volte tela inicial e re
pita o mesmo gesto. O lado direito da figura 34.1 mostra o gesto da letra R sendo
reconhecido com uma pontuao maior que 5.
Captulo 34 1 Gestos 851
A rea que voc pode desenhar o gesto delimitada pela view Gesture0ver1ayVew, a
qual foi inserida no centro do layout. Essa view sozinha faz o desenho e permite
reconhecer o gesto. Depois de reconhecer um gesto, ele precisa ser validado. Isso
feito com a classe GestureLbrary, que permite carregar a biblioteca de gestos que
foi salva em arquivo. Se o gesto for reconhecido como vlido pela biblioteca,
mostrada a pontuao (escore) do gesto no Textview. Quanto mais alta for a pon
tuao, melhor, pois signica que o gesto desenhado prximo do cadastrado.
No nosso exemplo, estou aceitando somente gestos com pontuao superior a 5,
pois a aplicao que dene qual pontuao mnima deseja aceitar.

Dica: na tela de cadastrar o gesto, voc deve desenhar o gesto, digitar um nome
para ele e clicar no boto Salvar. Foi o que z na gura 34.2. Somente gestos
previamente cadastrados podem ser identicados. Depois de cadastrar o gesto,
volte na tela inicial e tente repeti-lo. Se a pontuao (escore) for alta, o gesto
ser identicado.

Agora que j sabemos o que queremos fazer, vamos colocar a mo na massa. Crie
um projeto chamado HeIIoGestureLibrary e adicione a permisso wRITE_EXTERNAL_STORAGE
no arquivo de manifesto, pois vamos salvar os gestos feitos pelo usurio no
SD card. Para reconhecer um gesto no aplicativo, deve-se utilizar a classe
androd.gesture.Gesture0ver1ayVew, que uma view especial responsvel por moni
torar os gestos. Portanto, vamos criar o seguinte layout:

/res/layout/activity_main.xml
<?m1 version="1.0" encodng="utf-8"?>
<LinearLayout xmlns:androd="http://schemas.android.com/apk/res/androd"
android:1ayout_wdth="match_parent" androd:Iayout_heght="match_parent"
androd:orientaton="vertca1" android:paddng="16dp">
<TetView android:id="@+d/text"
androd:1ayout_width="match_parent" androd:1ayout_height="wrap_content" />
<FrameLayout androd:d="@+d/layout" androd:1ayout_weght="1"
android:1ayout_width="match_parent" androd:Iayout_heght="0dp" >
<ImageVew androd:d="@+d/img"
androd:1ayout_wdth="wrap_content" android:1ayout_height="wrap_content"
androd:1ayout_gravty="CtF"/>
android.gesture.Gesture0ver1ayView android:d="@+d/gesturevew"
android:1ayout_wdth="match_parent" android:1ayout_height="match_parent"
androd:gestureCo1or="#00ff00" />
352 eoogie :mmia-4=z|
Utilizei um FraneLayout para colocar o Gestureverlayview por cima do Inageview, assim
logo depois de fazer o gesto possvel extrair o Bitmap do desenho com o objetivo
de mostr-lo no Inageview.

No cdigo, para reconhecer os gestos, basta congurar o listener da classe


Gesture0verlayView:

Gesture0verlayView gestureverlayview = (GestureOverlayView) ndViewById(R.


id.gestureView);
gesture0verlayView . add0nGesturePerfornedListener(this);

Quando um gesto for reconhecido, o mtodo onGesturePerformed(. .) ser chamado,


ento podemos utilizar mtodo recogni.ze(gesture) da classe GestureLibrary para
reconhec-lo.

GestureLibrary gestureLib = . . .,
@Override

}
public void onGesturePerformed(Gesture0verlayView overlay, Gesture gesture) {
// Faz a biblioteca de gestos reconhecer o movimento
ArrayList predictions = gestureLib.recognize(gesture);

A classe GestureLibrary representa a biblioteca de gestos conhecidos. Na prtica a


biblioteca de gestos um arquivo que salvo no SD card e pode at ser copiado
para a pasta /res/raw do projeto. O trecho de cdigo a seguir mostra como carregar
a biblioteca de gestos a partir de um arquivo salvo na pasta /res/raw.
// Carrega a biblioteca de gestos a partir do arquivo /res/layout/gestures
GestureLibrary gestureLib = GestureLibraries.fromRawResource(this, R.raw.gestures);
boolean ok = gestureLib.load();

Isso til caso voc queira embutir no aplicativo o arquivo previamente criado
com a biblioteca de gestos. Mas geralmente os gestos sero salvos no SD card pelo
aplicativo em tempo de execuo. Neste caso podemos utilizar um cdigo como
este para ler a biblioteca e carregar o arquivo:
File le = new File(Environment.getEternalStorageDirectory(), "gestures");
gestureLib = GestureLibraries.fronFile(file);
boolean ok = gestureLib.load();

Ser exatamente isso que vamos fazer no nosso exemplo. Para finalizar o exemplo,
vamos mostrar o cdigo da activity que carrega a biblioteca de gestos no mtodo
onResume() e faz o reconhecimento dos gestos.
853
Captulo 34 u Gesto:

r; ManActIvIty.]ava

publlc class MalnActlvlty extends AppCompatActlvlty lmplements 0nGesturePerformedLlstener {


prlvate Gesturetlbrary gesturetlb;
prtvate Textvtew text;
private Imagevlew tmg;
00verrlde
protected vold onCreate(Bundle savedInstanceState) [
super.onCreate(savedInstanceState);
setContentVlew(R.layout.actlvlty_maln);
text 1 (Textvlew) ndVlewById(R.ld.text);
lmg - (lmagevlew) ndVlewById(R.ld.lmg);
// Congura o llstener do Gesture0verlayVlew
Gesture0verl|yVlew gesture0verleyVtew I (Gesture0verlayVlew)
ndVtewById(R.td.ge|tureVlew);
9esture0verl|yVtew.edd0nGe|turePerformedLtstener(this);
}

00verrtde
protected vold onResume() {
super.onResume();
/I L o arqulvo de gestos do SD card
re|dGe|ture|();
l
00verrlde
publlc vold onGesturePerformed(Gesture0verlayVlew overlay, Gesture gesture) {
tf(gestureLlb == null) return;
// Faz a blbltoteca de gestos reconhecer o movlmento
Arr|yLt|tPredlctlon predlctlons n gesturetlb.recogntze(ge|ture);
Predlctlon maScore = null;
for (Predlctlon predlctlon : predlctlons) {
// Vamos acettar somente escores matores que S
lf (predlctton.|core S.0) {
lf (maxScore == null || maxScore.score < predlctlon.score) {
maScore = predlctlon;
}

// Se encontrou algum gesto com escore alto, vamos mostrar o texto


lf (maScore != null) {
// Se o escore malor que 5
Strlng desc = maxScore.nome + ". SCOW " t "W5C0f@-SC0f@i
854 Google Android - 4' edio
tet.setTet(desc);
// Mostra o gesto em um Imageview
int w = (int) gesture.getBoundingBo().width();
int h = (int) gesture.getBoundingBox().height();
nal Bitmap b = gesture.toBitmap(w, h, 8, Color.GREEN);
img.setImageBitmap(b);
l
1

@0verride
public boolean onCreate0ptionsHenu(Menu menu) {
getMenuInater().inate(R.menu.menu_main, menu);
return true;
}

@0verride
public boolean on0ptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_add) {
startActivity(new Intent(this, SalvarGestoActivity.class));
return true;
1

return super.on0ptionsItemSelected(item);
}

public void readGestures() {


File le = new File(Environment.getEternalStorageDirectory(), "gestures");
if(le.eists()) {
gestureLib = GestureLibraries.fromFile(le);
}

if (gestureLib != null && gestureLib.load()) {


Toast.makeText(this,"Biblioteca de gestos lida com sucesso.",
Toast.LENGTH_SHORT).show();
}

Essa activity faz o reconhecimento dos gestos no mtodo onGesturePerformed(..).


Logo depois que um gesto detectado, feito um loop por todos os objetos
Prediction para vericar qual tem a maior pontuao. Se a pontuao (escore) for
acima de 5, o gesto reconhecido, a pontuao mstrada no Textview e o gesto
impresso no Imageview.
Para o cdigo compilar, crie o arquivo de menu com o boto de adicionar (+).
855
Captulo 34 I Gastos

) /res/menu/menu_man.xmI
<menu mlns:androd="http://schemas.androd.com/apk/res/androd"
m1ns:too1s="http://schemas.androd.com/tools"
xm1ns:app="http://schemas.androd.com/apk/res-auto" >
<item android:id="@+d/action_add" androd:title:"@strng/acton_add"
androd:con="@androdzdrawable/c_menu_add" app:showAsActon="a1ways" />

Ao clicar no boto de adicionar (+), a activity Sa1varGestoActvty ser chamada;


portanto, crie-a conforme o cdigo-fonte demonstrado a seguir:

/res/layout/attivity_saIvar_gesto.mI
<?xm1 version="1.G" encodng="utf-8"?>
<LnearLayout m1ns:androd="http://schemas.androd.com/apk/res/androd"
androd:layout_wdth="match_parent" android:1ayout_heght="match_parent"
androd:orentaton="vertca1" androd:paddng="16dp" >
<EdtText
androd:d="@+d/text"
androd:1ayout_width="match_parent" android:layout_heght="wrap_content" />
<Butt0n
androd:1ayout_width="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center_horizonta1" android:text="@string/salvar"
androd:onC1ck="onC1ckSa1var"/>
<FrameLayout android:d="@+d/layout"
androd:1ayout_width="match_parent" androd:1ayout_heght="0dp"
androd:layout_weght="1" >
<ImageVew androd:d="@+d/img"
androd:1ayout_wdth="wrap_content" androd:1ayout_heght="wrap_content"
androd:1ayout_gravty="center"/>
<androd.gesture.Gestureverlayview androd:id="@+d/gesturevew"
androd:1ayout_wdth="match_parent" androd:1ayout_heght="match_parent"
androd:gestureCo1or="#00ff00" />
</FrameLayout
</LnearLayout>

Esse layout tambm reconhece o gesto com a classe Gesture0ver1ayVew, mas logo
depois que o gesto reconhecido ele permite digitar o nome do gesto e clicar no
boto Salvar para salv-lo na biblioteca de gestos em arquivo.
856
Google Android - 4' edio

Sa|varGestoActivity.java

public class SalvarGestoActivity extends AppCompatActivity implements Gesture0verlayView.


0nGesturePerformedListener {
private Textview text;
private GestureLibrary gesturelib;
private nal File le = new File(Environment.getExternalStorageDirectory(), "gestures");
private Imageview img;
private Gesture gesture;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_salvar_gesto);
text = (Textview) ndViewById(R.id.tet);
img = (ImageView) ndViewById(R.id.img);
Gesture0verlayView gestureOverlayView = (Gesture0verlayView)
ndViewById(R.id.gestureView);
gesture0verlayView.add0nGesturePerformedListener(this);
// Carrega a biblioteca de gestos, a partir do arquivo salvo em /res/layout/gestures
gestureLib = GestureLibraries.fromFile(le);
}

@0verride
public void onGesturePerformed(Gestureverlayview overlay, Gesture gesture) {
// Mostra o gesto em um Imageview
int w = (int) gesture.getBoundingBo().width();
int h = (int) gesture.getBoundingBox().height();
nal Bitmap b = gesture.toBitmap(w, h, 8, Color.GREEN);
img.setImageBitmap(b);
overlay.setGesture(gesture);
this.gesture = gesture;
}
public void onClickSalvar(View view) {
String nome = text.getTet().toString();
if(gesture != null && nome != null) {
// Salva o gesto
gesturelib.addGesture(nome, gesture);
gestureLib.save();
nal String path = le.getAbsolutePath();
Toast.makeTet(this, "Gesto salvo: " + nome + ", Dth " + Dth,
Toast.LENGTH_SHORT).show();

}
Captulo 34 n Gestos . _. ,. ,f-asta
'r
No caso do emulador, o arquivo com a biblioteca de gestos e salvo na p
857

/storage/sdcard/gesturcs. justamente por isso, no metodo onResume() da Main/XCUVI Y


esse arquivo carregado em memria.
@0verrde
protected void onResume() {
super.onResume();
// L o arquivo da biblioteca de gestos
readGestures();
l

Depois, no mtodo onGesturePerformed( . . ), os gestos detectados podem ser validados


com base nesta biblioteca que foi carregada em memria.
ArrayLst predictons = gesturetb.recognze(gesture);
Para testar o cdigo, execute o projeto no emulador. O resultado deve ser como
as guras 34.1 e 34.2 explicadas no incio deste tpico.

34.3 Detectando gestos comuns, como scroll lateral


j aprendemos como utilizar a biblioteca de gestos para reconhecer gestos pre
viamente cadastrados, mas s vezes o que precisamos fazer apenas identificar
um padro de gesto amplamente conhecido, como um swipe lateral ou zoom.
Para reconhecer esses gestos, podemos utilizar a classe Gestureetector e a interface
0nGestureListener, a qual contm mtodos para receber os eventos quando um gesto

..3
for detectado, conforme a lista a seguir:

abstract boolean onDown(MotonEvent e)

Chamado quando o evento de toque iniciado.

abstract boolean onF1ng(MotionEvent el, MotonEvent e2, oat ve1octyX, oat ve1ocityY)
Chamado quando um movimento de swipe lateral foi encerrado, informan
do a coordenada inicial e a nal, assim como a velocidade do movimento
nos eixos x e y

abstract void onLongPress(MotionEvent e)

Chamado se um toque demorado, por exemplo, com mais de um segundg


foi realizado.
858 Google Android - 4 edio
abstract void onShowPress(MotionEvent e)

Chamado se o usurio fez o toque, mas ainda no soltou o dedo.

abstract boolean onSingleTapUp(MotionEvent e)

Chamado quando um toque simples detectado.


No prximo exemplo, vamos demonstrar como utilizar o mtodo onFling( . . .) para
reconhecer o gesto de swipe lateral. Como precisamos implementar apenas um
mtodo, podemos criar uma subclasse de Simple0nGestureListener, a qual j imple
menta todos esses mtodos de forma vazia, assim podemos sobrescrever apenas
o mtodo pelo qual temos interesse.
Para continuar, crie o projeto Helloestos e a seguinte activity Se preferir abra o
projeto de exemplo deste captulo. Neste projeto criei duas activities: uma para
reconhecer o gesto de svvipe e outra para reconhecer o gesto de zoom.
Vamos comear pelo exemplo de gesto com rolagem lateral, conhecido como swipe.

SwipeActivity.java

import static com.nineoldandroids.view.ViewPropertyAnimator.animate;


public class SwipeActivity extends AppCompatActivity {
Textview text;
private Imageview img;
private Gesturebetector gesturebetector;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe);
img = (ImageView) ndViewById(R.id.img);
text = (Textview) ndViewById(R.id.text);
text.setText("Faa um gesto");
// Cria a instncia de Gestureetector e informa o listener
gesturebetector = new GestureDetector(this,new MyFlingGestureDetector());
}

@0verride
public boolean onTouchEvent(MotionEvent event) {
// Aqui est o segredo. Delega o tratamento do touch para a classe Gesturebetector
boolean tratouEvento = gestureDetector.onTouchEvent(event);
if(tratouEvento) {
return tratouEvento;
}
859
Captulo 34 n Gestos

return super.onTouchEvent(event);

/ Listener para reconhecer o gesto


class HyFlingGestureDetector extends Gestureetector.Simple0nGestureListener {
// Distncia do movimento em pixels. Por exemplo, o usurio tocou na tela e m0vU 0
// dedo 100 pixels para a direita
private oat swipeMinDistance = 100;
// Velocidade do movimento em pixels por segundo
private oat swipeThreasholdVelocity = 200;
@Override
public boolean onFling(HotionEvent ei, HotionEvent e2, oat velocityX,oat velocityY) {
// Usurio fez um gesto de swipe lateral
try {
if (e1.getX() - e2.getX() swipeMinDistance && Math.abs(velocityX)
swipeThreasholdVelocity) {
// Fez um gesto da direita para a esquerda
tet.setText("<<< Left");
animate(img).xBy(-100); // Move 100p para esquerda
} else if (e2.getX() - e1.getX() swipeMinDistance && Math.abs(velocityX) >
swipeThreasholdVelocity) {
// Fez um gesto da esquerda para a direita
text.setTet("Right >>>");
animate(img).xBy(+100); // Move 100p para direita
}

} catch (Exception e) { }
return false;
}

/res/layout/activity_swipe.xmI
<FrameLayout mlns:android="http://schemas.android.com/apk/res/android"
mlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#eeeeee" android:padding="16dp" >
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:src="@drawable/android" />
<TetView

android:id="@+id/text" android:layout_gravity="center"
86 Google Android - 4 edio
androd:1ayout_wi.dth="wrap_content" android:1ayout_heght="wrap_content"
android:tet="@string/faca_um_gesto" androd:tetSze="18sp" />

/res/values/strings.xm|
<l'eSOU rces>

<string name="faca_um_gesto">Faa um gesto</strng

Esse arquivo de layout tem uma imagem no topo e um Textvew no centro, o qual
vamos utilizar para exibir as informaes sobre o gesto. Por exemplo, se o usurio
mover o dedo da esquerda para a direita, vamos mostrar essa informao no Textvew.
Para o cdigo compilar, adicione a biblioteca Nine0IdAndroids no arquivo app/buildgradle.

app/buiId.gradIe

Compile 'com.nineoldandroids:1brary:2.4.0'

Nota: ao fazer o swipe lateral, estamos utilizando a biblioteca Nne01dAndrods


(http://nineoldandroids.com/) para movimentar a imagem de forma animada para
esquerda ou direita, conforme o gesto efetuado. A biblioteca Nne01dAndroids
permite utilizar a sintaxe da ViewPropertyAnmator criada no Android 3.1
(I-loneycomb) mantendo a compatibilidade com verses antigas do Android.

Ao executar o exemplo, voc ver um texto escrito faa um gesto no centro da


tela, conforme a gura 343. A gura ao lado mostra o resultado logo aps fazer
um gesto da esquerda para a direita. Conforme podemos visualizar, o resultado
que o texto Right foi exibido no centro da tela e a imagem foi deslocada para a
direita, pois o gesto de swipe lateral foi reconhecido com sucesso.
Depois de ver o exemplo em ao, vamos entender o cdigo. O segredo criar um
objeto do tipo GestureDetector e uma implementao de 0nGestureLstener, para que
o mtodo onF1ing( . . .) seja chamado assim que o gesto for detectado.
Mas para a classe funcionar, voc precisa invoc-la de alguma forma. Isso pode
ser feito sobrescrevendo o evento de onTouch(event) da prpria activity ou de algu
ma view customizada. Neste caso, estamos fazendo tudo direto na activity Dessa
forma, sempre que o usurio tocar na tela, a classe GestureDetector ser invocada
para detectar o gesto. Note que o mtodo onTouchEvent(event) pode retornar true,
indicando que o gesto foi detectado por essa classe, ou false, indicando o contrrio.
Captulo 34 I Gestos 8

V'_-`~'.'` f.;=isE `i _]
t}*F

OOO
.
..)z;,'
2%

Faa um gesto '|;zht >> ~


61

Figura 34.3 - Gesto de swipe.

Quando o gesto de movimento for detectado, o mtodo onF1ng( . . .) chamado.


Observe que nos parmetros recebemos dois objetos do tipo MotonEvent, que
contm as posies x/y de onde o gesto foi iniciado e nalizado, assim como a
velocidade do gesto, medida em pixels por segundo.
Basicamente, o que precisamos fazer vericar a distncia percorrida no touch
entre os pontos inicial e nal do eixo x. Se a distncia for maior que um valor
congurvel, signica que o usurio arrastou o dedo por uma distncia suciente
para que o gesto se concretizasse.
Por m, estamos movendo a imagem do Android para direita ou esquerda, conforme
o gesto efetuado. Lembrando que para fazer a animao foi utilizada a biblioteca
Nine0IdAndroids, por isso importamos a funo anmate de forma esttica no cdigo.
import static com.nneo1dandrods.view.VewPropertyAnmator.animate;

Com essa biblioteca, animar uma view (mantendo a compatibilidade) simples assim:
annate(i.ng).xBy(-100); // Move 100p para esquerda

34.4 Detettando gesto de pinch (zoom)


Outro gesto bastante comum o de fazer zoom em alguma view que consiste em
tocar dois dedos na tela e fazer o gesto conhecido como pinch.
Esse gesto utilizado em diversos aplicativos nativos do Android, como o lbum
de fotos e o browser. Ao visualizar uma foto, podemos fazer o zoom da imagem
862 Googiznmi-4-ediao
com o classico gesto de pinch, assim como no browser, para dar zoom em alguma
pagina que est sendo visualizada.
Como esse gesto muito popular, foi criada a classe ScaleGestureDetector, que facilita
muito o trabalho de detect-lo. Para demonstrar, vamos criar a activity chamada
ZoonActivity conforme demonstrado a seguir:

/res/layout/activity_zoom.xml
<FrameLayout mlns:android="http://schemas.android.con/apk/res/android"
xmlns:tools="http://schemas.android.con/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:padding="16dp" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:text:"@string/faca_um_gesto" />
br.com.livroandroid.gestos.HyInageView
android:id="@+id/img"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" />

Note que a view Mylmageview inserida no layout customizada, portanto precisamos


criar essa classe que ser utilizada para desenhar uma imagem no centro da tela.
Essa view ser utilizada para fazer o desenho da imagem escalada logo aps o
gesto de zoom.

MyImageView.java
package br.com.livroandroid.gestos;

public class Mylmageview extends View {


private static nal int img = R.drawable.android;
private Drawable drawable;
private oat scaleFactor = 1;
private int imgw;
private int imgH;
private Paint paint;
public MyImageView(Context context) {
super(contet);
init(contet);
}
Captulo 34 I Gestos

public HyInageView(Context context, AttributeSet attrs, int defStyle) {


super(contet, attrs, defStyle);
init(context);
}

public MyImageView(Context context, AttributeSet attrs) {


super(context, attrs);
init(context);
}

private void init(Context context) {


// Cria o drawable/imagem para desenhar
drawable = context.getResources().getDrawable(img);
ingw = drawable.getIntrinsicNidth();
ingH = drawable.getIntrinsicHeight();
drawable.setBounds(0, 0, ingw, imgH);
// Cria o pincel azul
paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLUE);
}

Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Desenha um quadrado apenas para visualizar a view
canvas.drawRect(0, 0, getHidth() - 1, getHeight() - 1, paint);
// Multiplica a largura e a altura pela escala
int larguraComEscala = (int) (imgw * scaleFactor);
int alturaComEscala = (int) (ingH * scaleFactor);
// Posiciona o desenho no centro da tela
int dx = getwidth() / 2 - larguraConEscala / 2;
int dy = getHeight() / 2 - alturaComEscala / 2;
canvas.translate(dx, dy);
// Faz a escala do desenho (para dar zoom out/in)
canvas.scale(scaleFactor, scaleFactor);
// Desenha a imagem
drawable.draw(canvas);
}

public void setScaleFactor(oat scaleFactor) {


// Permite activity congurar a escala e redesenhar a view
this.scaleFactor = scaleFactor;
}

}
854 Google Android - 4' edio
O cdigo dessa classe est criando um Drawable com a imagem /res/drawable/android. jpg
para depois no mtodo onDraw(canvas) desenhar a imagem.
O cdigo faz um clculo para posicionar a imagem no centro da tela, algo que
bem comum em desenhos de baixo nvel em canvas. Depois que descobrimos as
posies x e y do centro da tela, o mtodo canvas.translate(d,dy) chamado para
informar ao canvas que o desenho deve ser feito nessas coordenadas.
Repare tambm que temos a chamada do mtodo canvas.scale(scaleX,scaleY), que
faz o desenho ser escalado conforme a proporo informada para os eixos x e
y Por exemplo, se voc escalar a imagem com o valor 1, signica que a imagem
car com o tamanho original, j 0,5 corresponde metade do tamanho, e 2, ao
dobro do tamanho original.
Note que a escala da imagem congurada pelo atributo scaleFactor, o qual por
padro tem o valor 1. Nossa brincadeira ser fazer a activity alterar o valor dessa
escala, conforme o gesto de zoom do usurio, refletindo no tamanho da imagem
a m de obter os efeitos de zoom out e zoom in.

Para nalizar o exemplo do zoom, vamos escrever o cdigo da activity

ZoomActivity.java

public class ZoomActivity extends AppCompatActivity {


Textview text;
private Scaleestureetector gesturebetector;
private Hylmageview img;
private oat scaleFactor = 1;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zoom);
// Informa o fator de escala padro
img = (HyInageView) ndViewById(R.id.ing);
ing.setScaleFactor(scaleFactor);
text = (Textview) ndViewById(R.id.text);
tet.setText("Faa um gesto de pinch/zoom");
// Congura o detector de gestos ,
gestureetector = new ScaleGestureDetector(this, new ZoonGestureDetector());
}

@0verride
public boolean onTouchEvent(MotionEvent event) {
// Delega o evento de touch ao detector de gestos
865
Captulo 34 I Gestos

boolean ok = gestureetector.onTouchEvent(event);
if(ok) {
return super.onTouchEvent(event);
}

return ok;
}

// Detecta o zoom
class ZoomGestureDetector extends ScaleGestureDetector.Sinple0nScaleGesturetistener {
@0verride
public boolean onScale(ScaleGestureDetector detector) {
// Armazena o fator de escala detectado
scaleFactor *= detector.getScaleFactor();
// Exibe o fator de escala no Textview
tet.setTet(String.value0f(scaleFactor));
// Altera o fator de escala da view customizada e pede para ela se redesenhar
img.setScaleFactor(scaleFactor);
img.invalidate();
return true;
}

@0verride
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}

@0verride
public void onScaleEnd(ScaleGestureDetector detector) {
}

Essa classe implementa a interface Sinple0nScaleGestureListener para receber o evento


sempre que um gesto de pinch/zoom feito pelo usurio. O mtodo mais impor
tante que precisamos implementar o onScale(detector), o qual chamado quando
o gesto de zoom reconhecido. Nesse momento, a escala do gesto recuperada
pelo mtodo detector.getScaleFactor() e armazenada na varivel scaleFactor, a qual
passada para a view customizada. No mtodo onDraw(canvas) da view esse fator
de escala utilizado para desenhar a view com o zoom denido pelo usurio.
O resultado disso um exemplo interessante que vai exibir uma imagem no
centro da tela. Voc poder fazer o gesto de zoom out e zoom in para brincar com a
escala da imagem. A gura 34.4 mostra o resultado. A primeira parte mostra a
imagem original com escala = 1 e ao lado podemos ver a imagem com o dobro
do tamanho com uma escala prxima de 2; ou seja, z o gesto de zoom para
expandir a imagem.
866 Google Android - 4' edio
d' V dulwz~O 5Q ufmvt
|;\t,:|\|t||.,r,1|(| p 1'

.z, ' ._.

: . V '
t-Vf$.?.t- ,_ `

z apy . :

1""!'

Figura 34.4 - Gesto de zoom.

Nota: para testar o gesto de pinch/zoom, utilize um dispositivo real. Ou caso


esteja utilizando o emulador do Genymotion mantenha a tecla Ctrl pressionada
para fazer o zom.

34.5 Links teis


Neste captulo, estudamos como incorporar o reconhecimento de gestos no aplicativo.
Segue um link para voc continuar seus estudos.
Android Training - Detecting Common Gestures

Irttps://devel*nundroitl. com/Iruining/gestures/dete'tn html


2 CAPITULO 35

\...zrf Sensores e Google Fit



I

Neste capitulo, vamos estudar como monitorar os sensores disponveis no Android,


como acelermetro, giroscopio, temperatura, luminosidade, proximidade, entre
outros. c, ainda, desenvolver vrios aplicativos para demonstrar como utilizar
cada sensor.

Tambm vamos estudar a plataforma do Google Fit que faz parte do Google Play
Services.

35.1 Como obter a lista de sensores disponveis


O Android tem trs tipos de sensores: de movimento (acelermetro e giroscpio),
de posio (orientao, geomagnetismo, proximidade) e de ambiente (luminosi
dade, presso e temperatura).
A lista de sensores disponveis pode variar de acordo com cada aparelho, pois eles
dependem do hardware. Quanto mais novo o aparelho, mais sensores ele ter. Os
sensores tambm podem variar conforme a verso do Android, pois o suporte a
novos sensores vai sendo adicionado medida que o Android vai evoluindo. A
gura 35.], extrada da documentao oficial, mostra os sensores disponiveis para
cada verso do Android, mas lembre-se de que, se o sensor vai existir ou no no
aparelho, isso depender do hardware.
Fonte: http://developer android. com/guide/topics/sensors/sensors_ovcrvicw. html

867
Google Android - 4= edio

H I' ' Yes YesrfYes


7 VGS Yes A
rtfs
Y
Y Y
Y YY YY
YYY Y
Y Y Y Y
Y
Y Y
YYYY
Y
Y YY Y Y
Y

Figura 35.1 - Lista de sensores por plataforma.

A lista a seguir explica os detalhes de cada sensor.


TYPE_ACCELEROMETER - Informa a fora da acelerao em m/sz aplicada no dis
positivo nos eixos (x, yr e z), incluindo a fora da gravidade.
TYPE_AMBIENT_TEMPERATURE -Informa a temperatura ambiente em graus Celsius
(C). Essa constante foi criada no Android 4.0 e substitui a constante
TYPE_TEMPERATURE.

TYPE_GRAVITY -Informa a fora da gravidade em m/sz aplicada no dispositivo


nos eixos (x, yr z).
TYPE_GYROSCOPE - Informa as medidas da taxa de rotao em rad/s aplicada
no dispositivo nos eixos (x, yr z).
TYPE_LIGHT - Informa o nvel de luz no ambiente em lux.
TYPE_LINEAR_ACCELERATION - Informa a fora da acelerao em m/sl aplicada no
dispositivo em todos os eixos (x, yr e z), desconsiderando a fora da gravidade.
TYPE_MAGNETIC_FIELD - Mede a fora do campo magntico ambiente aplicado
no dispositivo nos eixos (x, yr e z) em UT (microteslas).
TYPE_PRESSURE - Mede a presso do ar ambiente em hPa ou mbar.
TYPE_PROXIMITY - Mede a proximidade de um objeto em centmetros (cm) em
relao tela vista do dispositivo. Este sensor usado pelo Android quando
um usurio atende uma chamada telefnica, para desligar o LCD da tela
se o aparelho estiver prximo da orelha do usurio.
TYPE_RELATIVE_HUMIDITY - Mede a umidade do ambiente em percentagem (/o).
(aptulo 35 I Sensores e Google Fit 869
0 TYPE_ROTATION_VECTOR - Mede a orientao de um dispositivo fornecendo OS
trs vetores de rotao.
0 TYPE_STEP_COUNTER - Retorna a quantidade de passos dado pelo usurio desde
que ligou o aparelho pela ltima vez.
TYPE_HEART_RATE - Retorna a quantidade de batimentos cardacos por minUEO
Para utiliz-lo, necessrio declarar a permisso android . permission.BODY_SENSORS.
Para acessar um sensor, basta chamar o mtodo getDefau1tSensor(tipo) da classe
SensorManager informando como parmetro uma das constantes acima que acaba
mos de ver.

Sensoranager sensorManager = (SensorManager) getSystemService(Contet.SENSOR_SERVICE);


Sensor sensor = sensoranager.getDefau1tSensor(Sensor.TYPE_ACCELEROMETER);

Ateno: o mtodo getDefauitSensor(tipo) pode retornar nulo caso o sensor no


exista no aparelho, portanto sempre verique o retorno antes de utilizar 0 sensor.

O mtodo getDefauitSensor(tipo) retorna o sensor padro para o tipo especificado.


Saiba que pode existir mais de um sensor do mesmo tipo, pois o aparelho pode
embutir sensores de diferentes fabricantes.
Para comearmos a brincar com sensores, o primeiro passo descobrirmos quais
sensores esto disponveis em determinado dispositivo. Isso pode ser feito com o
mtodo getSensorList(tipo), conforme demonstrado a seguir:
Sensoranager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);

O parmetro Sensor.WPE_ALL vai retornar todos os sensores disponveis, mas tambm


poderamos ter utilizado uma constante especfica de um tipo de sensor, como
pcn'exern;(x Sensor.TYPE_ACCELEROMETER,Sensor.TYPE_GYROSCOPE,Sensor.TYPE_LIGHT etc.

Para demonstrar como listar os sensores disponveis no celular, vamos criar uma
activity como esta. Caso prera, abra o projeto HeIIoSensores no Android Studio e
acompanhe a explicao.

ListaSensoresAttivity.java
public class ListaSensoresActivity extends AppConpatActivity inpiements Adapterview.
0nItemCiickListener {
private Sensoranager sensoranager;
private ListSensor sensorList;
@0verride
37 Google Android-4-ediao
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_lista_sensores);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
sensoranager = (Sensoranager) getSystenService(SENSOR_SERVICE);
sensorlist = sensoranager.getSensorList(Sensor.TYPE_ALL);
List<String nomes = new ArrayList<>();
for (Sensor s : sensorlist) {
nomes.add(s.getName() + " - " + s.getVendor() + " - + 5,getType());
}

Listview listview = (Listview) ndViewById(R.id.listView);


listview . set0nItemClickListener( this);
int layout = android.R.layout.simple_list_item_1;
ArrayAdapter adaptador = new ArrayAdapter(this, layout, nomes);
listview. setAdapter(adaptador );
}

@0verride
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Sensor sensor = sensorList.get(position);
String msg = sensor.getName() + " - " + sensor.getVendor();
Toast . makeTet( this , msg , Toast. LENGTH_SHORT) . show( ) ;
}

r? /res/layout/attivity_Iista_sensores.xmI
<?ml version="1.0" encoding="utf-8"?>
<LinearLayout _ _ .>
<ListView android:id="@+id/listview"
android:layout_width="match_parent" android:layout_height="match_parent" /

Ao executar esse exemplo, a lista de sensores disponveis no aparelho ser exibida


na lista conforme a gura 35.2. O resultado desta gura so os sensores disponveis
no Nexus 5 com Android 5.1 (Lollipop). A gura demonstra a rolagem para baixo
na lista para visualizar todos os sensores encontrados.
\

Nota: para testar esses exemplos, use um aparelho com Android, pois o emulador
no consegue simular os sensores. A lista de sensores disponveis vai variar de
acordo com cada dispositivo, pois eles esto fortemente atrelados ao hardware.
Quanto mais novo o aparelho. mais sensores ele tera.
871
Captulo 35 I Sensores e Google Fit

M
y \/Ll Il '
~/w l/"'*'F .
E.*' ,fzi fl
:a-s

Y l

,1;i 1 H l
^" l 11i T 1 11 iMl.) l'1Fl ll
Mp
hk

_ _ " ` 'I
Jzr L V ( 18 .ill JiMrx1 lJ

P A

Avz;Q_
1 ( Im (ill il
Al-`U*$"1'\ i Qi ( K/ H ufifl Qll 71
nullja

3 t x. 1 ;:z em ;_ Peri-.melef GU H /lU'39


. .1'\;C_~_..Lr z-.
. V- .. AH (Gi f ";m- aCJ t |'ED%%X,mAN
Pmwi: k `1 3`l.!C
ACH/I [V Mom on H

Figura 35.2 - Lista de sensores.

35.2 Testando os sensores


Para melhorar o exemplo anterior que mostra a lista dos sensores, vamos tratar
o evento de clique na lista para permitir que o usurio selecione um sensor. Ao
clicar na lista, vamos abrir uma nova activit); a qual vai ativar o sensor selecionado
para ler os dados.
Altere o mtodo onItenClick(. . .) que trata o evento de seleo na lista para navegar
para a activity TestSensorActivity.

ListaSensoresActivity.java

public class ListaSensoresActivity extends AppConpatActivity inplenents Adapterview.


0nItenClickListener {
@0verride
public void onItenClick(AdapterView<?> parent, View view, int position, long id) {
Sensor sensor = sensorList.get(position);
// Navega para a activity que vai ligar este sensor
Intent intent = new Intent(this,TestSensorActivity.class);
intent.putExtra("position",position); // posi0 do sensor na lista
startActivity(intent);
}

}
372 Google Android - 4 edio
Observe que no possvel passar o objeto Sensor como parmetro, pois ele no
do tipo Serializable nem do tipo Parcelable, portanto estou passando o ndice
selecionado na lista. Na outra activity teremos de consultar a lista de sensores
novamente e utilizar este ndice para obter o sensor selecionado.
Agora crie a nova activity com o wizard NewActivity> BIankActivity do Android Studio
e deixe o cdigo-fonte conforme demonstrado a seguir:

TestSensorActivity.java
public class TestSensorActivity extends AppConpatActivity inplenents SensorEventListener {
private Textview tvalor;
private Sensoranager sensoranager;
private Sensor sensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_sensor);
// Posio do sensor
int position = getIntent().getIntExtra("position",G);
// Busca o sensor pela posio
sensorManager = (Sensoranager) getSystenService(SENSOR_SERVICE);
ListSensor> sensorList = sensoranager.getSensorList(Sensor.TYPE_ALL);
sensor = sensorList.get(position);
tValor = (Textview) ndViewById(R.id.tValor);
// Mostra o none e fabricante do sensor
getSupportActionBar().setTitle(sensor.getNane());
getSupportActionBar().setDisplayHoneAsUpEnabled(true);
}

@Override
protected void onResune() {
super.onResune();
// Registra o sensor selecionado
sensoranager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORHAL);
}

@0verride
protected void onPause() {
super.onPause();
// Cancela o registro do sensor
sensoranager.unregisterListener(this);
}
873
Captulo 35 n Sensores e Google Fit

@0verride
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Chamado se mudou o status de preciso do sensor
}

@0verride
public void onSensorChanged(SensorEvent event) {
// L os valores do sensor
StringBuffer sb = new StringBuffer();
for (int i = 0; i < event.values.length; i++) {
sb.append(i).append(": ").append(event.values[i]).append("\n");
}

// Mostra os valores
tvalor.setTet(sb.toString());
}

No arquivo de layout, vamos ter um Textview para mostrar os valores do sensor.


O sensor retorna os dados no formato de um array do tipo oat, portanto pode
ter mais de um valor dependendo do tipo do sensor.

/res/Iayout/activity_test_sensor.xmI
<?nl version="1.0" encoding="utf-8"?>
<LinearLayout ... android:orientation="vertical" >
<TetView
android:id="@+id/tvalor" android:tet="16sp"
android:layout_width="wrap_content" android:layout_height="wrap_content" /

No cdigo da activity logo depois de obter o sensor selecionado, iniciado o


monitoramento chamando o mtodo registerListener(listener, sensor, rate), in
formando os seguintes argumentos:
Parmetro Descriao
SensorEventListener listener Interface para rezef lvaio iii _
Sensor sensor Sensor selecionado.
int rateUs Intervalo desejado para receber os eventos do sensor.
O parmetro rateUs do mtodo registerListener(listener, sensor, rate) pode receber
alguma destas constantes, as quais denem o intervalo (velocidade) com que Q
sensor vai enviar informaes para o aplicativo:
874 Google Android - 4 edio
onstante Descrio
SENSOR_DELAY_NORMAL lntervao padro para enviar osvalores sensor a cada
200.000 microssegunds.
SENSOR_DELAY_UI lnterva o de 60.000 microssegundos.
SENSOR_DELAY_GAME lntervao de 20.000 microssegundos.
SENSOR_DELAY_FASTEST Esta constante envia os valores do sensor o mais rpido
possve sem nenhum atraso.
Depois de registrar o listener, necessrio implementar a interface SensorEventListener
que contm os mtodos onSensorChanged(event) e onAccuracyChanged(precisao). O mtodo
onSensorChanged(event) chamado sempre que o sensor tiver informaes, conforme
o intervalo (rate) registrado no listener. Neste mtodo, a aplicao deve ler os va
lores do sensor, os quais so sempre retornados como um array de oat. No caso
dos sensores de acelermetro e giroscpio, so retornadas trs posies no array
referentes aos eixos x, y e z.
public void onSensorChanged(SensorEvent event) {
// L os valores do sensor
oat x = event.values[0];
oat y = event.values[1];
oat z = event.values[2];
}

Mas outros sensores como luminosidade, presso e proximidade retornam apenas


um valor, portanto podemos ler somente a posio 0 (zero) do array
public void onSensorChanged(SensorEvent event) {
// L o valor do sensor
oat valor = event.values[0];
}

Se estiver na dvida de como ler os valores, simples: basta verificar o tamanho


do array
O mtodo onAccuracyChanged(precisao) chamado para informar que a preciso
do sensor mudou e o parmetro accuracy representa algum dos seguintes status:
SENSOR_STATUS_ACCURACY_L0w para sinal baixo, SENSOR_STATUS_ACCURACY_MEDIUH para sinal
mdio, SENSOR_STATUS_ACCURACY_HIGH para sinal alto e SENSOR_STATUS_UNRELIABLE para sinal
indisponvel. Em uma aplicao real, devemos monitorar esses status para vericar
a qualidade do sinal do sensor.
Algo importante sobre os sensores o tratamento do ciclo de vida da aplicao. O
monitoramento do sensor deve ser iniciado no mtodo onResume() e parado no me
todo onPause(). Acredito que a esta altura do livro voc j saiba por que isso assim.
875
Captulo 35 I Sensores e Google Fit

protected void onResune() {


super.onResume();
// Registra o sensor selecionado
sensornanager.registerListener(this, sensor, Sensorrlanager.SENSOR_DELAY_NORMAL);
}

@0verride
protected void onPause() {
super.onPause();
// Cancela o registro do sensor
sensorManager.unregisterListener(this);
}

Para testar este exemplo, execute o projeto em um dispositivo real, depois clique

z 1 ~
em algum sensor da lista. A figura 353 mostra o resultado do sensor de aceler
metro. Veja que no array existem trs posies, que so referentes aos eixos x, y
e z do acelermetro.
Q V 3 23:27
,Tp v . ue. , .fz _, p `'*,
?f<fFe"`iefffeeaatetetttefei A tfff`

' u 0198312207
MPU651-5Acceler0fe1ef~lnvenSen9-1 : a |ua>o
z o ni; sua

AKB963 Magnetometer - AKM - 2

AK8963 Magnetometer Uncalibrated


AKM - 14

Figura 35.3 - Sensor de acelermetro.

A gura 35.4 mostra o resultado ao clicar no sensor do contador de passos. Neste


caso o array contm apenas uma posio, que a quantidade de passos. Apro
veitando, caso precise desenvolver algo relacionado a contador de passos ou de
batimentos cardacos, recomendo estudar o aplicativo do Google Fit e a Fitness

_...
API. No nal do captulo faremos um exemplo com a Fitness API.

., . tfzz
Rotation Vector - QTI - 11
W! . .. V . v 123.26
0 54 0

Step Detector - QTI 18

f S=e;o==~ori-19 _ 3

Figura 35.4 - Sensor de contador de pa5505_


876 Google Android - 4' edio
35.3 Sensor de luminosidade

Embora o exemplo anterior j tenha sido suciente para mostrar como listar todos
os sensores c ainda obter o valor de cada sensor, vamos explorar alguns sensores
individuais apenas para praticar.
O sensor de luminosidade representado pela constante Sensor.TYPE_LIGHT e infor
ma a intensidade da luz e claridade no ambiente externo. Nesse sensor, para ler
o valor da intensidade da luz, basta acessar a primeira posio do array em que
o nmero oat representa o valor obtido do sensor.
public void onSensorChanged(SensorEvent event) {
// L o valor do sensor
oat valor = event.values[0];
}

O intervalo de nmeros que o sensor vai retornar variar conforme o fabri


cante. Para descobrir o valor mximo retornado pelo sensor, utilize o mtodo
sensor.getMaimunRange( ). Sendo assim, o sensor pode retornar valores entre O (zero) e
o valor mximojustamente por isso, para demonstrar o sensor de luminosidade,
vamos criar um arquivo de layout com a view SeekBar.

i /res/layout/activity_sensor_seekbar.xmI
<LinearLayout . . . android:orientation="vertical">
<TetView android:id="@+id/tvalor"
android:layout_width="wrap_content" android2layout_height="wrap_content" />
<SeekBar android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="ll_parent" android:layout_height="wrap_content"
android:padding="2Gdp"
android:progress="0" android:nax="100" />

Agora, vamos escrever o cdigo da activity que vai utilizar o sensor do tipo lumi
nosidade com a constante Sensor.TYPE_LIGHT. Veja que estamos obtendo o sensor
com o mtodo sensoriianager.getDefaultSensor(tipo); dessa maneira, possvel obter o
sensor padro caso exista mais de um. De qualquer forma, importante validar se
o sensor existe, pois, como j foi explicado, tudo depende do hardware do aparelho.

f) LuminosidadeActivity.java

public class LuninosidadeActivity extends AppCompatActivity inplenents SensorEventListener {


private static nal int TIPO_SENSOR = Sensor.TYPE_LIGHT;
private Sensoranager sensorManager;
877
Captulo 35 1 Sensores e Google Fit

private Sensor sensor;


private SeekBar progress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sensor_seekbar);
progress = (SeekBar) ndViewById(R.id.progress);
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = sensoranager.getDefaultSensor(TIPO_SENSOR);
if (sensor != null) {
// Dene o valor mximo do SeekBar
oat max = sensor.getMaxinumRange();
progress.setHax((int) max);
} else {
Toast.makeTet(this, "Sensor no disponivel", Toast.LENGTH_SHORT).show();
}

@0verride
protected void onResume() {
super.onResume();
if (sensor != null) {
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}

@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}

@0verride
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Mudou o status de preciso do sensor
}

@0verride
public void onSensorChanged(SensorEvent event) {
// L o valor do sensor (intensidade da luz)
oat valor = event.values[6];
((TextView) ndViewById(R.id.tValor)).setText("Luz: " + valor);
// Atualiza o valor no SeekBar
progress.setProgress((int) valor);
}

}
878 Google Android - 4 edio
Caso voc tenha um aparelho com o sensor de luminosidade, brinque com os
resultados testando o sensor em ambientes com bastante luz ou escuros. Voc
vera que a SeekBar vai mudar constantemente, pois est medindo a intensidade
da luz no local.

A gura 35.5 mostra o resultado. No lado esquerdo, coloquei o aparelho embaixo


de uma mesa de forma a pegar pouca luz. No lado direito, coloquei o aparelho

l l _. i A. "'_:

direto no sol, ento note que a SeekBar est totalmente preenchida.

.,_ . _ . _. ._.. x , ,, ._ ,. ,_ .
l f __ z. z ;.;, 2 . ',`.;_j.:zI ,ff .. _ .-Z* y *'" _ gs'

Figura 35.5 - Sensor de luminosidade.

35.4 Sensor de temperatura


O sensor de temperatura representado pela constante Sensor.WPE_AMBIENT_TEHPERATURE
e informa a temperatura em graus Celsius (C). Para utilizar esse sensor, o pro
cedimento exatamente igual ao do exemplo anterior. O valor retornado ser a
temperatura externa.
public void onSensorChanged(SensorEvent event) {
// Valor da temperatura em C
oat valor = event.va1ues[0];
}

importante ressaltar que antigamente era utilizada a constante Sensor.TYPE_TEMPERATURE


para esse sensor, mas a partir do Android 4.0 (API Level 14) essa constante foi
descontinuada (deprecated) e, agora, a constante Sensor.TYPE_AMBIENT_TEMPERAiURE
deve ser utilizada.
Como a utilizao desse sensor idntica do exemplo anterior, no vamos
demonstrar nenhum cdigo.

35.5 Sensor de proximidade


O sensor de proximidade representado pela constante Sensor.TYPE_PRt)XIMITY c
possibilita vericar o quo prximo o aparelho esta de determinado objeto.
.. ... `A . . . `~
879
Captulo 35 u Sensores e Google Fit

. - - ' astar Android pela aplicao


Esse sensor utilizado diretamente que controlaO
no _mamOS as
ligaoes telefomcas, pois, sempre que atendemos Uma 1183930 C QP
d t la ara econo

. _ . , ~ , lo da
celular do ouvido, ele detecta a proximidade e desliga o LCD 21 C ,P f mos
mizar recursos durante a ligaao. Quando terminamos de conversar, ao
o celular do ouvido, automaticamente o LCD da tela ligado.
Para utilizar esse sensor, o procedimento e exatamente igual ao do exemp
luminosidade ou temperatura, e o sensor retorna ap enas um valor, que 3 dis
tncia em centmetros do objeto que est prximo do celular. Portanto, para ler
esse valor, podemos utilizar o seguinte cdigo:
public void onSensorChanged(SensorEvent event) {
// Distncia em centimetros
oat valor = event.va1ues[6];
}

Nesse sensor, tambm interessante obterl o' va


` ooruemaxim
ele ode
qp retornar
com o mtodo sensor.getMaimunRange(), pois assim sabemos quais os valores que o
sensor retorna entre O (zero) e o valor mximo. Geralmente o sensor retorna valores
de O a 5 centmetros de proximidade, mas isso pode variar, dependendo do hardware.

35.6 Sensor de acelermetro


O sensor de acelermetro representado pela constante Sensor.TYPE_ACCELEROHETER e
permite monitorar a acelerao aplicada ao aparelho nos eixos x, y e z.
Esse um dos sensores mais conhecidos, pois frequentemente utilizado em jogos,
mas tambm um dos mais complicados de entender, porque fundamental com
preender bem o sistema de coordenadas em que ele atua, conforme a figura 35.6.
.V

I
Z

Figura 35.6 - Sistema de coordenadas do Senso):


88 Google Android - 4 edio
O valor desse sensor varia entre O e aproximadamente 9,81, medido em m/sl.
Com base nessa gura, com o celular posicionado na vertical, podemos assumir
que 0 valor do eixo Y aproximadamente +9,81. Caso voc gire o celular nesse
eixo e o deixe de ponta-cabea, o valor de y ser -9,81 aproximadamente.
Agora, se voc girar o celular para a direita, para deix-lo na horizontal, o valor
no eixo X ser aproximadamente +9,81. Caso voc gire o celular para a esquerda,
deixando-o tambm na horizontal, o valor do eixo X ser aproximadamente -9,81.
O eixo Z ca com o valor positivo, prximo de +9,81, quando o celular est deitado
sobre uma mesa, de forma que no h foras exercendo presso nos eixos x e y
Com base nessas informaes, interessante criarmos um exemplo para voc
visualizar os valores retornados desse sensor, porque logo vamos ter de discutir
alguns comportamentos que esse sensor apresenta ao girar o celular.
Vamos criar a activity AceleronetroActivity com o seguinte arquivo de layout:

/res/layout/activity_sensor_aceIerometro.xmI
<LinearLayout . . . android:orientation="vertical" >
<TextView android:id="@+id/tX" android:tet="@string/"
android:layout_width="wrap_content" android:layout_height=wrap_content" />
<TetView android:id="@+id/tY" android:tet="@string/y"
android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TetView android:id="@+id/tZ" android:tet="@string/z"
android:layout_width="wrap_content" android:layout_height="wrap_content" /

Esse layout tem trs Textviews para exibir os valores dos eixos x, y e z. j o cdigo
da activity funciona da mesma forma que os outros sensores. Basta ler os dados
do sensor do tipo Sensor.TYPE_ACCELEROMETER:

AceIerometroActivity.java
public class AceleronetroActivity extends AppConpatActivity inplenents SensorEventListener {
private static nal int TIPO_SENSOR = Sensor.TYPE_ACCELEROHETER;

// Faa tudo igual aos outros exemplos aqui...


// Basta iniciar o sensor, os resultados sero lidos no onSensorChanged(event)
@0verride
public void onSensorChanged(SensorEvent event) {
oat sensorX = -event.values[G];
oat sensorY = event.va1ues[1];
oat sensorz = event.va1ues[2];
881
Captulo 35 I Sensores e Google Fit

Textview tx = (Textvew) ndVewById(R.id.tX);


Textvew tY = (Textview) ndVewById(R.id.tY);
Textview tZ = (Textview) ndVewById(R.d.tZ);
if (tx != null) {
tx.setTet("X: " + sensorX);
tY.setTet("Y: " + sensorY);
tZ.setText("Z: " + sensorl);
}

Para testar o acelermetro, vamos travar a orientao da activity na vertical. Faa isso
adicionando o atributo androd:screen0rentation="portrait" ao arquivo de manifesto.
lsso necessrio porque, caso a orientao do celular mude para a horizontal, os
valores do acelermetro niudaro tambm. Logo explicarei mais detalhes sobre isso.
<activity androd:name=".Ace1erometroActvity" android:screen0rentation="portrait" />

Agora, execute essa activity A gura 35.7 mostra o resultado.


A primeira parte da figura mostra o resultado com o celular na vertical. Neste
caso o eixo que sofre a maior alterao o v que mede aproximadamente +9,81
nessa posio.
A figura do meio mostra o resultado ao girar o celular para a direita, at deixa-lo
na horizontal, de forma que os botes de Home, Voltar e Menu do Android fiquem fi
esquerda da tela. Nessa posio, o eixo que sofre alterao o x.
Na parte da direita da gura, o celular est no cho ou sobre a tncsa. Dessa vez,
o eixo z sofrer a alterao.

"l z'
L '* \;\;\'
` ^tWY*5 1 x Missas

Figura 35.7 - lilores do acelermetro nos eixos x, y e z.

At aqui, transcorreu tudo bem. Os valores retornados pelo sensor dg 1lm_


tro foram exatamente os esperados. Contudo, vamos remover a congurao da
activity que estava travando a tela na vertical para permitir que o usuario gn. ) ~et
celular. Portanto, deixe o arquivo AndroidManiest.xm1 da seguinte fmmq.
<actvity androd:name=".AcelerometroActvty" /
382 Google Android -4- edio
Feito isso, vamos executar novamente o exemplo e conferir o resultado.
Ao deixar o celular na vertical, tudo funciona normalmente, pois essa a orien
taao padro dos smartphones. Portanto, no h novidades no eixo y
O problema est no eixo x, pois ele reflete os valores na horizontal. Dessa forma,
gire a tela e conra o resultado dos valores retornados pelo acelermetro. O re
sultado que os valores de x e y na horizontal esto invertidos.
Para auxiliar seu entendimento, s voc imaginar que est jogando um jogo
como o clssico Labirinto. Nele, voc deve mover uma bola pelo caminho para
encontrar a sada. Nessa posio, com o celular deitado na horizontal, para jogar
geralmente voc segura o celular com o visor do LCD para cima. Com o celular
nessa posio, o que esperamos que, ao girar o celular para a esquerda e direita,
o eixo que sofrer a alterao seja o x; e, ao mover para cima e para baixo, seja o y
Mas se o celular estiver deitado, no isso o que acontecer, porque, no importa
a posio do celular, o sistema de coordenadas do acelermetro nunca mudar.
Para entender melhor essa situao, veja os eixos x, y e z do sistema de coordena
das, com o celular na vertical e horizontal, conforme demonstrado na gura 35.8:
.Y

i
l

,:.
l

j,__ z X
'~>~ z z~-
l* `__._""""'*--_..._=_

I
A

r* k

Figura 35.8- Sistema de coordenadas.

Nessa gu ra, podemos ver claramente que, ao girar o celular, o sistema de coorde
nadas x, y e z no muda, portanto no reflete a posio atual do aparelho. Nesse
caso, o que faremos compensar essa rotao, pois possvel identicar em qual
posio o celular se encontra e trocar os valores dos eixos x e y retornados pelo
acelermetro. Para descobrir a orientao atual do dispositivo, podemos utilizar
a classe Display e o mtodo getRotation():
HindowManager windowrlanager = (windowanager) getSystemService(wINDON_SERVICE);
Display display = windowlianager.getDefaultDisplay();
883
Captulo 35 1 Sensores e Google Fit

// Para descobrir a rotao do aparelho


switch (display.getRotation()) {
case Surface.ROTATION_9:
// vertical
case Surface.ROTATION_90:
// Horizontal (botes para a direita)
case Surface.ROTATION_180:
// Vertical: de ponta-cabea
case Surface.ROTATION_270:
// Horizontal (botes para a esquerda)
}

Esse cdigo permite descobrir a rotao e compensar os valores de x e ) que,


na prtica, sero trocados, pois a rotao do celular est diferente do sistema de
coordenadas utilizado pelo acelermetro, visto que esse sistema nunca muda.
Para organizar o projeto, deixaremos este trecho de cdigo na classe SensorUtil:

SensorUtiI.java

public class Sensortil {


public static oat[] Acelerometro(Context context, SensorEvent event) {
oat sensorX = 0;
oat sensorv = 0;
oat sensorZ = 0;
Hindowanager windowanager = (windowanager)
context.getSystemService(Contet.wINDOH_SERVICE);
Display display = windowManager.getDefaultDisplay();
// Verica a rotao do aparelho
switch (display.getRotation()) {
case Surface.ROTATION_0:
// Vertical
sensorX = -event.values[@];
sensorY = event.values[1];
break:
case Surface.ROTATION_9Q:
I/ Horizontal (botes para a direita)
sensorX = event.values[1];
sensorY = event.values[6];
break;
case Surface.ROTATION_180:
// vertical: de ponta-cabea
sensorX = -event.values[0];
884 Google Android - 4 edio
sensorY = -event.values[1];
break;
case Surface.ROTATION_270:
// Horizontal (botes para a esquerda)
sensorX = -event.values[1];
sensorY = -event.values[0];
break;
}

// Troca os valores de e y
oat[] values = new oat[3];
values[0] = sensorX;
values[1] = sensorY;
values[2] = sensorZ;
return values;
}

public static String getRotationString(Context context) {


windowManager windowManager = (windowManager)
context.getSystemService(Context.wINDOw_SERVICE);
Display display = windowManager.getDefaultDisplay();
// Verica a rotao do aparelho
switch (display.getRotation()) {
case Surface.ROTATION_G:
return "ROTATION_G";
case Surface.ROTATION_90:
return "ROTATION_90";
case Surface.ROTATION_180:
return "ROTATION_180";
case Surface.ROTATION_270:
return "ROTATION_270";
}
return null;
}

Agora, podemos atualizar o cdigo da activity para corrigir os valores do sensor,


conforme a rotao do aparelho:

AcelerometroAttivity.java
public class AcelerometroActivity extends AppCompatActivity implements SensorEventListener {
private long time;

@Override
public void onSensorChanged(SensorEvent event) {
885
Captulo 35 I Sensores e Google Fit

long now = System.currentTimeMillis();


if(now - time > 1000) {
time = now;
oat values[] = Sensortil.xAceleronetro(this, event);
oat sensorX = values[0];
oat sensorY = values[1];
oat sensorl = values[2];
Textview tx = (Textview) ndViewById(R.d.tX);
Textview tY = (Textview) ndViewById(R.id.tY);
Textview tZ = (Textvlew) ndViewById(R.id.tZ);
Textview tMsg = (Textview) ndViewById(R.d.tMsg);
if (tx != null) {
if(t != null) { t.setText("X: " + sensorX); }
if(tY != null) { tY.setTet("Y: " + sensorY); }
if(tZ != null) { tZ.setTet("Z: " + sensorZ); }
f(tHsg != null) { tMsg.setText("Rotao: "
+ Sensortil.getRotationString(ths)); }
}

Nesse exemplo, para efeito de testes, foi inserido mais um Textvew com o iden
ticador @+d/tMsg no arquivo de layout, para mostrarmos o retorno do mtodo
SensorUtil.getRotatonString(context).

/res/Iayout/activity_sensor_ateIerometro.xmI

I'',
<LinearLayout . . . androd:orientation="vertlcal" >
<TextVew androd:id="@+id/tX" . . . />
<TetVew android:id="@+id/tY" . . . />
<TetVew android:id="@+d/tZ" . . . />
TextVew android:d="@+id/tsg"
android:layout_width="wrap_content" androd:layout_heght="wrap_content" />

Feito isso, execute novamente o exemplo para conferir o resultado. interessante


que voc gire o celular e entenda os resultados retornados pelo sensor. Na verdade o
que a classe Sensorutl faz e inverter os eixos x e y caso o celular esteja na hgrizomal

Nota: importante ressaltar que a orientao padro de um aparelho pode no


ser sempre vertical. Por exemplo, nos tablets, a orientao padro geralmente
horizontal. Nesse caso, quando a rotao indicar ROTATION_0, signicar ue el
est na horizontal, isto , em sua posio padro. Portanto, lembre-se lle qu
33 ooogizmmia-4-eio
essas constantes de rotao indicam se o aparelho teve sua tela rotacionada,
a partir de sua posio padro, que pode ser vertical nos smartphones ou
horizontal nos tablets.

35.7 Movendo uma view pela tela com o acelermetro


O sensor do acelermetro muito utilizado em jogos. Vamos brincar um pouco
apenas para ter uma ideia de como podemos movimentar uma view pela tela
utilizando o acelermetro.
Vamos criar uma view customizada que vai desenhar uma imagem de um boneco
do Android. A imagem ser desenhada nas posies x e ) conforme os valores dos
atributos dx e dy definidos na classe. Na activity vamos ler os valores do sensor do
acelermetro e atualizar os atributos dx e dy desta vievw assim obteremos o efeito
de o boneco do Android car se movendo pela tela.

BonecoAndroidView.java
package br.con.livroandroid.sensores;

public class BonecoAndroidView extends View {


private Paint paint = new Paint();
private Drawable drawable;
private int dx,dy;
public BonecoAndroidView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public BonecoAndroidView(Context context) {


super(context);
init(context);
}

private void init(Context context) {


// Congura o fundo cinza e cria a imagen
drawable = context.getResources().getDrawable(R.drawable.android);
drawable.setBounds(0, 0, drawable.getIntrinsicHidth(),
drawable.getIntrinsicHeight());
1

@0verride
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
887
Captulo 35 u Sensores e Google Fit

paint.setColor(Color.LTGRAY);
// Desenha o fundo da view (um quadrado cinza)
canvas.drawRect(0, 0, getHidth(), getHeight(), paint);
paint.setTextSize(toPixels(16));
paint.setColor(Color.BLACK);
canvas.drawText("x/y:" + d+"/"+dy,50,50,Dt);
// Desenha a imagem das posies x e y
canvas.translate(dx, dy);
drawable.draw(canvas);
}

private oat toPixels(int dp) {


nal oat scale = getResources().getDisplayMetrics().density;
oat px = dp * scale + 0.5f;
return px;
}

public void setDx(int dx) { this.dx = dx; }


public void setDy(int dy) { this.dy = dy; }
public int getDy() { return dy; }
public int getDx() { return dx; }
public Drawable getDrawable() { return drawable; }
1

Na sequncia, crie a classe AceleronetroJogoActivity. Ela vai herdar de Aceleronet roActivity


que criamos anteriormente, para aproveitarmos o cdigo que ativa o sensor. Assim,
ela car apenas com a lgica de movimentar a view

AcelerometroJogoActivity.java

public class AceleronetroJogoActivity extends AcelerometroActivity {


// Posies para desenhar a imagem
private BonecoAndroidView androidview;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aceleronetro_jogo);
androidview = (BonecoAndroidView) ndViewById(R.id.bonecoView);
}

@0verride
public void onSensorChanged(SensorEvent event) {
super.onSensorChanged(event);
// L os valores retornados pelo acelermetro
oat values[] = SensorUtil.xAceleronetro(this, event);
oat sensorX = values[0];
888 Google Android - 4 edio
oat sensorY = values[1];
// Vai incrementando os valores de x e y para o objeto se mover
int newdx = androidView.getDx() + (int) sensorX * 10;
int newdy = androidView.getDy() + (int) sensorY * 16;
int imgw = androidView.getDrawable().getIntrinsicNidth();
int imgH = androidView.getDrawable().getIntrinsicHeight();
// No deixa o valor car negativo ou maior que o tamanho da tela
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
if ( (ewdx < 0 II newdx + imgw > displayMetrics.widthPiels)) {
/I Atualiza o eixo x
androidview.setDx(newdx);
}

int actionBarH = displayMetrics.heightPixels - androidView.getHeight();


if (5 (ewdy < 0 II newdy + imgH > displayMetrics.heightPixels - actionBarH)) {
// Atualiza o eixo y
androidview.setDy(newdy);
}

// Redesenha a view
androidview.invalidate();
1

No layout da activity vamos colocar um Textview para mostrar os valores dos eixos
x e y. Tambm vamos adicionar a view customizada que vai desenhar o boneco
do Android.

@ /res/layout/actvty_aceIerometro_jogo.xmI
<LinearLayout . . . android:orientation="vertical">
<TextView android:id="@+id/tX"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="@string/x" android:tetSize="18sp" />
<TextView android:id="@+id/tY"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:tet="@string/y" android:tetSize="18sp" />
<br.com.livroandroid.sensores.BonecoAndroidView android:id="@+id/bonecoview"
android:layout_width="match_parent" android:layout_height="0dp"
android:layout_weight="9" />

Ao executar esse exemplo, voc poder mover o bonequinho do Android pela tela
como se estivesse jogando. A Figura 35.9 demonstra o resultado quando o boneco
est praticamente no centro da tela. A implementao e simples. Estamos mo
nitorando o sensor do acelermetro e atualizando a propriedade dx e dy da view
889

. . . . - ` o.
. ' 'No la Out
.~'1
(aptulo 35 I Sensores e Google Fit

nhado um
customizada. Com isso a view vai desenhar a imagem I1553 P05' y
mostramos os valores de x e y do acelerometro. Dentro da view e dese d
quadrado cinza e a imagem do Android que fica se movendo. No top0 5qUer (
do quadrado, desenhada a posio ldx
o boneco
e dy do
naAndroid
qua I foi
_
desenhado. Acredito que se voc executar o exemplo no emulador cara m2lS
simples de entender; portanto, faa isso. Note que este exemplo s1mpleS. HIQ
tratou aspectos mais avanados como aceleraao e gravidade, TT1215 0 0bJUV O
apenas brincar um pouco.

roozms

Figura 35.9 - Movendo um objeto pela tela com o acelermetro.

35.8 Google Fit


O Google Fit uma plataforma completa que permite aos desenvolvedores obter

,, . . . , '
os dados dos sensores e ainda salvar ou ler os dados na nuvem do Google. In
clusive existe o aplicativo Google Fit para celulares, tablets e wearables. Tambm
existe o site https://t.google.com/ para visualizarmos os dados salvos pelo aplicativo
padro do Google Fit.

O melhor de tudo que essas APIs esto disponveis para o desenvolvedor' logo QSQ
voce esteja desenvolvendo aplicativos para a area de sade que precisam utilizar Q
contador de passos e batimentos cardacos, as APIs do Google Fit podem ajud lo
O Google Fit faz parte do Google Play Services, ento para utilizar esses servios
precisamos nos conectar aos servidores do Google, conforme aprendemo s no
89 Google Android -4' edio
captulo 23, sobre localizao e GPS. Isso feito porque os dados dos sensores
podem car salvos na nuvem do Google, conforme ilustrado na gura 35.10.
f/F `\z~~

\i I.
Google
FIDGSS SOTG .

"""" `\1__,//F
Android Device

i"
p Android App

..:_,j.1Ir~
toogle ily s.>rwt~s 5

Android Fitness APIs

" ` * ' ;.' <i" ;f =.?,:;' ix' oz ;~ H .;

* ` - {'=,* f i Q' <; 5 - ' - '


I

Android Sensors

Wearable Devices

Figura 35.10 - Google Fit.

As APIs do Google Fit, tambm conhecidas como Fitness API, so compostas de


outras subAPIs, cujas principais esto listadas a seguir:
SensorsAp - Esta a API mais utilizada e permite ler os dados dos sensores
de contador de passos e batimentos cardacos de forma simples.
RecordngAp - Permite coletar os dados e gravar no Google Fit.
SessonsAp - Permite ao aplicativo gerenciar sesses de atividades do usurio.
\

HistoryAp - Permite consultar os dados inseridos na base do Google Fit.


Agora vamos ver um pouco de cdigo para criar um exemplo de pedmetro, ou
seja, um aplicativo que conta os passos do usurio. Tudo comea criando um
objeto do tipo GoogIeApC1ent para se conectar ao Google Play Services. Ao fazer
isso, adicionada a Fitness API no escopo da classe Goog1eApCIent.
891
Captulo 35 u Sensores e Google Fit

/I Cria o cliente para se conectar ao Google Play Services


GoogleApiClient nGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Fitness.SENSORS_API) // Adiciona a Fitness API
.addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
.add0nConnectionsCallbacks(this)
.add0nConnectionFailedListener(this)
.build();
nGoogleApiClient.connect();

No captulo 23, sobre localizao, estudamos como se conectar ao Google Play


Services, portanto vamos continuar com as partes referentes ao Google Fit. QuandO
a conexo com o servidor do Google for realizada, o mtodo onConnected(bundle)
ser chamado e neste momento podemos iniciar a API do Google Fit. O trecho
de cdigo a seguir mostra como iniciar o contador de passos.
@0verride
public void onConnected(Bundle connectionHint) {
I/ Conectou-se ao Google Play Services
// Ento podemos nos conectar ao Google Fit
// Contador de passos (TYPE_STEP_COUNT_DELTA)
SensorRequest req = new SensorRequest.Builder()
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setSamplingRate(1, TimeUnit.SECONDS).build();
// Ativa o contador de passos
Fitness.SensorsApi.add(nGoogleApiClient,request, this);
}

Feito isso, sua classe ter de implementar a interface 0nDataPointListener do Google


Fit, e os resultados do sensor sero entregues no mtodo onDataPoint(dataPoint).
@0verride
public void onDataPoint(DataPoint dataPoint) {
I/ Ler os dados do pedmetro aqui...
}

Pronto! No mtodo onDataPoint(dataPoint) basta ler os passos, que sero entregues


automaticamente pela API do Google Fit, a qual compatvel com o Android 23
(API Level 9). Sua vantagem que ela faz parte de toda uma plataforma inclugive

' it,
facilitando persistir todos os dados e consult-los na nuvem do Google,
Um aspecto negativo que para o Google Fit funcionar necessria uma conexo
com a internet para pelo menos se conectar ao Google Play Services Depois que
o aplicativo fez a conexo pela primeira vez, ele pode trabalhar de forma offline
mas de qualquer forma necessria essa conexo inicial Utilizando 0 Goog|e F =
892 Google Android - 4' edio
os dados sero salvos na nuvem do Google, e depois com a HstoryApi possvel
inclusive consultar esses dados.

Caso seja um requisito do seu projeto que o aplicativo deva funcionar totalmente
offline, no possvel utilizar o Google Fit. Nesse caso deve-se utilizar o sensor
do tipo TYPE_STEP_COUNTER, conforme estudamos neste captulo. A desvantagem desta
soluo que se perde o poder de toda a plataforma do Google Fit.
Mas chega de conversa! Vamos criar um exemplo de aplicativo que utiliza o Google
Fit para criar um pedmetro.
Primeiramente, declare a dependncia do Google Play Services:

app/buiId.gradIe

compiie 'com.goog1e.android.gms:play-services:7.0.0'

Na sequncia, crie a activity com o seguinte cdigo. Basicamente ela se conecta


ao Google Play Services como j fizemos no exemplo de localizao, e no mtodo
onConnected(bund1e) iniciada a conexo com o Google Fit para contar os passos.

Fo GoogIeFitPedometroAttivity.java

public class Goog1eFitPedometroActivity extends AppCompatActivity implements


GoogieApiCiient.ConnectionCa1ibacks, Goog1eApiC1ient.0nConnectionFai1edListener {
private static nai String TAG = "1ivroandroid";
private GoogieApiCiient mGoog1eApiC1ient;
private Textview text;
private int qtdePassos;
private static nai int REQUEST_0AUTH = 1;
@0verride
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_goog1e_t_pedometro);
text = (Textview) ndViewById(R.id.text);
getSupportActionBar().setDisp1ayHomeAsUpEnabied(true);
// Congura o objeto GoogieApiCiient
mGoogleApiC1ient = new GoogleApiCiient.Bui1der(this)
.addApi(Fitness.SENSORS_API)
.addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
.addtonnectiontaiibacks(this)
.add0nConnectionFai1edListener(this)
.buiid();
Captulo 35 I Sensores e Google Fit

@Override
protected void onStart() {
super.onStart();
// Conecta ao Google Play Services
if(!nGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
toast("mGoogleApiClient.connect()");
nGoogleApiClient.connect();
}

@0verride
protected void onDestroy() {
super.onDestroy();
// Desconecta ao sair
mGoogleApiClient.disconnect();
}

@Override
public void onConnected(Bundle bundle) {
toast("Conectado ao Google Play Services!");
startPedoneter();
}

private void startPedoneter() {


Log.d("livroandroid","startPedometer");
// Listener do Fitness API que conta os passos
0nDataPointListener listener = new 0nDataPointListener() {
@0verride
public void onDataPoint(DataPoint dataPoint) {
for (Field eld z dataPoint.getDataType().getFields()) {
if (dataPoint.getDataType().equals(DataType.TYPE_STEP_COUNT_DELTA)) {
Value val = dataPoint.getValue(eld);
Log.d("livroandroid","Valor Pedometro: " + val);
qtdePassos += val.asInt();
run0nUiThread(new Runnable() {
@0verride
public void run() {
tet.setTet("Passos: " + qtdePassos);
}

});
}

};
// Contador de passos (TYPE_STEP_COUNT_DELTA)
SensorRequest req = new SensorRequest.Builder()
.setDataType(DataType.TYPE_STEP_COUNT_DELTA)
.setSanplingRate(1, TineUnit.SECONDS)
.build();
I/ Ativa a API do Fitness
PendingResult result = Fitness.Sensorshpi.add(mGoogleApiClient, req, listener)
Log.d("livroandroid","Google Fit pedometer ativado: " + result);
}

@0verride
public void onConnectionSuspended(int cause) [
toast("Coneo interronpida.");
}

@0verride
public void onConnectionFailed(ConnectionResult result) {
if (!result.hasResolution()) {
// Se for algum erro de conguraco ou servio, mostra alerta
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),this, G).show();
return;
}

// Caso contrrio, pode ser porque o usurio no autorizou o acesso


try {
result.startResolutionForResult(this,REQUEST_0AUTH);
} catch (IntentSender.SendIntentEception e) {
Log.e(TAG,"Erro ao autorizar: " + e.getMessage(), e);
}

@0verride
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_OAUTH) {
if (resultCode == RESULT_OK) {
// Depois que o usurio autorizou, faz login no Google Play Services
if (!nGoogleApiClient.isConnecting() && !nGoogleApiClient.isConnected()) {
nGoogleApiClient.connect();
}

private void toast(String s) {


Log.d("livroandroid","> " + S);
Toast.makeTet(getBaseContet(), s, Toast.LENGTH_SHORT).show();
}
895
Captulo 35 n Sensores e Google Fit
No projeto de
Esse cdigo est um pouco simplicado para facilitar a leitura.
odi vo, a, iqll
l1
exemplo do livro voc encontrar uma varivel authInProgress nO L E
controla se a janela de autorizao est aberta, para n o abrir a janela duas veZS
de de
No arquivo de layout teremos apenas um Textvew para mostrar a Cluamlda
passos retornada pelo Google Fit.

/res/layout/attivity_google_t_pedometro.xmI
<LnearLayout ... androd:orientatlon="vertca1">
<TetView android:text="@strtng/qtde_passos"
androd:1ayout_wtdth="wrap_content androd:1ayout_hetght="wrap_content />
Textvew androld:d="Q+ld/text"
android:l.ayout_utdth="wrap_content" androd:1ayout_heght="wrap_C0tt" />
/LtnearLayout>

iii /res/values/strings.xmI
<`SOUl'CS>

<string nane="qtde_passos"Quantidade de Passos</strng>


/resources>

Q
I ""' II
Para o cdigo funcionar, necessrio habilitar a Fitness API na pgina de console
do Google Developers (https://console.developersgoogle.com). Faa isso no menu
APIs&autl1>APls. Lembre-se de que j zemos isso nos captulos 22 e 28, sobre mapas
e GCM push. A gura 35.11 mostra estas trs APIs habilitadas na minha conta.
,- -__ ... ___

l API Library enabled Ails (3) I

Some
their
API
APIs are enabled automatically.
services. I
Fitness API
Google Cloud Messaging for Android;
Qoogle Maps Anafgia API vz
I

Figura 35.11 - Console do Google.

O prximo passo gerar a chave de autenticao do Google Fit no menu APIs&auth


>(rdentials. Essa chave ser do tipo Olluth, pois vamos precisar que 0 usurio enrrg
com sua conta do Google no aplicativo para permitir o acesso. A autenticao
do tipo 0Auth permite ao aplicativo ler os dados do usurio, mas garante que suas
896 Google Android - 4 edio O

informaoes condenciais quem privadas. Mas no se preocupe com isso, pois


o .Google controla tudo. Para gerar a chave, entre no menu APIs&auth > (redentials e
clique no boto Create New (IientlD na seo do 0Auth.

Na janela que vai abrir (Figura 35.12), selecione o item lnstalled application e o tipo de
aplicao Android. Digite o nome do pacote do aplicativo e o SHA1 Fingerprint
do certicado. Lembre-se de que voc aprendeu a obter o SHA1 Fingerprint no
captulo 22, sobre mapas. Feito isso, clique no boto (reateClientID. O legal dessa chave
que ela fica associada sua conta do Google, porm no preciso fazer nada no
aplicativo. Basta criar essa congurao no servidor do Google, e est tudo pronto.

Create Client ID
Application type
Web application
Accaased by web browsers over n netwonr
Service account
Calls Google APis on behall of your application instead of an end-user Learn more
O installed application
Runs on a desktop computer or handheld dente (like Androd or Phone)

lnstalled appllcatlon type


O Android Learn moro
Chrome Application l earn more
|0S Learn more
PlayStation 4
Other

AP! rcquests are Sent dlrectly to Google lrom your clients' Android devices
Google verllles that each request orlglnates lrom an Android application that
matches the package name and SHA1 slgnmg CI2l1i|C\ lmgorprmt name listed
below.

Package namo
br_com.Iivroandroidsensores

Signing certilicate lingerprint (SHAI)


Cl '66'56:93tDF:DE0B B9:DC:EDI762D7'65B7:l0:DC:l F`3F'OD4l

Deep linking
Enabled
O Disabled

Crcale Clicnl ID Cancel

Figura 35.12 - Criando a chave O/luth.

Uma vez que a configurao e a chave 0Auth foram criadas, vamos executar o aplica
tivo. Na primeira vez que voc acess-lo, ser solicitado que o usurio faa o login
com alguma conta do Google, conforme a figura 35.13. Isso acontece justamente
porque foi criada uma chave de autenticao do tipo 0Auth.
897
Captulo 35 I Sensores e Google Fit

Google

.:_ _
mmol . ,
*OWWV

ri||o;=nsor>s gostaria dc'

Ii/rozmdro|d(gmail com l Pvririltr- ver f.'1J


u.o TI1CO'f do almdades
no Google FI
Adicionar conta
9 ll*11|lf'V(*f'P\lS dados dv
Llwelfll UP no ~_ooq|e Fil !<c.rl|zaio nrfnazennrioz

1...
(1

.:-_

(ANCU .-U1 Cl*

Figura 35.13 -Autenticando o usurio na aplicao.

Depois da autenticao, o mtodo onConnected(bund1e) ser chamado e neste momen


to podemos iniciar a API do Google Fit. Se tudo correr bem, a API ser iniciada e
logo voc receber dados do sensor de contador de passos. Portanto, para testar,
d uma caminhada com o celular em mos que a quantidade de passos ser
mostrada na tela, conforme a gura 35.14.
Repare que no cdigo utilizamos a constante DataType.TYPE_STEP_COUNT_DELTA do
Google Fit para receber os dados do sensor. Essa constante retorna a quantidade
de passos feita desde a ltima leitura, portanto o aplicativo deve armazenar essa
quantidade e fazer a soma de passos para mostrar a quantidade total. Mas lembre
-se de que, se for necessrio, podemos utilizar a HistoryApi para consultar os dados
salvos na nuvem do Google.

.._.,
Quantidade de Passos i
Passos: '18
_ _. __. _.________
"*f- -_ 1r

Figura 35.14 - Pedmetro em ao.


393 Google Android - 4 edio
35.9 Links teis

Neste captulo estudamos como utilizar os sensores disponveis na plataforma


do Android.

Com a utilizao de sensores possvel implementar funcionalidades bem inte


ressantes nos aplicativos. Vimos at exemplos simples de como mover uma view
com o acelermetro, algo que muito comum em jogos.
Tambm estudamos o Google Fit, que a nova plataforma de Fitness do Google
e talvez uma das reas que mais crescer nos prximos anos.
Seguem alguns links para continuar seus estudos.
API Guides - Sensors Overview

http://developer android. com/guide/topics/sensors/sensors_o1/ervietuhtml


Google Developers - Google Fit

https://developers. google. com/t/

Google Play Services - Fitness

https://developerandroid.com/reference/com/google/android/gms/tness/Fitness.html
E
~1\ cAPtuLo 36
J Bluetooth
\
"i

Bluetooth muito utilizado como forma de comunicao entre dispositivos, mas


seu uso um tanto quanto trabalhoso.
Neste captulo, vamos criar um aplicativo de chat capaz de enviar e receber men
sagens, explicando todos os conceitos para facilitar o aprendizado.

36.1 Vericando se o dispositivo suporta Bluetooth


A principal classe da API de Bluetooth a BluetoothAdapter, a qual representa o
adaptador do Bluetooth disponvel no dispositivo.
Para obter o BluetoothAdapter, utiliza-se o mtodo getDefaultAdapter(). Embora a
maioria dos dispositivos com Android tenha Bluetooth, recomendado testar
para vericar se o adaptador do hardware de Bluetooth existe no dispositivo,
conforme demonstrado neste cdigo:
// Bluetooth adapter
BluetoothAdapter btfAdapter = BluetoothAdapter.getDefaultAdapter();
if(btfAdapter != null) { // Existe Bluetooth }
else { // No tem Bluetooth }

Depois de obter o objeto BluetoothAdapter, podemos executar diversas aes, como


fazer uma busca para encontrar dispositivos com o Bluetooth ativado;
obter a lista de dispositivos pareados;
conectar a outro dispositivo Bluetooth para enviar mensagens.

Nota: para testar os exemplos deste captulo, recomendo ter dois dispositivos
Android, pois vamos enviar mensagens de um para 0ur0_
90 Google Android ~ 4 edio
36.2 Ativando o Bluetooth por programao
Com o objeto BluetoothAdapter em mos, o prximo passo veri car se o Bluetooth
est ativado no dispositivo. Isso feito com o mtodo isEnabled(), que retorna true
caso o Bluetooth esteja ligado. Caso no esteja, podemos solicitar ao usurio para
ativar as conhguraes do Bluetoth.
if (btfAdapter.isEnabled()) { // Bluetooth est ligado }
else { // precisa ligar o Bluetooth }

Felizmente, possvel ativar as configuraes do Bluetoth via programao. Para


isso, precisamos declarar as permisses BLUETOOTH e BLUETO0TH_ADMIN:

AndroidManifest.xml
<manifest . . _
<uses-permission android:nane="android.pernission.BLUETO0TH_ADHIN" /
<uses-permission android:nane="android.permission.BLUETO0TH" /
<application . . . />

Com essas permisses, podemos utilizar as classes de Bluetooth e tambm disparar


uma Intent que vai abrir um dialog para o usurio ativar o Bluetooth:
Intent enablelntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enablelntent, O);

Para demonstrar esse funcionamento, crie uma simples activity chamada


BluetoothCheckActivity conforme demonstrado a seguir:

lffil BIuetoothCheckActivity.java

public class BluetoothCheckActivity extends AppCompatActivity {


protected static nal String TAG = "livroandroid";
protected BluetoothAdapter btfAdapter;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Bluetooth adapter
btfAdapter = BluetoothAdapter.getDefaultAdapter();
if (btfAdapter == null) {
Toast.makeTet(this, "Bluetooth no disponivel neste dispositivo.",
Toast. LENGTH_LONG) . Show( ) ;

// Vamos fechar a activity neste caso


nish();
return;
Capitulo 36 u Bluetooth

if (btfAdapter.sEnab1ed()) { // Bluetooth Ok.


T' 1p';.' ?P'f DTu#*^' <' IQO TQS.LEHCTH_LONG)'ShOw();
} else { f 2+ , `<, , pede ora o oof gf
,//
.' z,-z<.fo-~--~ ~~f~^'e 'OC_O'*SSO-BLUETOOTH_ADHINA />
Intent enablelntent = new Intent(BluetoothAdapter.ACTIOH_RQUEST_ENABLE)i
startActiv1tyForResuIt(enablelntent, 6);
}
1
4

J'r
rwJ"
Q I_
..,
. ~. "H ,'~
\ .I
.._. \.' ~_.Q;;;_-z, )
'1kJf)":?\_(:\.1'-.y (."1t(-Udh {

' ..` '.


if {btfAdapter.tsEb1ed()) {
,~ - ~-' '*<~. 53.e'-Jt foi tgdol , Toest.LENGTHMLNC/-ShJi

\;-fr. ttzf .t t~\t'u:l. \~ mei- dt-1\.1r I]'1utth lig;1d, :1 um


H; 1 . zr'wr\mft11t'!utuLi }_<._1tntr;ir1_um1 mtm
\il &"L :J tI\ t1t.\`|xy
li I mi rcccbcr 0 rctrn no mt
t' tfr* th rxar 1atLir|11gzu SPet-n>th. (,J clc rcs>n1
-. , l)(1()
___l
.ev .z H tit~:n- \'.1l1d;r sc dc t'z1r BlUCIf)({h rui l1;;;1d
'fil'
*_` z
t rt z ttmJ que 0 usurio hguc I%luetth.

Fzmrzol Az" 1
t . ' l..Uu1() 1) BL1lt_'[:'[_
36.3 Listando os dispositivos pareados
Uma funcionalidade bem comum na comunicao por Bluetooth a busca de
dispositivos j pareados. Se voc no est acostumado com Bluetooth, o pare
amento necessrio antes de trocar informaes entre um dispositivo e outro,
sendo feito geralmente na primeira vez que esses dispositivos estabelecem uma
conexo, informando um tipo de senha que ambos precisam aceitar. Depois que
os dispositivos esto pareados, eles podem trocar informaes.
Para recuperar a lista de dispositivos pareados, utilize o mtodo getBondedDevices():
Set pareados = btfAdapter.getBondedDevices();
for (Biuetoothevice device : lista) {
String nome = device.getName();
String address = device.getAddress();
// Fazer algo com essa informao...
}

Note que a lista do tipo BiuetoothDevice, objeto que representa um dispositivo.


A classe Biuetoothevice no contm muitos mtodos, os mais utilizados so o
getName(), que retorna o nome do dispositivo, e o getAddress(), que retorna o ende
reo do hardware do Bluetooth. Esse endereo nico e por meio dele podemos
nos conectar ao dispositivo.
A classe ListaPareadosActivity do projeto de exemplo deste captulo mostra como
exibir a lista de dispositivos pareados em um Listview. Mas como isso simples
demais, vamos pular para o prximo exemplo.

36.4 Buscar novos dispositivos Bluetooth


Para brincar com Bluetooth, precisamos buscar os dispositivos que esto com o
Bluetooth ativado. Depois de encontrar algum dispositivo, preciso fazer o pare
amento para somente depois iniciar a conexo e troca de informaes.
Para iniciar a busca, utilizado o mtodo startDiscovery( ) da classe B1uetoothAdapter
e para cancelar utilizado 0 mtodo canceiDiscovery().
Os resultados da busca iniciadas pelo mtodo startDiscovery() no so entregues
por meio de um tradicional listener, e sim por meio de um Broadcas`tReceiver, o
qual precisamos registrar dinamicamente na activity para receber broadcasts das
mensagens. Essas mensagens de broadcast so disparadas pelo Android com o
intuito de indicar que a busca iniciou (DISCOVERY_STARTED), que um dispositivo foi
Captulo 36 n Bluetooth . D .1\s
zs
903

_ . . ,, .. ~' '. ', i cxrn


z ~a
ritra tia
encontrado (ACTION_FOUND) e que a busca terminou (ACTION_DISCOVERY:FINISHE ) i

. . . - ' " uaoth


mensagens com as aes DISCOVERY_STARTED e ACTION_DISCOVERY_FINISHED sao chamt flo
apenasinru1vez,para1nd1carn1unefuT1dalausca.bAasz1nncnsagen1 d
a ACTION_FOUND pde ser chamada varias vezes para cada d1sp0SltlVO Cw
Agora, crlaremos uma activity de exemplo que val buscar os d1sp0Sltl\/05 Bhle
e mostra-los numa lista:

ListaDevicesActivity.java

public class ListaDevicesActivity extends BluetoothCheckActivity


implements Adapterview.0nItemClickListener [
private ProgressDialog dialog;
protected List lista;
private Listview listview;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lista_devices);
listview = (Listview) ndViewById(R.id.listView);
// Inicia a lista com os devices pareados
lista = new ArrayList<BluetoothDevice(btfAdapter.getBondedDevices());
// Registra o receiver para receber as mensagens de dispositivos pareados
this.registerkeceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
// Registra o receiver para receber a mensagem do nal da busca.
this.registerkeceiver(mReceiver, new
IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
}

@0verride
protected void onResume() {
super.onResume(); // Garante que no existe outra busca sendo realizada
if (btfAdapter.isDiscovering()) { btfAdapter.cancelDiscovery(); }
// Dispara a busca
btfAdapter.startDiscovery();
dialog = ProgressDialog.show(this, "Exemplo", "Buscando dispositivos Bluetooth...",
false, true);
}

// Receiver para receber os broadcasts do Bluetooth


private nal BroadcastReceiver mReceiver = new BroadcastReceiver() {
// Quantidade de dispositivos encontrados
private int count;
@0verride
Google Android - 4= edio

public void onReceive(Context context, Intent intent) {


String action = intent.getAction();
// Se um device foi encontrado
if (Bluetoothbevice.ACTION_FOUNO.equals(action)) {
/I Recupera o device da intent
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// Apenas insere na lista os devices que ainda no esto pareados
if (device.getBondState() != Bluetoothevice.BOND_BONDED) {
lista.add(device);
Toast.makeTet(contet, "Encontrou: " + device.getName()+":" +
device.getAddress(), Toast.LENGTH_SHORT).show();
count++;
}

} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
// Iniciou a busca
count = 0;
Toast.makeTet(contet, "Busca iniciada.", Toast.LENGTH_SHORT).show();
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// Terminou a busca
Toast.makeTet(contet, "Busca nalizada. " + count +
" devices encontrados", Toast.LENGTH_LONG).show();
dialog.dismiss();
// Atualiza o listview. Agora vai ter todos os devices pareados,
// mais os novos que foram encontrados
updateLista();
}

};
@0verride
protected void onDestroy() {
super.onDestroy();
// Garante que a busca cancelada ao sair
if (btfAdapter != null) {
btfAdapter.cancelDiscovery();
1

// Cancela o registro do receiver


this.unregisterReceiver(mReceiver);
}

private void updateLista() {


// Cria o array com o nome de cada device
List nomes = new ArrayList();
905
Captulo 36 I Bluetooth

for (BluetoothDevice device : lista) {


// Neste exemplo, esta varivel boolean sempre ser true, pois esta lista 2
// somente dos pareados
boolean pareado = device.getBondState() == BluetoothDevice.BOND_BONDED,
nomes.add(device.getName() + " - " + d@vC-9f^ddfSS() +
(pareado ? " *pareado" : ""));
}

// Cria o adapter para popular o Listview


int layout = android.R.layout.simple_list_item_1;
ArrayAdapter adapter = new ArrayAdapter(this, layout, HONGS);
listview.setAdapter(adapter);
listview.setOnItemClickListener(this);
}

@Override
public void onItemClick(AdapterView<?> adapterview, View view, int idx, long id) {
// Recupera o device selecionado
BluetoothDevice device = lista.get(idx);
String msg = device.getName() + " - " + device.getAddress();
Toast.makeTet(this, msg, Toast.LENGTH_SHORT).show();
}

O arquivo de layout dessa activity contm apenas um Listview para mostrar a lista
de dispositivos j pareados e aqueles que sero encontrados pela busca.

/res/layout/activity_Iista_devices.xmI
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout. . . android:orientation="vertical" >
<ListView android:id="@+id/listview" android:layout_weight="1"
android:layout_width="match_parent" android:layout_height="0dp" />

A classe ListaDevicesActivity lha da classe BluetoothCheckActivity que criamos


anteriormente. Assim, herdamos 0 comportamento de vericar se o Bluetooth
est ligado. Justamente por isso, o trecho de cdigo que busca os dispositivos
foi deixado no mtodo onResume() do ciclo de vida, pois pode ser que o aplicativo
saia da activity para solicitar ao usurio que ative o Bluetooth e, quando voltar,
o mtodo onResume() ser utilizado para preencher a lista.

O cdigo da activity inicia a lista com os dispositivos j pareados e depois dispara


a busca com o mtodo startDiscovery(). Quando um dispositivo encontrado a
mensagem de broadcast com a ao ACTION_FOUND enviada. Nesse momento, o
906 Google Android - 4 edio
receiver que registramos no codigo intercepta a mensagem e recupera o objeto
BluetoothDevce que representa o dispositivo encontrado.
A Figura 36.2 mostra o resultado desse exemplo. liu executei a activity ein um
Nexus 5. Esse dispositivo ja esta pareado com um Nexus 5, portanto o primeiro
dispositivo da lista. Lembrando que os dispositivos pareados podem ser obtidos
com o mtodo getBondedDevices() e a lista foi inicializada com eles:
lista = new ArrayList<BluetoothDevce(btfAdapter.getBondedDevi.ces());

Durante a busca, foi encontrado um Nexus que tambm foi adicionado na lista.
Quando a busca finalizada, a mensagem com ao ACTION_DISCOVERY_FINISHED
enviada e interceptada pelo receiver. Nesse momento, foi mostrado um toast com
a quantidade de dispositivos encontrados, que no meu caso foram trs.
l

' Nzzz. is - f`z1A*JDi';Qit>'?


rparearlo
Nexus i - l ti 88 QL l A il

Lltil il\la` iai YA all DE gi- L"i`5

Busca naliznda 3 devices encontrados

Figura 36.2 - Busca finalizada com disptisilivris encntiuds.

36.5 Deixando o Bluetooth visvel para ser encontrado


No exemplo anterior fizemos a busca pelos dispositivos Bluetooth e adicionamos
todos os resultados na lista. Porm, importante voc entender que somente
dispositivos visveis podem ser encontrados.
Para o dispositivo ser encontrado na busca, no basta o Bluetooth estar ligado. li
preciso que ele esteja visvel. Atualmente, no Android 4.0 ou superior, ao abrir a
tela de configuraes automaticamente, o dispositivo ficara visvel. Mas caso seja
_ . is os
_ . . . _ . . ~ is arar~ uma
Captulo 36 n Bluetooth

11
preciso deixar um dispositivo visivel por programa210, Pdems d _t_vO
intent, a qual recebe como parametro o tempo em segund05 que O P
car visvel para a busca:
907

// Deixa o dispositivo visivel para ser encontrado na busca


Intent discoverablelntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
discoverablelntent.putEtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverablelntent);

d.
Essa intent solicita ao usurio que permita que seu dispositivo seja encontrad
por outros durante o tempo em segundos informado. Ao dispar-la, o An r0l
vai mostrar um alerta na janela conforme a gura 363. Se o usuario aceitar a
solicitao, esse dispositivo poder ser encontrado na busca.

Dica: caso voc tenha executado o exemplo anterior e seu dispositivo no foi
encontrado na busca, deixe-o visvel e repita a busca.

Um app Cueror'1iar seu telefone


vr;iv'el para outros dis:p;>s>tivoG
B1ntt.<>h por 300 segimfios.

V'l,-1; z

Figura 36.3 - Deixando 0 Bluetooth visvel.

36.6 Criando um BIuetoothDevice pelo endereo


A classe BluetoothDevice permite lermos o nome e o endereo Bluetooth de um
dispositivo. Se o endereo Bluetooth de um dispositivo for previamente co
nhecido, podemos obter rapidamente o objeto Bluetoothevice com o mtodo
getRenoteDevice(address) sem a necessidade de uma busca.

O cdigo-fonte a seguir mostra como obter um Bluetoothevice a partir de um


endereo conhecido, que neste caso o 40:FC:89:6D:0A:81.

Bluetoothevice device = btfAdapter.getRenoteDevice("40:FC:89:6D:0A:81");


if(device != null) {
String none = device.getNane();
String endereco = device.getAddress();
}
98 Google Android - 4' edio
Mas, na prtica, geralmente os dispositivos so encontrados pela busca com o m
todo startDiscovery() ou com o mtodo getBondedDevices( ), que retorna os dispositivos
que j esto pareados. O importante obter o objeto Bluetoothbevice de alguma
forma, pois por meio dele podemos iniciar uma conexo com esse dispositivo.

36.7 (hat em Bluetooth


j aprendemos a usar o Bluetooth e buscar dispositivos. O prximo passo criar
uma conexo entre dois dispositivos para enviar e receber mensagens.
No Bluetooth uma conexo realizada no modelo cliente-servidor. Um dispositivo
inicia o modo servidor e ca aguardando conexes. Feito isso, outro dispositivo
(cliente) conecta-se a esse servidor. Depois que a conexo estabelecida. ambos
os dispositivos podem abrir uma InputStream para ler mensagens e uma 0utputStream
para enviar mensagens.

Nota: as interfaces InputStream e 0utputStream so clssicas do java e permitem ler


e escrever em um canal de comunicao aberto, que, neste caso, a conexo
Bluetooth.

Para iniciar o Bluetooth no modo servidor, a primeira tarefa criar um objeto UUID.
O importante utilizar este mesmo UUID no aplicativo cliente para fazer a conexo.
private static nal UUID uuid = UUID.fromString("fa87c0d0-afac-11de-8a39-086626Gc9a66");

Nota: o UUID (Universally Unique ldentier) um formato-padro de texto


com 128 bits e pode ser criado com qualquer gerador disponivel na internet.
Neste exemplo, o UUID vai identicar a conexo Bluetooth.

Com esse UU ID, e possvel chamar o metodo listenUsingRfcommHithServiceRecord(string,


uuid) da classe B1uetoothAdapter conforme demonstrado a seguir:
// Abre o socket servidor (quem for conectar precisa utilizar o mesmo UUID)
BIuetoothServerSocket serverSocket = btfAdapter.1istenUsingRfcommHithServiceRecord(
"LivroAndroid", uuid);

O retorno e um objeto do tipo BluetoothServerSocIet que representa o servidor do


socket criado. Feito isso, para definitivamente iniciar o servidor e aguardar as
mensagens, o mtodo accept() precisa ser chamado.
Captulo 36 I Bluetooth

// Fica aguardando algum conectar (esta chamada bloqueante)


BluetoothSocket socket = serverSocket.accept();

I .la a O
O mtodo accept() bloqueante, isto , a prxima linha ser executada somente
depois que algum cliente se conectar. Quando um dispositivo se conecta ao sr
vidor, o objeto BluetoothSocket retornado do metodo accept() e pode Ser UI1 12
para obter a InputStream e a 0utputStream.

.1d
Nota: se voc j trabalhou com sockets em java no ter nenhuma di culdade
para prosseguir a partir daqui. Basicamente vamos criar um servidor de socket
com a classe BluetoothServerSocket, e quando um cliente se conectar e obti o o
socket que a classe BluetoothSocket. De resto, toda a comunicao feita pelas
interfaces InputStream e 0utputStream, como de costume em aplicaes java.

Para encapsular a lgica do chat, vamos criar a classe ChatController, que vai rece
ber o socket BluetoothSocket no construtor, assim como um listener para delegar
os eventos quando uma mensagem for recebida.

ChatControIIer.java
public class ChatController {
private static nal String TAG = "chat";
private BluetoothSocket socket;
private InputStream in;
private OutputStream out;
private ChatListener listener;
private boolean running;
public interface ChatListener {
public void onHessageReceived(String msg);
}

public Chattontroller(BluetoothSocket socket, ChatListener listener) throws IOException {


this.socket = socket;
this.in = socket.getInputStream();
this.out = socket.get0utputStream();
this.listener = listener;
this.running = true;
}

// Inicia a leitura da InputStream


public void start() {
new Thread(){
@0verride
910 Google Android - 4' edio
public void run() {
running = true;
// Faz a leitura
byte[] bytes = new byte[1624];
int length;
// Fica em loop para receber as mensagens
while (running) {
try {
Log.d(TAG,"Aguardando mensagem");
// L a mensagem (ca bloqueado at receber)
length = in.read(bytes);
String msg = new String(bytes, 0, length);
Log.d(TAG,"Mensagem: " + msg);
// Recebeu a mensagem (informa o listener)
listener.onMessageReceived(msg);
} catch (Exception e) {
running = false;
Log.e(TAG,"Error: " + e.getMessage(),e);
}

}.start();
}

public void sendHessage(String msg) throws IOEception {


if (out != null) { out.write(msg.getBytes()); }
}

public void stop() {


running = false;
try {
if (socket != null) { socket.close(); }
if (in != null) { in.close(); }
if (out != null) { out.close(); }
} catch (IOEception e) { }
}

Essa classe obtm a InputStream e a 0utputStream do BluetoothSocket e inicia uma


thread para car continuamente lendo as mensagens da InputStream. Sempre que
uma mensagem lida, o evento entregue por meio da interface ChatListener com
o mtodo onMessageReceived(msg). O mtodo sendMessage(msg) dessa classe pode ser
utilizado para enviar uma mensagem ao socket aberto, que na prtica vai escrever
os dados na 0utputStream.
Captulo 36 n Bluetooth 911

Agora vamos implementar a activity cliente, a qual vai rC ber um objeto do tipo
BluetoothDevice por parmetro da intent e vai se cone ctar ao dispositivo.

BIuetoothChatCIientActivity.java
public class BluetoothChatClientActivity extends BluetoothCheckActivity
implements ChatController.ChatListener {
protected static nal String TAG = "livroandroid";
// Precisa utilizar o mesmo UUID que o servidor utilizou para abrir o socket servidor
protected static nal UUID uuid = UUID.fromString("fa87c6d0-afac-11de-8a39-9806266c9a66");
protected Bluetoothbevice device;
protected Textview tMsg, tMsgRecebidas;
protected ChatController chat;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_bluetooth_chat);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
tHsg = (Textview) ndViewById(R.id.tMsg);
tHsgRecebidas = (Textview) ndViewById(R.id.tMsgRecebidas);
// Device selecionado na lista
device = getIntent().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
try {

// Faz a conexo se abriu no modo chat cliente


if(device != null) {
getSupportActionBar().setTitle("Conectado: " + device.getName());
// Faz a conexo utilizando o mesmo UUID que o servidor utilizou
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);
socket.connect();
// Inicia o controlador chat
chat = new ChatController(socket, this);
chat.start();
ndViewById(R.id.btEnviarMsg).setEnabled(true)
}

} catch (IOException e) {
error("Erro ao conectar: " + e.getHessage(), e);
}

public void onClickEnviarMsg(View view) {


String msg = tMsg.getTet().toString();
try {
chat.sendHessage(msg);
tHsg.setText("");
912
Google Android - 4' edio

// Mostra o texto enviado na rea do chat


String s = tMsgRecebidas.getText().toString();
tMsgRecebidas.setTet(s + "\n> " + msg);
} catch (IOEception e) {
error("Erro ao escrever: " + e.getMessage(), e);
}

private void error(nal String msg, nal IOEception e) {


Log.e(TAG, "Erro no client: " + e.getMessage(), e);
run0nUiThread(new Runnable() {
@0verride
public void run() {
Toast.makeTet(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}

});
}

@0verride
public void onHessageReceived(nal String msg) {
Log.d(TAG,"onMessageReceived (recebeu uma mensagem): " + msg);
// chamado numa thread, portanto use o run0nUiThread
run0nUiThread(new Runnable() {
@0verride
public void run() {
String s = tMsgRecebidas.getText().toString();
tMsgRecebidas.setText(s + "\n<< " + msg);
}

});
}

@0verride
protected void onDestroy() {
super.onDestroy();
if(chat != null) {
chat.stop();
}

Essa classe recebe um objeto do tipo BluetoothDevice por parmetro da intent e inicia
uma conexo obtendo o BluetoothSocket. Depois de iniciar a conexo, a lgica do
chat delegada para a classe Chattontroller, a qual encapsula como ler e escrever
mensagens na InputStream e OutputStream.
Captulo 36 I Bluetooth .._ -z,.,,,, .- "z ruma
i)s ulT1
No layout desta activity vamos ter apenas um campo de texto Para dlflta
913

mensagem e um botao para envia-la. Na parte dc baixo do layout, KW"


Textview que vai mostrar todas as mensagens trocadas no chat.

r] /res/layout/activity_bIuetooth_chat.m|
<?nl version="1.0" encoding="utf-8"?>
<LinearLayout . . . android:orientation="vertical" >
<LinearLayout android:layout_width="natch_parent" android:layout_height="wrap_content
android:orientation="horizontal" >
EditText android:id="@+id/tMsg"
android:layout_width="0dp" android:layout_weight="1"
android:layout_height="wrap_content"
android:inputType="tet" android:singleLine="true"/>
Button android:id="@+id/btEnviarMsg"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text:"@string/enviar_msg" android:enabled="false"
android:onClick="onClickEnviarMsg" />

TextView android:id="@+id/tsgkecebidas"
android:layout_width="natch_parent" android:layout_height="0dp"
android:layout_weight="1" android:gravity="top"/

Como eu disse antes, a activity cliente vai receber um objeto do tipo BluetoothDevice
por parmetro pela intent. Logo, vamos alterar a classe ListaDevicesActivity de
modo que, ao seleeionarmos um dispositivo da lista, seja feita a navegao at a
activity cliente do chat.

i ListaDevicesActivity.java

public class ListaDevicesActivity . . . {

public void onItemClick(AdapterView<?> adapterview, View view, int idx, long id) {
// Recupera o device selecionado
BluetoothDevice device = lista.get(id);
// Vai para a tela para enviar a mensagen
Intent intent = new Intent(this, BluetoothChatClientActivity.c1as5);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
startActivity(intent);
l
}
914
Google Android - 4' edio

O cdigo da parte cliente do chat est pronto, falta apenas o servidor. Sendo assim,
crie a classe BluetoothChatServerActivity.

BIuetoothChatServerActivity.java
public class BluetoothChatServerActivity extends BluetooththatClientActivity
implements ChatController.ChatListener {
private static nal UUID uuid = UUID.fromString("fa87c0d9-afac-11de-8a39-08G0266c9a66");
private boolean running;
private BluetoothServerSocket serverSocket;
@0verride
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
I/ Deixa o servidor disponivel para busca
BluetoothUtil.makeVisible(this,39G);
}

@0verride
protected void onResume() {
super.onResume();
// Inicia a thread do chat para no travar a UI
new ChatThread().start();
}

class ChatThread extends Thread {


@0verride
public void run() {
try {
// Abre o socket servidor (o cliente precisa utilizar o mesmo UUID)
serverSocket
btfAdapter.listenUsingRfcommHithServiceRecord("LivroAndroid", uuid);
Log.d(TAG, "Servidor aguardando conexo...");
// Aguarda at algum conectar (esta chamada bloqueante)
BluetoothSocket socket = serverSocket.accept();
if (socket != null) {
// Mostra o device nome do device que conectou
nal Bluetoothevice device = socket.getRemote0evice();
run0nUiThread(new Runnable() {
@0verride
public void run() {
getSupportActionBar().setTitle("Conectado: " + device.getName());
ndViewById(R.id.btEnviarMsg).setEnabled(true);
Toast.makeText(getBaseContext(),"Conectou: "
+ device.getName(),Toast.LENGTH_SHORT).show();
}
915
Captulo 36 1 Bluetooth

});
// Algum conectou
hCh
chat = new ChatController(socket, Bluetoot a tServerActivity.this);
chat.start();
}

} catch (IOEception e) {
Log.e(TAG, "Erro no servidor: " + e.9etNSS9(), G);
running = false;
}

@0verride
protected void onDestroy() {
super.onDestroy();
try {
if (serverSocket != null) {
serverSocket.close();
}

} catch (IOEception e) {
}

Essa classe inicia o servidor do chat abrindo um socket com a classe


BluetoothServerSocket. Quando a parte cliente conecta-se ao socket, o mtodo
serverSocket.accept() retorna a conexo do socket no objeto BluetoothSocket. Logo
depois, iniciado o chat com a classe ChatController.
Pronto, terminamos o cdigo do servidor. Inclusive ele utilizar o mesmo arquivo
de layout do cliente, pois ambos precisam enviar e receber mensagens.
Naturalmente, o cdigo que controla o processo de conexo diferente entre servi
dor e cliente, mas ambos no final fazem a conexo e obtm o socket BluetoothSocket.
Com o socket em mos, ambos, servidor e cliente, utilizam a classe Chattontroller
para controlar a leitura e escrita das mensagens, e a partir disso o processo o
mesmo para ambos.
Note que a classe BluetoothChatServerActivity inclusive herda de BluetoothChatClientActivity
j que, como eu j disse, fora a parte de conexo, o restante do cdigo entre as duas
classes o mesmo, pois ambos, servidor e cliente, podem enviar e receber mensagens
916 Google Android - 4 edio
Nota: o servidor do Bluetooth abre um socket servidor que aguarda conexes.
Isso feito pelo mtodo accept(), que car bloqueado at algum cliente se
conectar. Depois que a conexo realizada, o objeto BluetoothSocIet representa
a conexo e, por meio dele, podemos obter a InputStream e a OutputStream para ler
e escrever mensagens.

Agora vamos executar o chat em Bluetooth para brincarmos um pouco e depois


recomendo que voc estude bem o cdigo. Naturalmente, para continuar, tenha
em mos dois dispositivos Android. Nos exemplos que vou mostrar o servidor
foi aberto em um Nexus 5 e o cliente em um Nexus S.
No projeto de exemplo do livro temos a lista com os exemplos conforme a
gura 36.4. Clique no exemplo Iniciar servidor (hat, o qual exatamente a classe
BluetoothChatServerActvty que acabamos de estudar. Feito isso, o servidor do chat
vai abrir e aguardar uma conexo. Veja que importante que o servidor esteja
visvel, caso o cliente precise fazer uma busca para encontr-lo.

I i
z
r:**. #113
fg..-l-'-.=;' . 'ivz
'~ .z~:,.
.~"'=" ": 'kz .
vsv 1 s 4 z :
* 4 Sen
;1"} ' ' f*"" ',,:?

Verificar e ativar Bluetooth l


Lista de Devices pareados
Buscar devices l

Sair j
Ficar visivel

Iniciar servidor Chat


x

Bluetooth est ligado' i

Figura 36.4 - Iniciado o servidor do chat.

Logo depois, pegue o outro celular e faa uma busca pelos dispositivos conforme
a figura 36.5. No meu caso fi encontrado o celular Nexus 5, que o servidor.
917
Captulo 36 n Bluetooth

Nz\vu'= L ix:z=11u.i`~~@<>l5%

Busca finalazada. 1 devices encontrados

Figura 36.5 - B1.stw1d os dispositiios.

Ao clicar na lista, vamos chamar a activity cliente do chat B1uetoothChatClentActvty


que vai iniciar a conexo entre os dois dispositivos. Caso os dispositivos ainda
no estejam pareados, e feito o pareamento em ambos, conforme a figura 36.6.
Note que, caso os dispositivos j estejam pareados, no necessrio nem fazer a
busca, pois podemos obter rapidamente os dispositivos j conhecidos.

Figura 36.6 - Parcando os diso5i1iW5_


918 Google Android - 4' edio
Depois de parear os dispositivos, a conexao e realizada. Em ambos os dispositivos
illCllls` e servidor), o aplicativo mostra no titulo da action bar o ttome do t>llIl`()
*l'5P0$1N\'U. Se a conexao tor lwem-sttcedida. o boto Enviar Mensagem e halwilitado.
entao basta comecar a digitar. A figura 30.7 mostra tinta conversa entre os dois
celulares. No cltat. o simbolo ">>" indica uma mensagem enviada; e o stttlwolo
"<<". utna mensagent recelwida.

QO
I 140

.__ -.
. ^ *Nexu5 i 4r__ s
V W ENVMRMENSAGEM
Enviar mensagem
_.._.___..~....,._.....,_..... V
(i.t\\.
t\\l\i`1*` izi. 'i 'ri I l .t
l

.1``l.lt*v'l:'l\`*
tl
1

qwe txt yin t oup


aSdigh1kl
|~ 2 c v b n nt (3
Tm

Y? C) C] d
I*igtt1t 30.7 - lftti'ittm1 rttettsttgerts tio rltttt.

36.8 Conectando-se ao Bluetooth pela serial


Bluetooth e um meio de comunicacao tatttlwetn tuttito utilizado para se contunicar
com tim Arduino, que geralmente utiliza a conexao serial.
Para se conectar a serial por lluetootlt, utilize o UUID padrao da serial:
00001101-0000-1000-8000-00805F9B34FB

Basicamente, voc ira utilizar o metodo createRfcommSocketToServceRecord(UUID) da


classe B1uetoothDevce, mas vai passar esse UUID padrao da serial:
UUID uui.dSera1 = UUID.fromStrng("00001101-00G0-1000-8009-00805F9B34FB");
BIuetoothSocket socket = device.createRfcommSocketToServiceRecord(uuidSeria1);
socket.connect();
0utputStream out = socket.getOutputStream();
. . . _ ~ ' ' ~ ' rito na 919

. . ~ A . mas
Captulo 36 I Bluetooth

Caso esteia curioso de onde tirei esse UUID padrao da serial, isso esta esc
documentao (javadoc) da classe B1uetoothDevce.
Depois de realizar essa conexao, voce pode enviar os dados por Bluet0OIh,
por de baixo dos panos a conexo realizada como se fosse serial.
Essa abordagem est sendo muito utilizada na comunicao de d1spoSltlV0S
Android com Arduino que geralmente recebem os dados pela serial. VOC deve
apenas instalar uma placa receptora de Bluetooth no Arduino para rcalllf 3
conexo, mas isso com voc! Aqui estou dando apenas a dica caso algum d.13
voc precise fazer isso.

36.9 Links teis

Neste captulo estudamos a comunicao por Bluetooth e inclusive desenvolve


mos um simples chat.
Segue um link para continuar seus estudos.
API Guides - Bluetooth

http://developenandroid.com/guide/topics/connectivity/bluetooth.html
* cAPruLo 37
A Reconhecimento de voz
.\ 4
`

Reconhecimento de voz est sendo cada vez mais importante nos aplicativos para
dispositivos mveis, principalmente para relgios (Android Wear), culos (Google
Glass) e carros (Android Auto), pois todas essas plataformas esto utilizando cada
vez mais os comandos de voz.
Neste captulo vamos aprender a fazer o Android falar e escutar.

37.1 Introduo
Neste captulo, vamos aprender a fazer o Android falar e escutar e usufruir dos
recursos de voz que so cada vez mais importantes para novas plataformas e
dispositivos como relgios, culos, carros etc.
Text-To-Speech (TTS) a API responsvel por converter texto em voz e fazer o
Android falar, e Speech-To-Text (STT) responsvel por converter voz em texto
e fazer o Android escutar.
O reconhecimento de voz (STT) muito til para interagir com os dispositivos
quando no possvel toc-los, como o caso de enviar comandos para o celular
quando estamos dirigindo.
j a capacidade do Android de falar (TTS) muito til para auxiliar na navega
o do aplicativo quando no podemos prestar ateno na tela. Novamente, o
caso de quando estamos dirigindo, pois o aplicativo do GPS pode falar para nos
orientar sobre as informaes da rota.
Em especial, o TTS muito utilizado para criar aplicativos com acessibilidade
para decientes visuais. Eu recomendo assistir palestra Text-To-Speech 61 Eyes
-Free Project no YouTube, apresentada por dois engenheiros do Google durante
0 Google l/O 2009. O interessante da palestra que um dos palestrantes era

920
- - . _ , ' c)h) ia
Captulo 37 I Reconhecimento de voz

deciente visual e, dentre outras coisas, ele mostrou como essc tipo de ILCH 3
921

pode mudar a vida de milhares de pessoas pelo mundo.

37.2 Hello TTS -faa seu Android falar


Para fazer o Android falar, podemos utilizar 8 Cl2lSS TEXTOSDEGCII, qll est pI`S(:`I
no Android desde a verso 1.6. A engine de voz est disponvel para ingls, alemo,
espanhol, francs e italiano, mas tudo pode variar de acordo com o fabricante.
Infelizmente, no so todos os aparelhos com Android que suportam a voz cm
portugus ou, como dizemos tecnicamente, a localizao (locale) pt-BR.
AAPI do TTS simples e tudo gira ao redor da classe TetToSpeech, que recebe no
construtor uma implementao da interface TetToSpeech.0nIntLstener.
TextToSpeech tts = new TetToSpeech(this, this); // Aguarde o mtodo onInt()

Logo aps criar uma instncia da classe TetToSpeech, preciso aguardar a inicia
lizao da engine de voz. Quando o mtodo onInt(status) da interface de listener
for chamado, signica que a engine foi inicializada com sucesso, e assim podemos
usar o mtodo speak(teto,nodo,parans) informando o texto para falar.
tts.speak("Ol Tudo bem?", TextToSpeech.QUEUE_FLUSH, null);

Para configurar o locale utilizado pela engine do TTS, utilize o mtodo


setLocale(locale) conforme demonstrado a seguir:
// Portugus do Brasil
Locale locale = new Locale("pt","BR");
tts . setLanguage( locale);
// Ingls padro
tts . setLanguage( Locale . ENGLISH);

O TTS tambm pode sintetizar a voz e salvar em um arquivo de udio. Isso feito
com o mtodo syntheszeToFle(texto,parans,arquvo).

File le = SDCardUtls.getPublcFile("arquivo-voz.wav", Envronnent.DIRECTORY MUSIC)


HashHap<Strng, String> parans = new HashMap<Strng,Strng();
tts.synthesizeToFle("0l, tudo ben?", parans, le.getAbsolutePath());

Por ltimo, quando a engine do TTS no for mais utilizada, voc deve chamar
o mtodo shutdown() para liberar os recursos. Isso geralmente feito no mmd
onDestroy() da activity
// Libera os recursos da engine do TTS
tts _ shutdown( );
922 Google Android - 4' edio
Outro detallie importante que muitas vezes precisamos vericar se o pacote de
dados de voz est instalado no aplicativo. lsso pode ser feito disparando uma intcnt:
// Verica se o pacote de dados do TTS est instalado
Intent checklntent = new Intent();
checklntent.setAction(TetToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResu1t(checklntent, ACTION_CHECK_DATA_CODE);

O resultado entregue no mtodo onActivityResu1t( . . . ). Se o cdigo dc retorno for


ACTION_CHECK_DATA_CODE. isso indica que o pacote de dados de voz est instalado. Caso
contrrio, podemos instala-lo tambm com uma intent, a qual vai abrir uma apli
cao para o usurio fazer o download do pacote de voz para o idioma desejado.
// Solicita a instalao do pacote de dados de voz
Intent instattlntent = new Intent();
insta11Intent.setAction(TetToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(insta11Intent);

Depois desta introduo, vamos criar utn exemplo prtico. Crie a activity
Het1oTTSActivity com o seguinte arquivo de layout. Se preferir, abra o projeto Hellovoz
disponvel nos exemplos do livro.

ts /res/layout/actvity_he|Io_tts.xmI
<LinearLayout . . . android:orientation="vertical">
EditTet android:id="@+id/tsg"
android:1ayout_width="natch_parent" android:1ayout_height="wrap_content"
android:tet="01, bom dia." />
Button android:onC1ick="onC1ickFa1arTeto"
ll
android:1ayout_width="wrap_content" android:1ayout_height="wrap_content
android:text="Fa1ar o Texto" />
Button android:onCIick="onC1ickSa1var"
N

android:1ayout_width="wrap_content" android:1ayout_height="wrap_content
android:tet="Sa1var Arquivo" /
Button android:onCIick="onC1ickFa1arArquivo"
II

android:1ayout_width="wrap_content" android:layout_height="wrap_content
android:tet="Fa1ar do Arquivo" />

No layout temos um campo para digitar utn texto. O boto FaIaroTexto vai utilizar
a engine do TTS para falar. O boto Salvar Arquivo vai utilizar a engine da mesma
forma, mas em vez de falar, vai salvar a voz em utn arquivo de udio no SD card.
E o boto Falardo Arquivo vai tocar o arquivo de udio salvo com a classe MediaP1ayer.
O cdigo-fonte da activity pode ser visualizado a seguir:
923
Captulo 37 1 Reconhecimento de voz

j HeIIoTTSActivity.java

public class HelloTTSActivity extends BaseActivity implements TetToSpeech.0nInitListener {


private static nal String TAG = "livroandroid";
private static nal int ACTION_CHECK_DATA_CODE = 1;
private Textview tMsg;
private TextToSpeech tts;
@0verride
protected void onCreate(Bundle savedlnstancetate) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_tts);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
tMsg = (Textview) ndViewById(R.id.tMsg);
tts = new TextToSpeech(this, this);
}
@0verride
public boolean onCreate0ptionsMenu(Menu menu) {
getHenuInater().inate(R.nenu.menu_hello_tts, menu);
return true;
}

@0verride
public boolean on0ptionsItenSelected(Menulten item) {
int id = item.getItemId();
if (id == R.id.action_pt_br) {
Locale locale = new Locale("pt","BR");
tts.setLanguage(locale);
return true;
} else if(id == R.id.action_en_us) {
Locale locale = Locale.ENGLISH;
tts.setLanguage(locale);
return true;
}else if (id == R.id.action_check_data) {
// Verica se o pacote de dados do TTS est instalado
Intent checklntent = new Intent();
checklntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checklntent, ACTION_CHECK_DATA_CODE);
return true;
} else if(id == R.id.action_install_data) {
// Instala o pacote de dados
Intent installlntent = new Intent();
installlntent.setAction(TetToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installlntent);
uma amu: z 4- mm
mmmtmm
l
return super.onotlonsItehSelected(lten)3
}

vetrlde
publlt void nmttttnt mtos) (
Log\d(TAG,En9tne TTS tntctaltzada con xucessoz + Locale.qetDefault());
l Delxa ingles por padro
tts\setLangoage(Locale.getDefault());
}

oubllc void on(llckFalerTeto(Vtew vlew) {


Strlng s = tNsg.getIet().toStrln9();
tt.speak(s, TextTo$peech.QUEUI_FtU N, null);
Loq.d(TAG,Soeak: s);
}

oobllc void onCllckSalvar(Vlew vlew) [


Strlng s = tHs9.QetTet().toStrtng();
ij sintetiza a voz para arquivo
File le = Socardutlls.getPobllcFl\e(arqulvo-voz.wav, Envlronnent.DIRtcToRY_HUSIC);
HashNapStrtng, Strlng parans 2 new HashNapStrtng,Strlng>();
tts.synthestzToFt1e(s, perene, le.9etAbtolutePeth());
toast(Voz salva en arqulvo: + le);
}

publtc void onCltckFalarArqolvo(Vlew vlew) {


File le = SDCardUtlls.getPubllcFlle(arquivo-vozwav, Envtronnent.0IRE(TORY_HUSI();
tf(le.elsts()) [
try (
NedtaPlayer np = new NedlaPlayer();
no.setDataSource(le.getAbsolutePath());
n~oreoare();
p.start();
} catch (Exceptton e) {
Log.e(TG,Erro ao tocar: 4 e.9etNessa9e(), e)
}

@0verrtde
protected votd onActtvttyResult(tnt requesttode, tnt resulttode, Intent data) (
super.onActtvltyResult(requestCode, resolttodv data);
lf (requesttode == ACTION_CHECK_DATA_CODE) {
lf (resulttode == TetToSpeech\Engine.CHECK_VOI(E_DATA_PASS) {
toast(Pacote de dados de voz 0Kl)
l else {
925
Captulo 37 u Reconhecimento de voz

// Falta pacote, solicita instalao


Intent installlntent = new Intent();
installlntent.setAction(TetToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installlntent);
}

@0verride
protected void onDestroy() [
super.onDestroy();
// Libera os recursos da engine do TTS
tts.shutdown();
1

Crie o seguinte arquivo de menu com as opes da action bar.

/res/layout/menu_heIIo_tts.xmI
<nenu . _ .
<item android:id="@+id/action_pt_br"
android:title="@string/action_pt_br" app:showAsAction="never" />
<item android:id="@+id/action_en_us"
android:title="@string/action_en_us" app:showAsAction="never" />
<item android:id:"@+id/action_check_data"
android:title:"@string/action_check_data" app:showAsAction="never" />
<item android:id="@+id/action_install_data"
android:title="@string/action_install_data" app:showAsAction="never" />

O mtodo synthesizeToFile(texto,parans,arquivo) precisa das seguintes permisses


para funcionar; sendo assim, adicione-as no arquivo de manifesto.

AndroidManifest.xmI
<nanifest . . .
uses-pernission android:nane="android.pernission.HRITE_EXTERNAL_STORAGE" /
<uses-permission android:nane="android.permission.RECORD_AUDIO"/>
<application . . . />

A gura 311 mostra a aplicao executando no emulador. Veja que no menu temos
algumas aes, como trocar o idioma da engine de voz, assim como vericar e
instalar o pacote de dados de voz.
926 Google Android - 4' edio
. ='` if i

iOla,
_ Voz e __ US
tfrzv 5; ;
*QE 1 swijt . z

born dia m. bom dja n


i=At.Aa o rsxro rum o rm' ep
i Ch ,k
Dm
Install Data
SALVAR ARQUIVO 5__Vg QQ
FL^R U0 RQUWO FALAR DO ARQUWO

Figura 321 - Exemplo de TTS.

Executar a aplicao e testar os resultados com voc, pois no posso fazer o


livro falar. Mas, antes de encerrarmos o assunto, vamos explicar alguns detalhes
importantes. Ao criar a classe TextToSpeech, preciso informar uma implementao
da interface 0nIntLstener. Quando o mtodo onInt(status) for chamado, signica
que a engine de voz foi inicializada. O parmetro status pode ser TextToSpeech.SUCCESS
em caso de sucesso ou TetToSpeech.ERROR em caso de falha.

No caso de a inicializao ser bem-sucedida, podemos chamar o mtodo


speak(teto,modo,parans), passando a frase para a engine de voz falar.
Argumento Descrio
String texto Texto para falar.
int queueode Determina como o texto ser processado pela fila de execu
o da engine. Aceita as constantes TetToSpeech.QUEUE_FLUSH e
TextToSpeech . QUEUE_ADD.Aconstante TetToSpeech.QUEUE_FLUSH faz com
que todos os textos que ainda no foram pronunciados sejam
descartados da la e o novo texto seja falado imediatamente. j
a constante TetToSpeech.QUEUE_ADD faz com que 0 novo texto seja
adicionado fila, para aguardar o trmino da pronncia dos
outros textos que estiverem na fila.
Hashap params Parmetros opcionais a serem passados para a engine,
COITIO KEY_PARAH_STREAH, KEY_PARAH_UTTERANCE_ID, KEY_PARAH_VOLUHE,
KEY_PARAH_PAN. Para mais detalhes, veri car a documentao oficial.

O mtodo speak(teto,nodo,parans) foi descontinuado (deprecated) na API Level 21,


pois foi substitudo pelo mtodo speak(text, queuehode, parans, utteranceld). Utiliza
mos o mtodo antigo no cdigo por questes de compatibilidade.
927
Capitulo 37 I Reconhecimento de voz

Argumento Descrio
texto Idem ao anterior.
int queueMode Idem ao anterior.
Hashiiap params Idem ao anterior, mas agora os parmetros so passados Por um
Bundle.
String utteranceld Identificador nico desta chamada.
Outro mtodo que tambm utilizamos no cdigo o synthesizeToFiie(texto,parans,
arquivo), que converte o texto para voz e salva o udio em um arquivo.
Argumento Descrio #__W ~____M____~___________
ii' A *+,;iia,>;.;arii.a"ist q
Hashap parans Parmetros opcionais como no mtodo speak( . . . ).
String 1eNarfie Caminho do arquivo para salvar o udio.
Esse mtodo tambm foi descontinuado (deprecated) na API Level 21, sendo
substitudo pelo mtodo synthesizeToFi1e(teto,parans,arquivo).
Argumento Descrio _ __
iiii i_"i`r<15}zz{QzQM"" M P'
Hashap pararfis Parmetros opcionais como no mtodo speak( . . . ).
File le Arquivo para salvar o udio.
String utteranceld Identicador nico desta chamada.
Neste tpico aprendemos a fazer o Android falar. O assunto ainda novo e pouco
explorado. Com certeza, se voc for desenvolver alguma aplicao real de voz, ser
necessrio se aprofundar nos estudos, pois o que zemos aqui foi um simples
hello world para te mostrar o bsico da API.

Nota: internamente, o TTS um Service compartilhado entre todas as aplicaes,


sendo assim recomendado liberar os recursos quando voc no os utilizar
mais. Faa isso chamando o mtodo shutdown().

37.3 Vericando o idioma e falando em portugus


Por padro, a engine de voz no suporta o portugus, embora ao comercializarem
o aparelho no Brasil alguns fabricantes adicionem o suporte ao portugus. Mas,
como nada garantido, vamos explicar o passo a passo bsico para fazer seu
Android falar em portugus.
92 Google Ana-4- zuo
leoricamente, se quisermos falar em portugus, basta alterar o locale da engine
para pt-BR:

TetToSpeech tts = . . .;
tts.setLanguage(new Loca1e("pt","BR"));

Infelizmente, no assim to simples, pois o portugus no suportado por


padro e tudo vai depender da customizao que foi feita em seu aparelho.
Portanto, para ter certeza de que um idioma suportado, chame o mtodo
tsLanguageAvaIab1e(Iocale), que retorna uma das seguintes constantes:

int LANG_AVAILABLE = 0

Indica que o idioma suportado, mas no exatamente daquele pas. Por


exemplo, pode ser que o portugus de Portugal seja suportado, mas no o
do Brasil. Por isso, lembre-se de que um locale geralmente espeeicado
com o cdigo do idioma e do pas, como new Loca1e("pt","BR").

int LANG_COUNTRY_AVAILABLE = 1

Indica que o locale suportado exatamente da forma que foi informado.


Se essa constante for retornada, tudo est ok.

int LANG_COUNTRY_VAR_AVAILABLE = 2

Idem a constante LANG_COUNTRY_AVAILABLE. A diferena que esta constante ta mbm


leva em considerao as variantes da classe Locale, o que pode ser especicado
no terceiro parmetro do construtor, como new Loca1e("pt","BR", variantes).

int LANG_MISSING_DATA = -1

Indica que o idioma e suportado, mas falta fazer o download dos pacotes
de voz. Caso seja necessrio, podemos disparar uma intent para fazer esse
download. Mas, geralmente, esse retorno acontece somente para os idiomas
suprtados de forma nativa pelo Android.

int LANG_NOT_SUPPORTED = -2

Infelizmente, o idioma especicado no suportado.


Caso seu celular no tenha a engine de voz para portugus, possvel instalar
alguma disponvel no Google Play A mais famosa a SVOX (Iasslt, que tem um sin
tetizador para portugus. Depois de instalar o aplicativo, voc vai precisar baixar
os arquivos de voz e congurar nas preferncias do TTS a voz para portugus.
mas voc pode encntrar esse passo a passo na internet.
929
Captulo 37 I Reconhecimento de voz

37.4 Reconhecimento de voz por intent


os faz-lo es
At o momento, estamos fazendo o Android falar, mas agora vam
CUlaI`. DCSEI VCZ, VQIHOS COl'lVl'tI' 8 VOZ ITl texto, O qLl
conhecido como STT
(Speech-To-Text).
Para a alegria dos desenvolvedores, o reconhecimento de voz pode ser feito por
uma simples intent:
// Intent para fazer o reconhecimento de voz
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putEtra(Recognzerlntent.EXTRA_LANGUAGE_MODEL,
Recognzerlntent.LANGUAGE_MODEL_FREE_FORM);
intent.putEtra(Recognzerlntent.EXTRA_CALLING_PACKAGE, getPackageName());
intent.putExtra(Recognzerlntent.EXTRA_PROMPT, "Fale a1go");
intent.putEtra(RecognzerIntent.EXTRA_LANGUAGE, "pt-BR");
intent.putEtra(Recognizerlntent.EXTRA_MAX_RESULTS, 19);
startActivityForResu1t(intent, O);

O parmetro EXTRA_PROMPT recebe o texto que ser mostrado no alerta padro do


Android, o parmetro EXTRA_LANGUAGE recebe o idioma para auxiliar no reconhe
cimento de voz e o parmetro EXTRA_MAX_RESULTS recebe a quantidade mxima de
resultados que devem ser entregues. O resultado da intent entregue no mtodo
onActivtyResu1t( . . . ), no qual podemos obter uma lista das possveis palavras que
foram encontradas:
@0verrde
protected void onActvtyResu1t(nt requestCode, int resultCode, Intent data) {
if (resu1tCode == RESULT_0K) {
// Recupera as possveis palavras que foram pronunciadas
ArrayLstString> words = data.getStringArrayListExtra(kecognzerlntent.EXTRA_RESULTS);
1stView.setAdapter(new ArrayAdapter(this,
androd.R.Iayout.smp1e_1st_tem_1, words));
}

Para demonstrar como implementar o reconhecimento de voz, vamos criar uma


tela com um boto para disparar essa intent e um Lstvew para exibir a lista com
os resultados. O arquivo de layout pode ser visualizado a seguir:
930
Google Android - 4 edio

/res/layout/activity_heIIo_recognizer_intent.mI
<?xnl version=1.6" encoding="utf-8"?
<LinearLayout ... android:orientation="vertical" >
<Button android:id="@+id/btSpeak"
android:layout_width="natch_parent" android:layout_height="wrap_content"
android:tet="Clique aqui e fale algo" /
<ListView android:id="@+id/list" android:layout_weight="1"
android:layout_width="natch_parent" android:layout_height="6dp" /
</LinearLayout

Ao clicar no boto, ser disparada uma intent para chamar o aplicativo nativo de
reconhecimento de voz. O resultado ser mostrado no Listview. Note que, como
o reconhecimento de voz no suportado em todos os aparelhos, o cdigo est
vericando se ele suportado antes de disparar a intent.

HeIIoRecognzerIntentActivity.java

public class HelloRecognizerIntentActivity extends AppConpatActivity {


protected Listview listview;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_recognizer_intent);
View btSpeak = ndViewById(R.id.btSoeak);
listview = (Listview) ndViewById(R.id.list);
// Verica se o Android suporta a intent de reconhecimento de voz
Packageanager pm = getPackageHanager();
ListResolveInfo activities = pm.queryIntentActivities(new
Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), H);
if (activities.size() != 0) {
btSpeak.set0nClickListener(onClickSpeak());
btSpeak.setEnabled(true);
} else {
Toast.nakeTet(this, "Reconhecimento de voz indisponvel", Toast.LENGTH_SHORT).show();
}

protected View.0nClickListener onClickSpeak() {


return new View.0nClickListener() {
@0verride
public void onClick(View v) {
Intent intent = getRecognizerIntent();
931
Captulo 37 n Reconhecimento de voz

startActivityForResuit(intent, 0);
}

};
}

/I Intent que dispara o reconhecimento de voz


protected Intent getRecognizerIntent() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putEtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
Recognizerlntent.LANGUAGE_MODEL_FREE_FORM);
intent.putEtra(Recognizerlntent.EXTRA_CALLING_PACKAGE, getPackageName());
intent.putEtra(Recognizerlntent.EXTRA_PROMPT, "Fale aigo");
intent.putExtra(Recognizerlntent.EXTRA_LANGUAGE, "pt-BR");
intent.putEtra(RecognizerIntent.EXTRA_MAX_RESULTS, 10);
return intent;
}

@0verride
protected void onActivityResult(int requestCode, int resultode, Intent data) {
if (resu1tCode == RESULT_0K) {
// Recupera as possiveis palavras que foram pronunciadas
ArrayList<String words = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
1istView.setAdapter(new ArrayAdapter(this,
android.R.iayout.simpie_iist_item_1, words));
}

Nota: o reconhecimento de voz vai utilizar os servidores do Google para fazer


a converso de voz para texto, portanto certique-se de que seu Android est
conectado internet.

Ao clicar no boto, a intent disparada para iniciar o reconhecimento de voz,


conforme demonstra a figura 312. Neste caso eu disse "0I,pessoaI, e na lista pode
-se ver que foi encontrado justamente esse texto. Agora com voc, fale algumas
coisas e brinque com o seu Android.
932 Google Android - 4 edio

(LIQU AQUl FLF LGO

Ulet pe mui

Figura 322 - Reconlzecimcrzm de voz.

37.5 Reconhecimento de voz por um Iistener


Sempre que possvel, implementar uma funcionalidade com intents pode sim
plificar muito o aplicativo e economizar vrias linhas de cdigo. Mas utilizar
uma intent implica mostrar a janela para o usurio falar, o que dependendo dos
requisitos do projeto no e algo vivel. Sendo assim, tambm e possivel fazer o
reconhecimento de voz pela API em segundo plano, sem utilizar intents.
A classe SpeechRecognzer utilizada para fazer o reconhecimento de voz pela API.
Basta congur-la com um listener do tipo Recogntonstener e chamar o metodo
startLsteni.ng(ntent). Feito isso, a engine do reconhecimento de voz ca executando
em segundo plano e lendo qualquer coisa que voc falar.
SpeechRecognzer stt = SpeechRecognzer.createSpeechRecognzer(this);
stt.setRecogntonLstener(Iistener);
Intent intent = . . .;
stt.startLstenng(ntent);
C) nietocha startListenng(ntent) recxde cornvariru:n1 a niesnia intent (plc
utilizamos no tpico anterior. Como a interface Recogntionstener contm varios
mtodos, vamos criar a classe BaseRecogntonLstener que implementa essa interface
com metodos vazios para facilitar o cdigo nal.
933
Captulo 37 I Reconhecimento de voz

BaseRecognitionlistenenjava

public class BaseRecognitionListener implements RecognitionListener {


// Indica que o usurio comeou a falar
public void onBeginning0fSpeech() [ }
// Buffer que vai sendo montado medida que o usurio vai falando
// No garantido que este mtodo seja chamado
public void onBufferReceived(byte[] buffer) { }
// Indica que o usurio terminou de falar
public void onEnd0fSpeech() { }
// Indica que ocorreu um erro no reconhecimento de voz
public void onError(int error) { }
// No momento este mtodo no utilizado
public void onEvent(int eventType, Bundle params) { }
// Chamado quando existe alguma leitura de voz parcial
// No garantido que este mtodo seja chamado
public void onPartialResults(Bundle partialResults) { }
// Chamado quando a aplicao est pronta para receber o comando de voz
public void onReadyForSpeech(Bundle params) { }
// Chamado para entregar os resultados para a aplicao
public void onResults(Bundle results) { }
// Indica que o nivel de som do udio mudou
public void onRmsChanged(oat rmsdB) {}
}

O mtodo mais importante dessa interface o onResults(Bundle), o qual chamado


quando alguma palavra pronunciada reconhecida. Neste exemplo, esse mtodo
o nico que vamos sobrescrever. Agora que temos essa classe que simplica a
interface RecognitionListener, podemos utiliz-la com a classe SpeechRecognizer:
SpeechRecognizer stt = SpeechRecognizer.createSpeechRecognizer(this);
stt.setRecognitionListener(new BaseRecognitionListener(){
public void onResults(Bundle results) {
// Recupera as possiveis palavras que foram pronunciadas
ArrayList words = results.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
listView.setAdapter(new ArrayAdapter(getContet(),
android.R.layout.simple_list_item_1, words));

P8
}

});

Como podemos vericar, o mtodo onResults(Bundle) recebe o Bundle que contm a


lista de palavras que foram pronunciadas. O cdigo-fonte completo desse exemplo
ode ser visualizado a se uir:
934
Google Android - 4 edio

HelIoSpeechRecognizerAttivity.java

public class HelloSTT_Listener extends AppCompatActivity {


I/ Reconhecedor de voz
private SpeechRecognizer stt;
private Listview listview;
@0verride
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_hello_speech_recognizer);
listview = (Listview) ndViewById(R.id.list);
// Cria o SpeechRecognizer e congura o listener
stt = SpeechRecognizer.createSpeechRecognizer(this);
stt.setRecognitionListener(new BaseRecognitionListener(){
public void onResults(Bundle results) {
// Recupera as possiveis palavras que foram pronunciadas
ArrayList words = results.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
listView.setAdapter(new ArrayAdapter(getBaseContext(),
android.R.layout.simple_list_item_1, words));
}

});
// Inicia o listener do reconhecimento de voz
Intent intent = getRecognizerIntent();
stt.startListening(intent);
}

protected Intent getRecognizerIntent() {


Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
// Mesma intent, simpliquei o cdigo aqui...
intent.putEtra(Recognizerlntent.EXTRA_LANGUAGE, "pt-BR");
return intent;
}

@0verride
protected void onDestroy() {
super.onDestroy();
// Libera os recursos e naliza o STT
stt.stopListening();
stt.destroy();
}

No layout desta activity adicione apenas um Listviewz


935
Captulo 37 I Reconhecimento de voz

/res/layout/activity_heIIo_speech_recognizer.mI
?m1 verson="1.0" encodng="utf-8"?>
<LnearLayout ... android:orientation="vertica1"
<LstView androd:id="@+d/list"
android:1ayout_width="natch_parent" androd:1ayout_hetg
' ht="match_parent " />

Apenas para constar, para esse cdigo funcionar, necessri o adicionar a per
misso androd.permsson.RECORD_AUDIO no arquivo de manifesto, mas j zemOS SS0
anteriormente ao utilizar o syntheszeToFi1e(teto,params,arquvo), que C0nVfI 0
texto para voz e salva o udio em arquivo.
A diferena deste exemplo para o anterior que utilizava intents era que no pri
meiro zemos um boto que chamava a intent, ou seja, o aplicativo nativo de
reconhecimento de voz. Desta vez, a activity inicia a engine de reconhecimento de
voz em segundo plano e ca monitorando tudo o que falado. Portanto, se voc
falar algo, os resultados sero entregues no mtodo onResu1ts(bund1e) e mostrados
no Lstview. Faa o teste.

37.6 Links teis


Neste captulo estudamos os recursos de voz disponveis na plataforma do
Android. Seguem alguns links para continuar seus estudos.
Android Developers Blog - Introduction to TTS

http://android-developers.blogspot. com. br/2009/09/introductiorrto-text-to-speech


-in.html

YouTube - Palestra sobre TTS no Google I/0 2009

https://www. youtube. com/ watch ?v=xS -j u 61 1/O Qw


` CAPTULO 38
Gradle
`\ il
\
l

O Cradle (gradle.org) um moderno sistema de builds que segundo a prpria


documentao oficial denido com a seguinte frase: Cradle combina o poder
e a flexibilidade do Ant com o gerenciamento de dependncia e convenes do
Maven, em uma maneira mais ecaz
Neste captulo vamos estudar alguns dos recursos mais interessantes do Cradle
aplicados ao desenvolvimento Android, como gerenciamento de dependncias e
builds customizados.

38.1 Introduo
Ant (ant.apache.org) e Maven (maven.apache.org) so dois sistemas de builds bastante
populares, e o Cradle (gradleorg) a evoluo que combina o melhor de cada um.
Tanto o Ant quanto o Maven ou Cradle so assuntos que podem ser abordados
em um livro inteiro, mas neste captulo vamos estudar o bsico sobre o Cradle e
principalmente sobre como ele pode ajudar no desenvolvimento para Android.
A esta altura do livro, vocj deve ter entendido que um dos principais benefcios
do Cradle gerenciar as dependncias, pois por diversas vezes durante o livro
atualizamos o arquivo app/build.gradle para declarar as bibliotecas necessrias.
Para relembrar, as dependncias so conguradas dentro da estrutura dependences,
conforme demonstrado a seguir:

app/buiId.gradIe
apply plugin: 'com.androd.applcaton'

dependencies {
comple leTree(dr: 'libs', include: ['*.jar'])

936
937
Captulo 38 n Gradle

compile 'com.androd.supportzappcompat-v7:22.1.G'
conpile 'br.con.Ivroandrod:androd-uti1s:1.6.6'
}

38.2 Gerenciando dependncias


No Cradle podemos declarar mdulos do projeto como dependncias ou biblio
tecas, sendo que as bibliotecas podem estar em um repositrio local ou remoto.
Por padro, todas as bibliotecas adicionadas no build.gradle buscam os artefatos
no repositrio do Maven jCenter, localizado no seguinte endereo:
http:/centex bi ntray. com/

Mesmo que voc no declare o repositrio ]Center no arquivo buildgradle, ele


usado como repositrio padro.
repostories {
jcenter()
}

O ]Center um superconjunto, ou seja, ele engloba o famoso Maven Central


(http://repo1.maven.org/mai/en2), outro gigantesco repositrio de cdigo.
repostores {
mavenCentra1()
}

Como eu disse antes, o repositrio do ]Center padro e ele ser utilizado para
buscar as dependncias mesmo que no esteja declarado no buildgradle.
Mas com certeza o que voc ou sua empresa vai precisar no dia a dia criar bi
bliotecas internas com classes utilitrias.Ao criar a biblioteca, voc ter de decidir
se ela ser livre e de cdigo aberto, como a biblioteca androd-utls que z para
o livro, ou se ela ser privada, ou seja, somente voc e o pessoal da sua empresa
podero utiliz-la.
Bibliotecas livres podem ser publicadas no repositrio global do Maven Central
ou ]Center, e foi exatamente o que z com a biblioteca androd-utls.
No caso da minha biblioteca, eu a publiquei no Maven Central.
http://repol.maven.org/maven2/br/com/livroandroid/android-utils/

E como o]Center engloba o Maven Central, tambm podemos encontrar a lib aqui'
http://jcenter bintray.com/br/com/livroandroid/android-utils/
938 Google Android - 4' edio
Mas o que lazer caso a biblioteca precise ser privada da sua empresa? Nesse caso,
elas podem ser publicadas em um repositrio local do seu computador ou em
algum servidor remoto interno da empresa.
Apenas para car claro o significado de cada tipo de repositrio, veja este exemplo
de arquivo buildgradle. Nele est declarado o repositrio global do ]Center (j
incluso por padro), um repositrio local do computador e um repositrio remoto.
No caso do repositrio remoto preciso instalar o servidor web Sonatype Nexus,
que vamos estudar mais para frente.

app/buiId.gradle
apply plugin: 'com.android.application'

dependencies { . . . }
repositories {
jcenter()
maven { url "lez///c:/gradle/rep"}
naven { url "httpz//localhost:8081/neus/content/repositories/releases/"}
i

38.3 Trabalhando com mdulos


Na documentao do Android muito enfatizada a criao de mdulos para
conter 0 cdigo comum do projeto, ou seja, as bibliotecas ou classes que voc
pode reutilizar entre vrios mdulos.
Para praticar, vamos fazer um exerccio. Crie um projeto chamado HelIoModulo com
uma ManActivity padro. Logo depois, utilize o wizard File > New Module e selecione a
opo Android Library. Na prxima pgina do wizard digite Mylibrary para o nome do
mdulo, prossiga com o wizard e clique em Finish.
Feito isso, voc ter 0 mdulo app, que padro do Android Studio, e o mdulo
MyLibrary, que foi criado para ter classes utilitrias, conforme a figura 38.1. Veja
que no mdulo MyLibrary criei uma classe chamada ClasseUtilitaria, a qual pode
ser compartilhada com o mdulo principal, ou seja, o mdulo app.
939
Captulo 38 z Gradle

rwma l
499
ip'
.zw
Ui

myib t .n y
-HJ |lz
Lt. ~

,Lt_em
,_i_l ,r=:tnzIC'V=.
.. .,, ,s
\ ll"I=7'\'.
- iv fz, '1~
l^.,,fm;i .~.Jl| .mz

ul-.fl
' IM *i,;;' 151 V'
bu kl .HM r

* lu 'n~l'`
5~ :af .'1:
l` \,.2.j`.' 't lt
JY JH? Ui :,*ri."

tlc .,; qt.t"1|^


_ ,.'i'\",;?Y|$C

Figura 38.1 - Mdulo criado.

Mas, para que a dependncia entre os mdulos funcione, precisamos configurar o


arquivo app/buildgradle do mdulo app, adicionando a dependncia para o mdulo
MyLibrary, pois isso no feito automaticamente.

app/buiId.gradIe
apply plugin: 'con. android . application'
dependencies {
Compile leTree(include: ['*.jar'], dir: 'libs')
compile 'con.android.support:appcompat-v7:22.1.G'
conpile project( ' :nylibrary ')
}

Dica: mdulos so usados para conter o cdigo referente ao Android Wear,


TV, Glass etc., como tambm podem conter o cdigo reutilizvel para todg
os mdulos do projeto. Outro beneficio dos mdulos que cada um pode ser
compilado e executado separadamente.

assim que dependncias para mdulos so adicionadas. Feito isso, sempre qug
compilar o mdulo app, todas as classes do mdulo MyLibrary sero adwirmadas no
build, ou seja, podemos utilizar as classes do mdulo MyLibrary no mdulo app. Para
comprovar a teoria, altere o cdigo-fonte da HainActivity do mdulo app para utili
zar a classe Classetilitaria do mdulo MyLibrary, conforme demonstrado a seguir
94 Google Android - 4 edio
MainAttvity.java
inport br.con.livroandroid.nylibrary.ClasseUtilitaria;
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s = Classetilitaria.hello();
Log.d("livro"," " + s);
}

Por ltimo, vale ressaltar que cada mdulo tem seu prprio arquivo build.gradle;
mas tenha ateno, pois o arquivo mylibrary/build.gradle diferente do arquivo
app/build.gradle. A diferena est na primeira linha do arquivo, pois declarado o
plugin com.android.library para indicar que este mdulo uma biblioteca.

mylibrary/buiId.gradIe
apply plugin: 'con.android.library'
// O restante igual

Isso possvel graas ao conceito de plugins do Cradle. Note que o mdulo app
sempre declarado com o plugin com.android.application, indicando que esse mdulo
executvel, ou seja, possvel clicar no boto Run do Android Studio.

app/buId.gradIe
apply plugin: 'con.android.application'
// O restante igual
Esse conceito importante, pois somente bibliotecas podem ser compiladas e
disponibilizadas para outros projetos. Neste caso estamos compilando um m
dulo que est na mesma estrutura de diretrios do projeto. Masem casos mais
avanados a biblioteca pode ser compilada de forma independente e instalada
em algum repositrio, seja uma pasta local ou um servidor remoto.

Dica: mdulos podem ser compilados e executados separadamente. Um exemplo


disso so os mdulos para Android Wear, Tablet e Glass que podemos criar no
projeto. No boto Run do Android Studio podemos escolher qual o mdulo que
deve executar, desde que ele no seja uma biblioteca.
941
Captulo 38 I Gradle

. . _ . _ _ g , - f -' (3
38.4 Trabalhando com bibliotecas
Caso voce faa muitos projetos mobile, com certeza sentira a necessidade d
criar uma biblioteca para reutilizar suas classes em todos os seus aplicam/OS. Um
exemplo de biblioteca a androd-utils que estudamos no livro; veja como 618
facilitou os nossos estudos.
Caso tenha interesse em aprender a criar bibliotecas, como a androd-utls que fiz
para o livro, sugiro ler esta srie de trs artigos que publiquei no meu site.
Criando Iibs no Gradle com Android - Parte 1

http://ricardolechetu.com.br/?p=371
Criando libs no Gradle com Android - Parte 2

http://ricardolecheta.com. br/ ?p=423


(riando Iibs no Gradle com Android - Parte 3

http://ricardolecheta.com.br/?p=450
A primeira parte do artigo mostra como criar um projeto biblioteca chamado MyLib
e como fazer o build em uma pasta local do computador. Uma vez que o build
feito, o artigo mostra como declarar a dependncia deste repositrio local para
compilar o projeto e utilizar a biblioteca. Farei um resumo da primeira parte do
artigo no prximo tpico, mas as partes 2 e 3 estaro somente no site.
A segunda parte do artigo mostra como instalar e congurar o servidor web
Sonatype Nexus, que um servidor de dependncias corno o Maven. Veremos
como fazer o build e enviar (upload) da biblioteca para o servidor.
A terceira parte deste artigo ensina como enviar uma biblioteca para o Maven
Central, caso voc queira distribuir sua biblioteca de forma global. Foi exatamente
o que z com a biblioteca androd-utls que usamos durante os estudos deste livro.

38.5 Criando uma biblioteca

Chega de teoria, vamos exercitar e aprender a criar uma biblioteca e publica-la


em um repositrio local, ou seja, uma pasta no seu computador.
Para criar a biblioteca, crie um novo projeto no Android Studio chainado Myl.ib.
No wizard nem precisa criar uma activity pois no vamos us-la. Na sequncia,
crie a classe ToastUti1. O objetivo dessa classe utilitria facilitar o cdigo para
mostrar um toast na tela.
942
Google Android - 4 edio

ToastUtiI.java
public class ToastUtil {
public static void toast(Contet context, String msg) {
Toast.nakeTet(contet, nsg, Toast.LENGTH_SHORT).show()
}

Para transformar este projeto em biblioteca, precisamos aplicar o plugin


con.android.library no arquivo app/buildgradle. Tambm vamos aproveitar e aplicar
o plugin do naven, pois necessrio para fazer o build da biblioteca e instal-la no
repositrio, seja este local ou remoto.

app/bui|d.grad|e
apply plugin: 'com.android.library'
apply plugin: 'naven'
android {
conpileSdkVersion 22
buildToolsVersion "22.0.1"
defaulttong {
// ATENO! FOI REMOVIDO applicationld
ninSdkVersion 9
targetSdkVersion 22
versionCode 1
versionNane "1.0"
}

buildTypes { // Tudo igual aqui... }


}

uploadhrchives {
repositories {
mavenDeployer {
repository(url: "le:///hone/ricardo/gradle/rep")
pon.groupId = "br.con.livroandroid"
pon.artifactId = "nylib"
pon.version = "6.0.1"
}

}
task install(depends0n: uploadArchives)
dependencias {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'con.android.support:appcompat-v7:22.1.0'
}
_ . _ - ~ item
943
Captulo 38 1 Gradle

Ateno: uma biblioteca nao pode declarar no arquivo bmld.gradle O


appllcationld, o qual representa o pacote que identifica a aplicaaO.

No arquivo buildgradle da biblioteca foi criada a task uploadArchves, qU dene O


repositrio no qual a lib deve ser instalada. A ltima linha dessa congurao l'
dica que a tarefa install depende da tarefa uploadArchves. Portanto, se VOC executar
a task install, tambm ser feito o upload da lib para o repositrio, que nSt C2150
uma pasta local do computador. Dentro da tag maveneployer denido o local
do repositrio para instalar a biblioteca, assim como o groupld, artifactld e verSa0.
mavenDeployer {
repostory(url: "lez///home/ricardo/gradle/rep")
pom.groupId = "br.com.lvroandroid"
pom.artifactId = "mylb"
pom.verslon = "0.0.1"
}

Para utilizar a biblioteca, preciso seguir o padro groupIdzartifactldzverslon, ou


seja, no projeto cliente a biblioteca nylb deve ser declarada assim:
complle 'br.cormllvroandroid:mylb:G.0.1'

Mas espere! Antes vamos aprender a fazer o build da biblioteca. Abra a janela
Gradle que fica no lado direito do Android Studio, ou se preferir utilize o menu
\ew>TooIWindows> Gradle. Essa janela, conforme a gura 38.2, mostra todas as tasks
(tarefas) que voc pode executar. Procure pela task install e d um duplo clique.
Szade rain ' ' * ,..
f
JI "
lr L 5; C *8
"z .=

QI

3
`U

11

., .1Q
;.!t;b.s JJ'
~.,..~ generatee-.easemzetr
geneatF`eleaseEuld~.enfia fi
ai genertePeeaseRes'alues Q,
z. gerifateRelee:eRe;ufces fiO

. , `
' ' I l srAgenev.ateRelea.ecorces
' 7 ' ` ,.:= ;ff,:.z" U
mlalmeuuqndreidlest

Figura 38.2 - Executando task install.


944 ewgie Anam-4- edio
Ao executar a task install, ser feita a compilao do projeto, e se tudo correr bem
a biblioteca ser publicada no repositrio, que neste caso uma pasta local. A
gura 383 mostra o resultado dos logs na janela Run do Android Studio.

M r WY~*P.f'?'*'1 .

zz_
". ..... . ,_ z
:app:k:u.ndleRe3_:se LT?-T3~-ZATE
~ ~ -^ ~~-
1p::upl:e.z::;':es"c`
. -' . l-r'rar*
-C fi*I3C8l-1,
z*~'
;='.- =.-....-._z .-....,,-Y-~'-r
-~*~f~:1'~~1
at _1.ze: z e :.:ar:i.qr::-e :ep
,. ' z
~.....
',* ....
.' .-Z3IEI`IL.J
' `A__~ z-_..
*T~f

`, .pp2l3&-..
*"~ , . ,
i-...-r3E.a Burt

EUIL SUIESSE uL

Figura 38.3 - Mensagens no console.

Para comprovar que a biblioteca foi realmente instalada no repositrio local, a


figura 38.4 mostra a pasta le:///home/ricardo/gradle/rep de meu computador. Na

9. \.

/c;
|"`
nf' f
,
turalmente voc pode alterar o caminho do repositrio conforme preferir.

4i ` i
v R:\nom=\ri(ardo\gr'adle\rep\br\com\|iwoandi o1d\myIib\3.(> 1 - _
~=~ - J J :ep > br torr hr ii U v myhli > 00.1 X
\J
_, ,z> _
h 'l

. rf R J

Qd. Rzzj

Figura 38.4 - Pasta do repositrio com o build.

Nota: para testar o build, altere a pasta do repositrio para alguma que exista
em seu computador.

Pronto! Agora que j temos a biblioteca vamos criar um projeto para test-la. Crie
um projeto chamado HeI|oLib e adicione a dependncia da biblioteca Mylib. Como a
biblioteca est em um repositrio customizado, ou seja, no est no ]Center ou
Maven Central, preciso configurar o repositrio dentro da estrutura repostories.

app/bui|d.gradIe
apply plugin: 'con.androd.applcaton'
androd { . . . }
repositores {
naven { url "lez///hone/ricardo/gradle/rep" }
945
(apltulo 38 u Gradle

dependencies {
compile leTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.0'
conpile 'br.con.livroandroid:nylib:6.8.1'
}

Uma vez que a biblioteca est declarada. podemos utilizar a classe ToastUtil no
[r)j:tt:

MainActivity.java
inport br.con.livroandroid.nylib.ToastUtil;
public class HainActivity extends AppCompatActivity {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain);
ToastUtil.toast(this,"Teste Lib!");
l
}

Do ponto de vista do projeto cliente, isto; basta declarar a dependncia e os


repositrios em que esto as bibliotecas. O resto, 0 Cradle faz sozinho.
Apenas uma ltima dica que pode ser interessante. Digamos que voc cometa
um erro e precise lanar um novo release da sua biblioteca. Nesse caso se voc
incrementar a verso de 0.0.1 para 0.0.2 e fazer o build, basta tambm incrementar
a verso para 0.0.2 no projeto cliente e tudo estar resolvido. Porm, muitas vezes
queremos ajustar a verso atual. Ao fazer o build da mesma verso, neste caso
da 0.0.1, ela ser instalada no repositrio normalmente. Mas 0 problema que
no projeto cliente do Android Studio a lib no ser atualizada, pois o Cradle tez
cache da biblioteca, sendo que ele j baixou esta verso no projeto.
O cache do Cradle ca na pasta /Projeto/app/build/intcrmediates/exploded-aan con
forme mostra a figura 38.5. Para limpar 0 cache, apague da biblioteca essa pasta;
feito isso, no prximo build o Cradle vai sincronizar os arquivos e baixar nova
mente as dependncias.
94 Google Anami-4 zaro
l GY Pw 7. Qi
Herrorm f
l C idea
..PP
build
qt-ritfird
rm-rmedatce
wxploded-ar
br tomlwrzrwdrorrl
mylnb
H 0.0.1

Figura 38.5 - Pasta de cache do Gradle.

38.6 (ongurando um servidor Maven Sonatype Nexus


O servidor Sonatype Nexus pode ser instalado em qualquer servidor e uma aplica
o web. Ele foi criado pela Sonatype, empresa que tambm criou o Maven Central.
Se voc instalou o servidor Sonatype Nexus em algum computador, basta decla
rar a URL do repositrio conforme demonstrado a seguir. O resto tudo igual.

[ app/buiId.gradIe
apply plugin: 'com.androd.app1caton'

repostories {
maven { url "httpz//servidor_da_sua_empresa:8081/neus/content/repostores/re1eases/"}
}

Caso tenha interesse em aprender a congurar esse servidor, leia a parte 2 da


srie de artigos sobre como criar libs no Cradle (veja os links no ltimo tpico).

38.7 Publicando no Maven Central


Publicar uma biblioteca no Maven Central outra tarefa que muitos desenvol
vedores Android precisam fazer. Contudo, isso e um tanto quanto complicado c
exige diversos passos, principalmente da primeira vez.
Esse assunto est fora do cscpo do livro, mas se tiver interesse recomendo ler a
parte 3 do artigo sobre como criar libs no Cradle (veja os linls no ltimo tpico).
947
Captulo 38 1 Gradle

38.8 Flavors
render a
Depois de estudar o conceito de dependncias e bibliotecas, vamos ap
utilizar flavors, recurso que permite criar diferentes builds do mesmo apl1C1UV0
Existem vrias razes para fazer dois ou mais builds do mesmo apliCaUV0, ff Para
exemplicar vou citar alguns exemplos:
Criar uma verso free e uma paga do mesmo aplicativo.
Criar uma verso de homologao e outra de produo do mesmo aplica
tivo. A verso de homologao utiliza os web services de homologaao e
vice-versa.

Criar verses customizadas do mesmo aplicativo, trocando layouts e cores, assim


como pequenas regras de negcios. Porm, no geral, o mesmo aplicativo.
Enm, no importa qual seja o seu objetivo, o Gradle pode ajud-lo, e justamente
aqui que entram os avors (sabores).
Para demonstrar o que um flavor, vamos criar duas verses do aplicativo dos
carros. A verso ocial ter as cores em azul, conforme j fizemos. Mas faremos
uma verso plus que ter as cores em vermelho. Para isso, adicione os flavors azul
e vermelho no arquivo app/build.gradle, conforme demonstrado a seguir. Se preferir,
abra o projeto dos carros de exemplo deste captulo.

app/buId.gradIe
apply plugin: 'com.androd.app1icaton'
android {

defau1tCong { . . }
productF1avors {
azul {
applicatonld "br.com.1ivroandrod.carros.azu1"
}

vermelho {
applcatonld "br.com.1ivroandrod.carros.vermelho"
}

repositores { . . .}
dependences {. . . }

Neste caso foram criados dois builds diferentes, ou seja, dois aplicativos diferente s
cujos pacotes so: br.com.1vroandrod.carros.azul e br . comlvroandroid.carros.verme1ho
943 Google Android - 4 edio
Portanto, o usurio poder ter instalado as duas verses do aplicativo ao mesmo
tempo, pois deixei os flavors com pacotes diferentes.
Logo depois de criar um flavor, abra a janela Wew>TooIVndows> BuiIdVariants, conforme
mostra a figura 38.6. Build variant uma combinao de flavors com build type.
Por padro, o Android tem dois build types (tipos de build) que so: debug e release.
O tipo de build debug usado em desenvolvimento e assina a aplicao com o
certificado debug.kesytore localizado na pasta do usurio no sistema operacional.
j o tipo de build release utilizado para publicar o aplicativo na Google Play
e tambm deve ser assinado, porm com outro certicado que voc deve criar.
Ento temos por padro dois build types: debug e release. E agora no projeto dos
carros temos dois flavors: azul e vermelho. Como uma build variant uma combi
nao de flavors com build type, teremos quatro combinaes que so: azulDebug,
azulRelease, vermelhobebug e vermelhoRelease (Figura 38.6). Portanto, ao executar o pro
jeto, podemos deixar selecionada na janela Build Variants qual verso do build deve
executar. Mas antes disso vamos aprender como customizar cada avor.
uild Voriants ' l*
Test Amact.
Module Build Variant
pp Q, a:ulOebug
5. azu!ReIease
c
5
vermelhcebug j

g_errneihqRelease ___ _ '__

Figura 38.6 - Pasta de cache do Gradle.

Por padro, o mdulo app tem a seguinte estrutura de arquivos:


/app/src/main/res
/app/src/main/java
/app/src/main/Andr0idManifest.xml
Mas, como criamos dois flavors (sabores), podemos sobrescrever esses arquivos.
Basta criar as pastas /app/azul e /app/vermelho, conforme demonstrado a seguir:
/app/src/azul/res
/app/src/azul/java
/app/src/azul/AndroidManiest.xml
949
Captulo 38 - Gradle

/app/src/vermelho/res
/app/src/vermelho/java
/app/src/vermelho/AndroidManiest.xmI
No caso do aplicativo dos carros, vamos criar apenas a pasta /res para sobrescrever
os recursos, portanto crie as seguintes pastas no projeto:
/app/src/main/res (esta j existe)
/app/src/azul/res
/app/src/vermelho/res
Agora vamos comear a customizar o aplicativo. Para comear com algo simples,
vamos trocar o nome do aplicativo para (arros Azul ou Carrosvermelho, dependendo do
tipo do build. Crie os seguintes arquivos (preste ateno no caminho dos arquivos).

/app/src/azuI/res/strings.mI
<?ml version="1.0" encodng="utf-8"?>

<strng name="app_name">Carros Azul</strng>

/app/src/vermelho/res/strings.xmI
?xml verson="1.0" encodng="utf-8"?>

<strng name="app_name">Carros Vermelho</strng>

A seguir, vamos customizar as cores do avor vermelho. Note que no azul nao vamos
mexer, pois o padro do aplicativo dos carros j azul.

/app/src/vermelho/res/coIors.xmI

<color name primary"#F44336


<color name primary_dark">#C62828</color

<color name control_highlght">#76FF03 <!_. verde ..>

Pronto, feito isso, as cores do tema Material para o avor vermelho ja estao fgdas
modicadas. Por ltimo, como o aplicativo dos carros tem um provedor d
950 Google Android - 4' edio
conteudo, precisamos customiz-lo no avor vermelho, pois no possvel ter dois
provedores de contedo com a mesma authority. No arquivo de manifesto do
projeto dos carros, existe a seguinte congurao:

AndroidManifest.xmI

<provider android:name=".domain.CarroContentProvider"
android:authorities="@string/provider" android:eported="true"
android:enabled="true"/>

Sendo assim, vamos customizar a authority do provedor de contedo para o avor


vermelho.

/app/src/vermelho/res/strings_tong.xmI
<?xml version="1.0" encoding="utf-8"?>

<string name="provider">br.com.livroandroid.carrosvernelho</string

Lembrando que o arquivo strings_cong.xml padro est assim:

/app/srt/main/res/vaIues/strings_tong.xmI
<?ml version="1.0" encoding="utf-8"?>

string name="provider">br.com.livroandroid.carros</string

<string name="API_KEY">AIzaSyDYCu8-Ijlg-dug-VzT1SC_6fekDKn0oTQ

Pronto! Agora a customizao chegou ao m. Na janela Build Variants do Android


Studio selecione o build azulDebug ou vermelhoebug para executar o aplicativo. O
resultado ser o aplicativo dos carros nas cores azul e vermelha. No mostrarei
nenhuma gura com o resultado, pois no possvel ver as cores no livro, portanto
mmaueopnmonoemuhdmz
A gura 38.7 mostra as duas aplicaes instaladas no emulador, pois cada flavor
foi congurado com um pacote diferente.

Dica: s vezes, trocar a build variant de azulDebug para vermelhoDebug no faz a


compilao inteira do projeto. Portanto, se perceber que algo no foi compilado
corretamente, force a compilao pelo menu Build > Rebuild Project.
951
Captulo 38 n Gradle

v sss:Af.a@<i - f`

Figura 38.7 - Aplcati/os instalados no cmuladoit

38.9 Classe BuiIdCong


Se voc ainda no conhece a classe BuildConfg, vamos apresent-la agora, pois ela
gmadaamnxequeopnnmoconnnhdo()unmdupmeddoconuada&wR,
a qual j nossa velha conhecida.
O cdigo-fonte a seguir mostra o resultado ao compilar o projeto em debug com
o avor vermelho. O cdigo est comentado explicando cada constante disponvel
ria classe BuildCong.

app/build/.../BuildCong.java
public nal class Buildtong {
// Indica se a aplicao foi compilada como Debug ou Release
public static nal boolean DEBUG = Boolean.parseBoolean("true");
// Pacote da aplicao
public static nal String APPLICATION_ID = "br.com.livroandroid.carros.vermelho";
// Indica o tipo de build
public static nal String BUILD_TYPE = "debug";
// Indica o avor (azul ou vermelho)
public static nal String FLAVOR = "vermelho";
// Version code do build
public static nal int VERSION_CODE = 1;
// Version name do build
public static nal String VERSION_NAME = "1.0";
}
952 Google Android - 4 edio
Essas informaes so muito teis, pois podemos descobrir se a aplicao foi
assinada com um certicado de debug ou release. Caso a aplicao seja assinada
com um certicado de debug, sabemos que se trata do ambiente de desenvolvi
mento e podemos adicionar vrios logs no aplicativo para auxiliar na depurao.
j em modo release, recomendado que todos os logs sejam desabilitados antes
de publicar no Google Play Isso preciso porque, caso o dispositivo seja plugado
na USB, todos os logs aparecero no LogCat. Portanto, tome cuidado para no
expor informaes demais do seu aplicativo em modo release.
Mas o que mais gosto da classe Buildtong a constante Buildtong . FLAVOR, que retorna
o flavor com o qual o aplicativo foi compilado. Usando essa constante, possvel
testar se o build do aplicativo foi feito com a verso azul ou vermelha.

CarroServite.java
public class CarroService {

public static boolean isBuildAzul() {


return "azul".equals(BuildCong.FLAVOR);
}

public static boolean isBuildVermelho() {


return "vermelho".equals(BuildCong.FLAVOR);
}

Assim, em qualquer lugar do aplicativo, possvel customizar a verso conforme


o tipo do build:
if(CarroService.isBuildAzul()) {
// Build Azul
} else if(CarroService.isBuildVermelho()) {
// Build Vermelho
}

Dica: no aplicativo dos carros usamos o conceito de flavors para criar duas
verses: azul e vermelho. Lembre-se de que esse recurso pode ser usado para
vrias situaes diferentes, como criar verses gratuitas ou pagas do mesmo
aplicativo, verses de homologao ou produo etc.
Captulo 38 I Gradle 953
38.10 Assinando o aplicativo para o build release
Para publicar o aplicativo no Google Play necess rio assina-lo com um certi
cado de release, que diferente do certicado de debug, o qual j explicamos
por diversas vezes no livro.
O primeiro passo criar o certicado de release. Isso pode ser feito abrindo um
prompt e digitando a seguinte linha de comandofcom rramenta
ae keytool dis
ponvel no JDK:
keytool -genkey -v -keystore carros.keystore -alias 1vro_androd -keyalg RSA -valdty 10000

Voc ter de responder algumas perguntas ao digitar essa linha de comando. Mas
eu particularmente prero criar o certicado utilizando o prprio Android Studio
pelo menu Build >GenerateSigned APK. No wizard, clique em (reate New para criar um novo
certicado. Na janela New Key Store, digite o nome do arquivo, senha do certicado,
o alias, senha do alias e o restante das informaes, conforme a gura 38.8.
Ao clicar em OK, o certicado (keystore) ser gerado na pasta informada. Logo depois
de gerar o certicado, o wizard fornece a opo para gerar um apk assinado com
ele. Mas agora cancele o wizard, pois nosso objetivo era apenas gerar o certicado.

Logiirm. j nn

Qonfrrrnx .N

fmdreid

If--,,.
z fr: .j j j.
foumrv Cade s L

<~~=
5-ia '
~
* 7 z%.. ~ .-51

Figura 38.8 - Criando um certicado (leeystore).

Agora copie o arquivo do certicado para a pasta app do projeto (Figura 38 9)


954 Google Android - 4 edio
.z -v-.._...-..._ ......... fz, - . ..._ ...._-_ zzf.-f ,.-, -..,_. z,z . _-. .,.... \.,.,....,,..-..,_.. ...,..

1 rl'@.*'z* o >_ _c%


` Carros ~
m .idea

u PP
build
` src
.gitrgncre
l app.mI
` buildgradle
carroslteystort

Figura 38.9 - Arquivo do certicado.

Como j temos o certicado, o prximo passo congurar o Gradle para utiliz


-lo ao fazer o build para release. Isso feito no arquivo build. gradle. Basta adicionar
a estrutura sgnngCongs dentro da estrutura androd, conforme mostrado a seguir.
Tambm congure cada avor para usar este item sgningtong. Tenha ateno, pois
devemos informar o nome exato do arquivo keystore que foi criado, assim como
a senha, o alias e a senha do alias.

app/buiId.grad|e
apply plugin: 'com.android.applcaton'
androd {

sgningtongs {
release {
storeFile le("carros.keystore")
storePassword "carros"
keyAlias "carros"
keyPassword "carros"
}
}
productFlavors {
azul {
applcatonld "br.com.livroandrod.carros.azul"
sgningtong sgningCongs.release
}

vermelho {
applcatonld "br.com.lvroandrod.carros.vermelho"
signngtong sgningCongs.release
}

buldTypes { . . .}
955
Captulo 38 I Gradle

repositories { . . .}
dependenctes { . . . }

Dica: uma janela interessante do Android Studio que permite congurafr 0


arquivo build.gradle visualmente a File > Project Structure. Nessa janela, C P0551Vl
visualizar o local do SDK, conguraes do projeto e todas as informaoes de
build do mdulo app, como flavors, assinatura dos certicados e ainda gerenciar
as dependncias. D uma brincada nesta janela, pois tenho certeza de que vOCe
vai gostar. Ao alterar qualquer informao nesses formulrios, tudo ser reetido
no arquivo buildgradle.

E como fazer o build para release? Isso simples, basta selecionar uma das build
variants azulRelease ou vermelhoRelease e executar o projeto.
O build nal, ou seja, o arquivo .apk, ca na pasta app/build/outputs do projeto.
No caso do aplicativo dos carros, ao fazer o build com o certicado de debug, os
arquivos se chamaro app-azul-debugaplc e app-vermelho-debugapk. Ao fazer 0
build com o certicado de release, os arquivos se chamaro app-azul-release.ap/e e
app-vermelho-release.apk.
Outra opo para exportar os arquivos .aple de release utilizar o wizard Build
> Generate Siqned APK. Basta selecionar o certicado keystore de release e preencher o
formulrio. Acredito que isso simples, portanto faa o teste voc mesmo.

Importante: o arquivo .apk assinado com o certicado de release representa


o build nal da aplicao, e exatamente esse arquivo que voc publicar no
Google Play. Nunca perca 0 certicado de release, pois se isso acontecer no ser
possvel atualizar o aplicativo na loja.

38.11 Links teis

Neste captulo estudamos o bsico sobre como utilizar o Gradle no Android


Studio. Como o assunto muito extenso, procurei abordar o essencial que acre
dito que voc usar no seu dia a dia, como por exemplo: criar uma biblioteca e
diferentes builds do aplicativo com avors.
Para continuar seus estudos, seguem alguns links da documentao Qial e
tambm a srie de trs artigos publicados em meu site.
Google Android - 4' edio

Gradle Docs - Dependency Management/Repositories

htlps://gradle. org/docs/currcnt/userguide/dependcncy_managemcnt.
html #sfc:rcpsirri's

Android Tools - Build System Overview

https://dcv'l'pcnandroid.com/sdk/instaNing/studio-build.html

Android Tools - Building and Running from Android Studio

https://devclopcnandroid.com/tools/building/building-studio.html
Android Tools - Gradle Plugin User Guide

http://toolsandroid.com/tech-docs/new-build-system/user-guide
Criando Iibs no Gradle com Android Parte 1

http://ricardolechcta.c0m.br/?p 371

Criando Iibs no Gradle com Android Parte 2

http://ricardolccheta.com.br/Pp 42.3

Criando Iibs no Gradle com Android Parte 3

http://ricardolecheta.com.br/Pp 450
cAPruLo 39

Android Wear
4

O Android VVear C o sistema operacional criado para Wearables, como os relgios


inteligentes (smartvvatches). O termo wearables em portugus pode ser traduzido
como dispositivos vestveis.
Neste captulo vamos aprender a integrar o aplicativo do smartphone com o relgio
e desenvolver interfaces especficas para as telas pequenas do relgio.

39.1 Introduo
O Android Wear permite que voc que conectado ao seu smartphone e receba
noticaes em tempo real. Tem uma interface baseada em cards (cartes), de
forma similar ao Google Now A interao com o usurio feita via gestos e
comandos de voz.

A figura 39.1 mostra alguns dos relgios com Android Wear disponveis no mercado
na epoca que este livro estava sendo escrito. Em ordem da esquerda para direita
esto: Samsung Gear Live, Moto 360 e LG G Watch.

Figura 39.1 - Relgios.

O mundo do desenvolvimento para Android cheio de surpresas. Ora estamos de


senvolvendo aplicativos para poderosos smartphones, ora estamos desenvolvendo

957
953 Google Android - 4 edio
aplicativos para tablets nos quais podemos usufruir de um generoso espao dispo
nivel na tela. A evoluo da plataforma do Android no para, e com o surgimento
dos relgios o foco ter uma interface enxuta, simples e objetiva, especca para
telas pequenas.
Criar interfaces para relgios totalmente diferente de criar interfaces para
smartphones e tablets, portanto precisamos primeiro entender como o relgio
funciona e como deve ser a experincia do usurio neste tipo de dispositivo.
A lista a seguir mostra alguns dos conceitos sobre o design de interfaces para o wear:
lnitiaraplicativos ou cards automaticamente - Usurios esto acostumados a iniciar
os aplicativos manualmente, mas no wear muitas vezes os cards (cartes)
simplesmente aparecem na tela, mostrando informaes relevantes naquele
momento: como um lembrete de uma reunio, alguma mensagem ou email,
uma noticao etc.
Interfaces simples, prticas e rpidas (glanteable) - A interface de usurio deve ser
leve e prtica, pois, quanto menos tempo o usurio levar para entender
o signicado da tela, melhor. O ideal que o usurio bata o olho na tela
e imediatamente consiga entender os dados apresentados. Por exemplo,
cartes que mostram informaes sobre o tempo ou informaes sobre o
horrio de um voo so simples, prticos e concisos, mostram pouca infor
mao, mas o suciente; assim deve ser a interface para os relgios.
Sugesto sobre demanda - Aplicativos para relgio so responsivos e podem
reagir a eventos para auxiliar o usurio em momentos do dia a dia, como
enviar uma mensagem de texto rapidamente para um contato, por meio de
um comando de voz.
Interaes simples - A interao com o relgio toda por meio de gestos e
comandos por voz e deve ser o mais simples e prtica possvel.
Crie o design para telas pequenas - Ao contrrio da tela de um smartphone, o re
lgio deve mostrar poucas informaes e com icones grandes, para facilitar
o toque nos elementos grcos.
Utilize cartes (cards) - Cartes promovem uma interface consistente em toda a
plataforma do Android e representam um aspecto importante do Material
Design. Cartes so simples, prticos e mostram rapidamente a informao
para o usurio.
Respostas rpidas - Facilite a interao do usurio com o aplicativo. Quanto
mais rpido o usurio interagir e executar alguma ao, melhor.
Capitulo 39 I Android Wear

Para mais detalhes sobre os princpios de design para o Android Wear Y@C9mcndO
ler a documentao oficial. Por ora, essa introduo e suficiente, o proximo paSSO
e colocar a mo na massa.
https://dei/elpcrandroid. (`())H/(1('5,{P2/LU('(II'/II(1('X. html

39.2 Hello World Wear

Acredito que a esta altura do livro voc no tenha nenhum problema com relao
a criar um emulador e corn certeza j descobriu que possvel criar emuladores
para smartphones e tablets, Google T\{ Glass Q wear
Ao criar um emulador para o wear, voc deve escolher entre o dispositivo com
uma tela quadrada (square) ou redonda (round). O problema que o espaamento
das telas quadradas e redondas so diferentes e, se aplicarmos o mesmo layout
em ambas as telas, teremos resultados diferentes, conforme mostra a figura 39.2.
A tela quadrada tem 280x280 pixels, e a tela redonda, 320x320 pixels. Embora a
tela redonda seja maior, ela perde muito espao nos cantos devido s margens.

Figura 39.2 - Layout na tela quadrada e redonda.

Para solucionar esse problema, foi criada a biblioteca Wearable UI Library, que failim
criar interfaces para wearables. Ento mos obra, faa os seguintes passos:
1. Crie dois emuladores para o wear: um quadrado, outro redondo.
2. Crie um novo projeto no Android Studio com suporte ao Android Wear
(selecione 0 mdulo do wear no wizard).

3. Execute 0 mdulo wear do projeto criado nos dois emuladores do wear


A gura 393 mostra 0 resultado do Hello World nos dois emuladores (rer Q
round). Observe que, como utilizamos o wizard do Android Studiocada
< 4 verso
mostrou uma mensagem diferente.
95 Google Android - 4 edio
Dica: ao executar o emuladr do wear, siga as instrues para aprender a
manusear os eards (eartoes). O pequeno tutorial do emulador vai ajuda-lo nos
riiiit:irs a:ss. li lvtaii li\w:rtitl

Hgtmz 39-3 - l.ayul separado com Watch ViewStub.

Por padrao, o wizard do /\ndroid Studio gera dois layouts distintos, um para a
tela quadrada (square) e outro para a tela redonda (round). O layout da activity
tvrnteqvaliitizazi\atw\'watchVewStub,qt1e})erntiu:e(n1ruurar)sla}uts para cacha
tnveh:tela erniiatribt1usapp:rectLayoutt:app:roundLayout.((n1rn1etipcLttela
do dispositivo, o watchvewtub vai escolher o layout correto em tempo de execuo.

/wear/res/Iayout/activity_main_wear.xmI
<androd.support.wearable.vew.watchViewStub
xmlns:android="http://schemas.androd.com/apk/res/android"
m1ns:app="http://schemas.androd.con/apk/res-auto"
androd:id="@+id/watch_vew_stub"
android:1ayout_width="match_parent" androd:1ayout_height="natch_parent
app:rectLayout="@1ayout/rect_actvity_man_wear"
app:roundLayout="@1ayout/round_actvty_man_wear" />

Sendo assim, e possivel ter dois layouts distintos para cada tipo de tela, quadrada
ou redonda. A seguir podemos ver estes arquivos de layout.

zzzo /wear/res/Iayout/rect_attivity_man_wear.mI

<LinearLayout androd:orentation="vertica1" . . .>


<TetVew androd:d="@+id/text"
androd:1ayout_width=wrap_content androd:1ayout_heght=wrap_content"
android:tet="@string/heilo_square" />
_ 961
(pI.||0 39 I A|~|d|0d we

/wear/res/layout/round_activity main wearxml


<L"@HfLy0ut androidzorie t
_ n ation="vertical" , _ _,
<TextView android:id="@+1d/text"
--:uu-_

android:tet="@
_ - p_content" d ' - _"
androidzlayout width-"wra
t ' N an Fold 1ayUt-h19ht- WFD_content"
s ring/hello_round />

. , pode utilizar o m r ' ~


No cdigo da Xactivit `'
21 dlfrcna e que no se ' ' f
diretamente, e necessri b ' - e Qdo ndV1ewById(1d)
e utlTIza-
I 1 oopara
o terobter
a view do NatchViewStub
as views internas: que controla os dois layouts

MainWearActivity.java
public class MainNearActivity extends Activity {
private Textview nTextView;
@0verride

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
nal HatchViewStub stub = (HatchViewStub) ndViewById(R.id.watch_view_stub);
stub.set0nLayoutInatedListener(new watchViewStub.0nLayoutInatedListener() {
@Override

public void onLayoutInated(watchViewStub stub) {


// Faa o ndViewById com ajuda do watchViewStub
mTetView = (Textview) stub.ndViewById(R.id.text);
.}

});
}

.- ' '' _to nafratela.


utilizar
em qualquer outra activity Lembrando que tambm Podem g
ments
Prgmo Depois de obter as views, o restante do desenvolvimento normal, como

no Wear, inclusive existe o CardFragment que facilita mostrar um car


~ e o Android Studio
Antes de prossegu1rmOS fepafff qu '` a as bibliotecas
dicionou
do wear no arquivo buildgradle do mdulo Wf

) wear/buiId.gradIe

- ear ,
dependencies {
Compile leTree(df5 'libs', include: ['*-Hf'1>
// wearable UI Library _
conpile 'com.google.android .SU
D port:wearable:1.1.0'
// Google Play Services Pra w . _ rVceS_wearab1e:7.0'0
COMP ile 'com.9009l@-adr1d'9mS'p1ay se
}
962 Google Android - 4 edio
Dica: o HatchViewStub permite utilizar layouts distintos para telas quadradas e redondas,
assim podemos fazer customizaes especcas para cada tipo de tela. Em tempo
de execuo, o layout correto ser aplicado con forme a tela do dispositivo.

Outra forma de criar layouts utilizar um layout nico para ambas as telas,
quadradas e redondas. Isso possvel com a classe BoInsetLayout, que aplica es
paamentos no layout da tela redonda para que o contedo se encaixe melhor
na tela. Para testar, altere o cdigo-fonte do arquivo de layout principal conforme
demonstrado a seguir:

/wear/res/layout/activity_main_wear.xmI
<?xm1 version="1.0" encoding="utf-8"?>
<android.support.wearab1e.view.BoxInsetLayout . . .
android:layout_width="match_parent" android:1ayout_height="match_parent"
android:padding="15dp">
<LinearLayout android:orientation="verticai"
android:1ayout_width="match_parent" android:1ayout_height="match_parent"
android:padding="5dp" app:1ayout_box="a11" >
<TextView android:id="@+id/text" android:tet="@string/he1io_round"
android:1ayout_width="wrap_content" android:iayout_height="wrap_content" />

</android.support.wearable.view.BoInsetLayout>

O importante deste layout o atributo android:padding="15dp" do BoInsetLayout, pois


ele aplicado apenas nas telas quadradas, para que o espaamento que igual
tela redonda. A vantagem de utilizar o BoInsetLayout que temos um layout nico
para todos os tipos da tela, e a programao no lado da activity volta ao normal,
pois podemos usar o mtodo ndViewById(id) diretamente, sem aquele stub.

MainWearActivity.java
public class MainwearActivity extends Activity {
private Textview mTetView;
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
mTetView = (TetView)ndViewById(R.id.text);
}

Depois dessa alterao, execute o projeto novamente no emulador do wear, e O


resultado deve ser 0 mesmo nas duas telas. Note que eu coloquei uma cor cinza no
Captulo 39 i A|1d0d wem,

) L f z ~. , X HSE
lunldo do layout, assim podemos ver que o Bo I 963
nl llmlt PJU Qllt 0 resultado ~ ' ' ~ . tlayllut Pl um 5P3ainento
seja similar nos dois tipos dg tela

Hello Round World!


Hello Round World!

Figura 39.4 - Layout nico com BoxInsctLayout.

Nota: 0 WtCl'lV1WStUb.Ll[lllZ8 layouts distintos para telas quadradas e redondas,


Ja o BoInsetLayout utdiza o n1esrnclayotn; adickonaruo urn espaarnento para
deixar as telas parecidas. Voc devera escolher uma forma ou outra de fazer os
layouts, conforme suas necessidades.

Para nalizar os conceitos bsicos sobre criar interfaces e layouts para wear,
vamos olhar como funciona os temas. No caso do wear, so utilizados os temas
Theme.DevceDefau1t ou Theme.DeviceDefault.Lght, portanto se voc olhar no arquivo
de manifesto encontrar a congurao de tema da aplicao:
<manfest _ . . >
<app1caton . . .
androd:theme="@androd:style/Theme.DevceDefau1t" >

</app1icaton>
</manfest>

' frefa
.\ . \ Y uece
_ C ` . `- .\ l.._
39.3 Conectando o smartphone n0 dmld Wei"
i vemos fazer para desenvo lve
r vara
1 wear e conectar o

.. 3 Anroi
\f` * 3 _ . z
A primeira ta q I, io PW, 550 voc vai pfgusr de um smartphone (dis
Smanphom mm O Kdogd 4d iu suPCrior com o Google l)lly5L`I`\'l&`L`h mst.il.ido.
plmmo
No seu full)
smartpimmm,
f download
Ka do aplicativo do Android Wear dispomxcl nt
Google Play
964 Google Android - 4 edio
Para conectar (parear) o smartphone com 0 emulador do relgio, conecte o
smartphone na USB e digite o seguinte comando no prompt:
adb -d forward tcp:S601 tcp:5601

Importante: sempre que conectar o smartphone USB ou reiniciar o emulador


do wear, necessario executar o comando adb -d forward tcp:5691 tcp:5601 para
parear os dispositivos.

Feito isso, abra o aplicativo do Android Wear no smartphone e utilize o menu (onettar
Emulador (Connect Emulator) para fazer a conexo com o emulador. A gura 39.5 mostra
o aplicativo do Android Wear conectado no emulador do wear.
A comunicao entre os dispositivos feita com Bluetooth, portanto certifique
-se de que o Bluetooth do smartphone esteja ligado. lsso significa que, para os
dispositivos estarem conectados (pareados), eles precisam estar prximos e no
raio de ao do Bluetooth, que algo em torno de no maximo 8 a IO metros.

Parem' com novo wearable

Desconr-:tar nwultor

Esquecer Emulador

Card 5 de 1t~mc~rslrano

i.4i.>'| al

Irlti but; oo wrarahln

i ..i.'1< ropluu 1.10 1:|.. do wz*am{*!4~

. ,,,
i it`lif1I de ixiq .. tw '.~.'.|*.ii7lz -.

"t~i.\ :~ fm' lfurz


1. r\|,~l',..`

Figura 39.5 - /l[>ltlZl'() (lAn1ril llur no smrtphom*.


Dic
ng, 965
Captulo 39 . Andmd wear

az ao c ~ . ,
. r no, menu_Ogl().
Chca
Smartph()n
_ mostradas eno
onectarSero
o smartphone
asno
no, orel'
- , , _ oatiOo de
as
x 'inI ~_ .
ga,-ds C e(D-.P0rao.ad-Recomendo
`wear
Isso ft d~ Lb tb mdf*
l10[lC'1`*`
~
de Demonstrao _, voc
~ 1 1. _
disparar vamos x _ ~ ards)
lc
deno aplicativo doarAndro d Pam

-.`8
exemplos noticaoes 1 eat
Ja tem uma boa Ideia d . . I P Lmulador do wear. Assim voc
-z _ ___-. ..-___.__ P _ terfdce refomcnddi Pra o relogiu

39.4(
onectando o smartphone no relgio fsico
Caso voc tenha um relgio com o Android Wear e queira utiliz lo no lu ar do
emulador siga
, g passos paraos se uintes
conecta '_smartphone:
lo ao seu .
1.
No relgio, entre na tela de (onguraes>Sobre(Settings>About) e clique no item Nmero
do Build (Build number) por sete vezes. Isso vai habilitar as opes de desenvolvedor.
2. Entre no menu das Opes do Desenvolvedor(Deve|oper0ptions) e habilite o debug pela
USB.

3. Conecte 0 relgio na USB do computador. Ao fazer isso, uma mensagem


ser exibida em ambos os dispositivos, solicitando uma autorizao para
fazer o debug.
4. No aplicativo do Android Wear, clique no cone de uma caixa de ferramentas
(configuraes) e selecione a opo Depurao por Bluetooth. Inicialmente a tela
vai mostrar que os dispositivos esto desconectados, com as mensagens:
Host: dsconnected / Target: connected.

5. Abra um prompt e digite os seguintes comandos:


adb forward tcp:4444 localabstract:/adb-hub

adb connect loca1host:4444 d

ocial: .
~ ' ` d Android Wear voc vera que os is
6.
' ` de
Na tela foram
positivos conectadoos
Conguraoe com as
aphcanvo mensag
O ens '
Host- connected / Target: connected.

consulte o tpico Debugging over Bluetooth da documentao


Para mais informaS,

Cl. m-nOlCmll
dor
- /d lo er android.com/training/wearables/aPP5/bt'debggmg`html
httpsi/ eve p i tilizar o emulador do Wf Para lcslar Os exem
21
De qu al uer forma, fewmendo U
plos d o livro, pois todos funciona
966 Google Andoid - 4 edio
39.5 Noticaes no wear
\ W" U 'flyli \`l\cl.\d zu |u;\rlplum'. rmlns as uu ic:\:s du snxznrtplmnc
.\p\|'vu'|x1 uu n*I`gm_ I\I;s,c;1s xfnu quz-ir;|, v pussvcl pclu API couigumr uma
"*'''\`4K P;lI`.l .\|\L`I;Ih .IP.\I`\`\`\`l` uu s|u;u'lp|unc uu rc|'gi.

\`.lI`.l l\l`l\L`.\l` uuu .ns 1\liIic;\g`vs_ nlunn pmjctu HeIION0tifI(ati0n quc izcnuws uu
\~_w1tulu Ji sulur 1utiix1g(cs. v lispnrc .us l\(\liL`;IL`S no SlT\1ll'I[)h()l\L` (cstnndu
\`Ul\L`\`l;\\lU |uvuu1l.ulr| \\'m1`)..\u mm'is_ulns;1mxifiz1`L-5 scrn|11>;
lI.l\l.l uus|u1rlplu|u*v |ucuu||.u|m'du\\'c;u'.
.~\ luzum Wi umstm ;\ m1iliu.1g.`n uuplcs lu p|'cu HeIIoNoti(ation no cnuxlulm' du
\\\~.u'. NoLulucsq1u'ulul;1 i3u1|'.1\.1 |\liic;n.`|n;1p;11'u'cuu Ich iuirinl c.msclcci|\;i
1.1 v.1r1.`n \c;uxl\ sulwc pnm x`IllI;I qxuwc D. Nu lL`l`\`L`I'.l |mrlc da figura. plc|ms
W131 p.\;zu\.1 quc umsuxu ;\ npginw p;ux1 .ulwrir .1 |m1iiu1;1 uu \lll;ll`I[1h(\l1L`. Scmprc
quc uum ililvlll L`Sl\'L`I` ;1ssm.ul;1 C1 mui lic;1. l p;`1 Open on phone vstzuxi dispm\'cI.

voc posam num


HVJ |Hl\S(]tIN
Lt U>Ll= LIIHJ

I~`igzr. .Wo -- I\'u!i_i`.n'Ju nu uxxu.

Ouuv \`\(`Il\PIU que lcuuvs uu plvjrln He|IoNotication C ;\ 11nlitic.1g.1n glzlmlc dug


untiic.1Iiuu\ un qu.| .1dicuu1.u1us trs 11u'11s.3;cus..-\|igum Nf xuouzx msm nn
tiiic;1fu uu :uu1l;ulu' do \\'c;u'.

I~`:gzu'.: _*)." - \nr:fi.\'.ugr.r:z1z no uxur;


(plIl0 39 | A|1dmd wear

l iwiwr liiiii iiiii cliis xK() ]1_ `_ 967


capitulo l mim. Im[imgL\S`. ililililia-L~ss;iiucs, o qual iaiulwnm mm)S nn
nmflm .il nlll*`*N> com a acao dc Pause lol llmlhmim mm "*- ^ igura M8
sraciilhuwarachis ui;u)hkuV(XlSSC tuxo i PY~l\o cXciu;u.SSnS ucS`ncn[S
usuario 5cOm. uma mm mm rm ) iiiin~icz1o L. mmm mil mm qm O
rcnnCn.d(,SclrCh5giU`S` ~`_.oliuiccialguiuaiiun1uacaidclorunirapkh,
Llil ill.ll o cillllal do liolso,

Mmwdomuma

FQQIIIU 39.8 - l\l>Ii/ll`i(ii Ho \l'iU;

39.6 Noticaes com vrias pginas


No wcar muito comum tcrmos noiilicacocs com varias pzginzi, dc forma que
o usurio pode fazer o scroll lalcral para visualizar os proxiiuos cartes (cards).
[Kira clcrnnstrar ciniio criar iurui nuificagxniiaginas.ca1c a classe
Noticationweartiii1iru3diiklo sniartr1nic,cn1rniclcnioinnrachwziseguir

o NoticationWearUtiI.java

public class Noticationwearutii {


public static void createPagesNotication(Context context) {
// Pgina 1
Noticationompat.Builder noticationBuiider =
new Noticationtompat.Bui1der(contet)
.setSma11Icon(R.mDW8D~C_lU"Che'l
.setContentTit1e("Pgina 1")
.setContentTet("Pfelra e"5a9em l
/_SetcontentIntent(viewPendin9f@tl*/5
// Pgina 2
Notification 93992 =
t.Bui1der(contet)
new NoticationC0D
.setSma _ H
11Icon(R-WDaP-ic-launcherl
.setContentTit1@('P09\ 2 l
963 Google Android - 4' edio
.setContentTet("Segunda mensagem")
.bui1d();
// Cria as pginas
Notcation noticaton =
noticatonBuilder.etend(new Noticationtonpat.Hearab1eExtender()
.addPage(page2)).bu1d();
// Dspara a notcao
NotcatonManagerCompat nm = NotcatonHanagerCompat.from(context);
nm.notfy(1, notcaton);
}

Nota: o importante desse codigo e a classe Nearab1eEtender. Ela e responsavel por


adicionar funcionalidades especificas do wear nas notificaes.

Em qualquer lugar do codigo (do smartphone), chame este metodo para criar a
ncnicaco:
NotcationwearUt1_createPagesNotcation(this);

ll
No smartphone somente a primeira noti cao ser exibida. porm no wear voc
ver as duas paginas conforme a gura 39.9. Se quiser conlerir o exemplo funcio
nando, abra o projeto He|IoWearNotications no Android Studio. Lembre-se de executar
o mdulo mobile no smartphone e 0 mdulo wear no emulador do Android \\/ear.

` _.
Segunda
Pnnqea llellsflgelll
mensagem

Figura 39.9 - Niicaa com duas pginas.

39.7 Noticaes empilhadas


Outro tipo de notificao muito comum no wear so as notificaes empilliadas
(stack notications), que mostram uma notificao com um indicador de que
existem mais N mensagens, conforme a figura 39.10.
Captulo I Andmid wear

' ' " -Ce tl I .


NoIsmartphone voc
g. `- GS trs notie ~ ver cst 969
automaticamente elas so agrupadas VO A aois separadamente, mas no Wear
no hrolcto HeIIoWearNotications. am em pode Conferir este exempl

Mensagem 3 Mensagem
i Mensagem 3

Mensanern? Mensa
9 er1
Figura 3910- Noticaes empilhadas.

.elc:c::o;;;t;:(nUtl
i u que cria uma da notlcaao
biblioteca android-utils
agrupada. contm um mtodo
O cdigo 0
mesmo utilizado para criar qualquer outra noticao, com exceo do mtodo
setGroup(groupId), que recebe o cdigo do grupo. Notificaes que forem criadas
com o mesmo cdigo de grupo sero agrupadas automaticamente no wear.
1:
eg NoticationUtiI.java
public class Noticationutil {

public static void createStackNotifcation(Context context, int id,String groupld,


Intent intent,int snalllcon, String contentTitle, String contentTet) {

l,t~
Em
}

qua
// Cria a notication
NoticationCompat.Build er builder = new NoticationConpat.Builder(contet)

QUCT
1 1 d digo
.setContentIntent(p).setContentTitle(contentTitle)
.setContentText(contentTet).setSnallIcon(smallIcon)
.setGroup(groupId)
.setAutoCancel(true);

UE-Ear chame
(do smartphone),
not1hca,ao.VC]1 aus C5 _ O*)WOG)Qw
. V `~ ~, 'tou passando sempre L
C este
'd' metodo
do *ru ocrllf fi
para
E P
1,"zwpx",1nrenr,R.fvav-1a'*'~

111, 1I|);
NotificationUtil . createStackNotificati0 ( UWS ,
97 Google Android - 4 edio
NoticationUtil.createStackNotication(this,Z,"GrupoXXX",intent,R.mipmap.ic_launcher,
"Titulo 2","Mensagem 2");
NoticationUtil.createStackNotication(this,3,"GrupoXXX",intent,R.mipmap.ic_launcher,
"Titulo 3","Mensagem 3");

39.8 Noticaes com comandos de voz


Sem dvida, os comandos de voz so uma das funes mais importantes do wear,
e esse tipo de recurso pode ser facilmente criado por meio de uma notificao. Por
exemplo, se o aplicativo do smartphone precisa coletar uma resposta do usurio,
podemos enviar uma noticao para o relgio, solicitando que o usurio fale
uma resposta ou escolha uma dentre as possveis respostas apresentadas.
Para criar uma noticao que responda por meio de um comando de voz,
preciso criar um objeto do tipo Remotelnput informando um array de opes com
as respostas (opcional) e a chave que ser utilizada para enviar a resposta pela
intent de retorno. No cdigo de exemplo a seguir, a chave a string remote.input.key.

NoticationWearUti|.java
public class NoticationwearUtil {
public static void createRemoteInputNotication(Context context, Intent replylntent) {
// Intent para executar no smartphone ao responder
Pendinglntent replyPendingIntent =
Pendinglntent.getActivity(context, G, replylntent,
Pendnglntent . F LAG_UPDATE_CURRENT) ;
// Remote Input
String[] replyChoices = contet.getResources().getStringArray(R.array.reply_choices);
Remotelnput remotelnput = new Remotelnput.Builder("remote.input.key")
.setLabel("Resposta") // Titulo
.setChoices(replyChoices) // Array com respostas
.build();
NoticationCompat.Action action =
new NoticationCompat.Action.Builder(R.drawable.ic_action_reply,
"Responder", replyPendingIntent)
.addRemoteInput(remotelnput)
.build();
// Cria a noticao
Notication notication =
new NoticationCompat.Builder(contet)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Lembrete")
I

Captulo 39 z Am|d we f
97
.setContentTet("Voc vai feSta,") 1
Not`F ' . .extend(new Notificationton
t. l
.bu1d(); Da HearabIeExtender().addAction(action))
I'catl"Ma"a9fCWDt not1cationManager z
Not'f

z . Q $_
n t`f . IICt10HdgFCONDt.fF0(C0tXt);
} O 1'Cat1nMa"a9r~1fy(1, notcation);
}

O array com as rSpostas e denido no arquivo arr 1


a pratica.
as possiveis respostas opcional pgrm uma bo Y - Fornecer a lista com

/mobile/res/vaIues/arrays.xmI
<?m1 version="1.0" encodng="utf-8"?>

<5tf"9'FFy @="rep1y_choices">
<'Ef'1>5|'l</tI'1> <.tem>N0</tem> <tem>Ta1vez</tem>

Para testar as noticaes com resposta por comandos de voz, basta chamar um
cdigo como este e denir a activity que vai receber a resposta:
Intent intent = new Intent(ths, Rep1yActvty.c1ass);
Noticationweartil.createRemoteInputNotcaton(this, ntent);

Neste caso, quando o usurio responder o comando de voz no relgio, a activity


ReplyAct`vty ser chamada no smartphone para receber a resposta. Para ler a
resposta, deve-se utilizar o mtodo Remotelnput.getResu1tsFromIntent(intent) a m
de obter o Bundle e na sequncia ler a mesma chave que foi utilizada para criar
a notificao, que relembrando a string remote.nput.key.

RepIyActivity.java
public class Rep1yActivty extends ActonBarActvty {
@0verrde
B ndle savedInstanceState) {
protected void onCreate( u
supe r.onCreate(savedInstanceState);
setContentView(R-HYOU-activity-reply);
getSupportActionBar() ___
.setDisp1ayHomeAsUpEnab1ed(true);

Textview text = (T@tVle) "dVlewById(R'ld'tet)'


1 t nt()));
text.setTet(9@tM@55a9Tet(9et n e
Notificatontil . cance11A11( this);
}
972 Google Android - 4 edio
// L a resposta da voice input
private String getMessageTet(Intent intent) {
Bundle renotelnput = Renotelnput.getResultsFronIntent(intent);
if (remotelnput != null) {
// Mesma chave utilizada para criar a intent voice input
CharSequence c = renotelnput.getCharSequence("renote.input.key");
return c != null ? c.toString() : null;
}

return null;
}

Nota: notificaes que respondem por comandos de voz so chamadas de Remote


Input Notications.

Caso queira conferir o resultado, este exemplo tambm est disponvel no projeto
HeIIoWearNotications. A gura 39.11 mostra o resultado da noti cao. Ao rolar a tela para

ir
o lado, o usurio pode clicar no boto Responder. Para responder, possvel utilizar o
comando de voz caso voc tenha um relgio real, ou escolher uma das opes que
so previamente apresentadas na lista. Ao escolher a resposta, a activity recebe o
retorno pela intent, mas isso voc ver por si mesmo ao executar o exemplo.

Voce vaia iesta?

Figura 3911- Remote Input Notification.

39.9 Google Play Services e Wearable API


Uma das funcionalidades mais importantes do wear a capacidade de enviar
e receber mensagens do smartphone. Novamente, tudo funciona com base no
Google Play Services, conforme mostra o seguinte template de cdigo:
Captulo 39 1 Andmd Wei"

G91@ApiCIient .
. 97
GoogIeAptCIient - new GOOQIQA ' 1 3
.addConnectionCaII.backs(new ConnectionCaIIIi)ifk;f;t Bui1der(this)
Dubli '
.add0nC t' ' - ~ ~ -I I )
pubuc void onConnected(BundIe connectionint) { _ _ _}
c void onConnectionSuspended(int cause) {

pub{::nq):nFqiIedListener(new 0nConnectionFaiIedListener()
i on onnectionFai1ed(Conne t' R {
// Adiciona a Hearable API C Ion esult result) {' ") } II
.addApi(Hearab1e.API)
.buiId();

ar. A
lhe deve serefamili
- ,3
Como j estudamosgleo Play
Goo P no livro acredito que este cdi o
Services
p nas observe que estamos adicionando a Wearable API no
mt(?d0 add^P1(pi). Lembre-se tambm de que a dependncia do Google Plz
SfVlCS deve ser declarada no arquivo buildgmdle do mdulo Uma vez que);
conexao com o Google Play Services foi estabelecida, podemos usar as bibliotecas

Message API e Data API. '


de comunicao da Wearable API, a qual dividida em trs APIs que so: Node API

39.10 Node API


A Node API utilizada para obter o identificador do dispositivo conectado, seja
o smartphone ou wearable. Esse identicador chamado de nodeld.
Uma forma de obter o nodeld utilizando o mtodo getConnectedNodes( . . .), conforme
demonstrado a seguir. Note que necessrio informar uma implementao da
interface ResuItCaI1back para obter a resposta de forma assncrona.
Hearab1e.NodeApi.getConnectedNodes(nGoogIeApiCIient) .setResuItCaIIback(
new ResuItCa1Iback() {
@0verride
G tConnectedNodesResuIt getConnectedNodesResuIt) {

II); , ,
public void onResuIt(NodeApi. e
_St<N0de, nodeg z getConnectedNodesResuIt.getNodes();
if (nodes gz null && !nodes.isEnpty()) {
Node ngde : I'\0deS.gt(0);
nodeld : |'\0d.getId(); Este e O nodeld

' e mais - _, - . .
I

'. '. vezes sermeto -da'Nodc


, . 1c o
' de um dispositivo conectado, Pdm5 dlzcr

que na malona. Outro


. . ta c
das ' do interessante _ _.
Embora esse metodo retom apenas um Dc qualquer forma. em aplicativos
API
Pmsslonals 1550 deve ser iratmlo. _ z tonectado ou no. Para ativaro Iisteml
C

listener queomtora
m I _se 0 dispositivo es
basta utilizar este cod1g0.
974 Google Android - 4 edio
Goog1eApiC1ient mGoog1eApiC1ient = .
NodeApi.NodeListener nodeListener = _ . .;
wearable.NodeApi.addListener(nGoog1eApiC1ient, nodeListener);

Como parmetro deve-se informar uma implementao da interface


NodeApi.NodeListener que contm dois mtodos para indicar se o dispositivo foi
conectado ou desconectado.
void onPeerConnected(Node node) { // dispositivo conectado }
void onPeerDisconnected(Node node) { // dispositivo desconectado }

Isso muito importante para saber se o wearable est no raio de ao do


smartphone, pois sabemos que a conexo entre eles feita via Bluetooth.

39.11 Message API


A Message API extremamente til para enviar mensagens curtas e pequenas
entre os dois dispositivos. A mensagem identicada por um path (caminho)
que deve comear com "/" e o contedo um array de bytes.
Para enviar a mensagem, necessrio o nodeId obtido com a Node API. A seguir,
temos um exemplo de mensagem enviada, no qual o path "/nsg".
wearab1e.MessageApi.sendMessage(nGoogleApiC1ient, nodeId, "/msg", new byte[]{1,2,3});

Para receber a mensagem, o dispositivo deve ativar o Iistener da Message API,


informando uma implementao da interface MessageApi.MessageListener.
Goog1eApiC1ient nGoog1eApiC1ient = . . .;
MessageApi.MessageListener nessageListener = . . .;
wearable.DataApi.addListener(mGoogieApiC1ient, messageListener);

A interface MessageApi . MessageListener contm apenas o mtodo


onMessageReceived(messageEvent) que recebe a mensagem enviada. Neste mtodo po
demos obter o path que identica a mensagem, o nodeId de origem e o contedo
que o array de bytes.
public void onMessageReceived(na1 MessageEvent nessageEvent) {
String path = nessageEvent.getPath(); // identicador
String nodeId = messageEvent.getSourceNodeId();
byte[] bytes = messageEvent.getData(); // mensagem
}

Geralmente, as mensagens enviadas so pequenos bytes ou nmeros inteiros que


apenas indicam aes que os aplicativos devem fazer. Por exemplo, uma ao seria
abril `~l '1[i\;it \
Captulo 39 I |\d|d we"
M

. ` ` 3" X mostrei ` ` .f ,.
~ 2 IL a entre os dia . . ara trocar
iuhmnaes de fmn r ipa poncagao Y etc.AMessage
~ POSlIlv05, API foi criada p

...z ' L OUSC1


e onlin~ ` ~ nf- _
Nota: a I\\S;
*JH,_ AH
z
tstqam conectados. l J* ` n5*'*"l (1116 Os dois dispositivo;

39.12 Data API


A DataAPl compartilhar
` - ~ ~informaes entre o smartphone e o wear-able
utilizando
Dl para
um z ' .
a estrutura 5lmPle5 de Chave e valor. Os dados adicionados na estru

se
.os. -,di.`_' ~_ ',. _
tura sero automaticamente ncromzados
si ~ ` entre o smartphone Q O \vearable. Mesmo
5P05mV05 estelam d5C0nCI2!Cl0S, a sincronizao ser feita ao conect-los.
l dra enviar e armazenar mtormaoes na area de compartilhamento com a Data
API. podemos utilizar um cdigo como ester
Bundle bundle = new Bund1e();
bund1e.putStrng("nome",Ricardo);
String path = "/nsg";
PutDataHapRequest putDataHapReq = PutDataHapRequest.create(path);
Dataap dataap = Dataap.fronBund1e(bund1e);
putDataHapReq.getDataMap().putA11(dataHap);
PutDataRequest putDataReq = putDataHapReq.asPutDataRequest();
Nearab1e.DataApi.putDataIten(nGoog1eApiClient, putDataReq);

O identicador da mensagem o caminho (path) "/msg". e os dados podem ser


enviados por um Bundle, o qual convertido para um Dataap. Dessa torma, conse
guimos compartilhar o contedo entre o smartphone e o wearable com a estrutura
de chave e valor com a qual j estamos acostumados.
D0 outro lado, para ler os dados. e preciso ativar o listener da Data API. para ser
noticado sempre que ocorra uma mudanca nos dados compartilhados entre os
.

dispositivos.
C.oog1eApiCIient G009l~^0U~"'t = ~ '*
DataApi.DataListener dataListener = . .dataLrstener);
1 ApiC1ient, .; _
Hearab1e.DataApi .addListener(nG009 0
Listener contem apenas o mtodo onatathanged(dataEventBuffer).
A interface DatAD-ta
ataEventBuffer) {
public vo id onatathanged(DataEventBuffer Cl
for (DataEvent event : dataEvent8uffer) {
DataEvent.TYPE_C"^"5) {
if (event .getTyP@() =*
976 Google Android - 4' edio
Datalten tten = event.getDataIten();
if (ten.getUrt().getPath().conpareTo("/nsg") == 9) {
Datarlap dataap = Datarlaplten.fronDataIten(ten).getDataMaD();
String none = datallap.getString("none");
} else if (event.getType() == DataEvent.TYPE_DELETED) { _ . .}
I
I

Esse cdigo veri Fica se o evento gerado do tipo DataEvent.TYPE_CHANGED, para depois
obter o objeto Dataltem que contm os dados. Um Datalten composto do path
(caminho) que identica os dados e um Dataap que funciona da mesma forma
que um Bundle. Vale lembrar que a Data API pode ser usada pelos dois lados,
sendo que ambos, smartphone e wearable, podem armazenar e ler as informaes.

Nota: a Data API funciona de forma offline. Caso os dispositivos no estejam


conectados, o evento onDataChanged(...) ser chamado futuramente quando houver
uma conexo. Recomenda-se que o contedo compartilhado no exceda 100l<b,
pois a transmisso feita via Bluetooth.

39.13 Enviando mensagens entre o smartphone e o Wear


Agora vamos juntar os pedaos e criar um exemplo que vai unir a Node API,
Message API e Data API.
Crie um projeto no Android Studio chamado HeIloWear e no wizard selecione para
criar os mdulos para smartphone/tablet e wear. Siga o wizard passo a passo e crie a
ManActvty de cada mdulo. Por conveno, gosto de chamar a activity principal do
smartphone de HanHob1eActvty e a activity principal do wear de Mainweamctvty.
Com o projeto criado, vamos criar um mdulo para compartilhar o cdigo-fonte
entre os mdulos mobile e wear. Crie um mdulo com o wizard FiIe> New Module, sele
cione o item Android Library e digite shared para o nome do mdulo.
O resultado do projeto no Android Studio pode ser visto na gura 39.12. Ambos os
mdulos, mobile e wear, foram criados automaticamente pelo Android Studio. Dentro
deles temos as classes e recursos com os quais j estamos acostumados. Lembre-se
tambm de que cada mdulo tem seu prprio arquivo build.gradle e pode scr execu
tado separadamente pelo boto Run do Android Studio. A gura 39.12 tambm mostra
o mdulo shared que acabamos de criar. I)entro do mdulo shared, vamos adicionar a
classe HearUt1 que ser compartilhada pelos mdulos mobile e wear.
Captulo 39 z Android WW
r

mobile 64 az Q. .-
ri 'l`a\*L
^%^nuwu .
`f Li
` 'att
i ~ ` 11t ,zl'Lvll\1()ld
v
-\, _ i`z->1l_\_Jlp,

WF.
br r r~_z --. _/oc
L z 'zzn ,'z>t>L;i._zr\,. ,

LO H [\'| 0.jnLjf|(j jam


ea les
K --

shared
L
`C`|3r1F(55

oicorn li~.'roandroid.shared
||I|||||IiEIME5EEi|||||||||||||||||IIIIIIIII
'~ tzr.com.l,roandrod.shared .~ ~
l ' F
f' " Weaf
lcfi F
z

DF CC z~r- ii~,1'g5[,d-O_1 huodetapk


.'~.3iFTf,'i5rL{__,|:'

21 Vcs
"= ^_ _. v
" orzdle Scrlprg

Figura 39.12 - Projeto com trs mdulos.

Conforme apresentado na gura 39.12, crie a classe wearutil no mdulo shared

WearUti|.java
package br.com.livroandroid.shared;

public class NearUtil {


private static nal String TAG = "wear";
private nal GoogleApiClient mGoogleApiClient;
private DataApi.DataListener dataListener;
private HessageApi.HessageListener messageListener;
private NodeApi.NodeListener nodeListener;
// id do outro device (mobile ou wear)
private String nodeld;
public HearUtil(Contet context) {
this.dataListener = dataListener;
this.nessageListener = messageListener;
G leA iClient Builder(context)
nGoogleApiClient = new oog p
@0verride _
addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {

ndDeviceNodeId();
// Ao conectar, liga a DataAPI e Message);
- API
public void onConnected(BUd1@ COHHECOHHIH) i

ices ononnected!
tg.1Ao, "Play Serv
munmnnlzzuuuzu
ifdatListenr I nult) {
1ezd1. ddrieenef tA@\1
Hoorcslszitapiza dttscnoftnunglhlittlnt1 liiLiI0F11
1

iftmvaaliter l ull) (
Lqzdcfu. aduiernef n@^9ii
iaf|bteznesgeAptzaddtisteneftnooplohpttiont, n|9o&ttenuf)
1

1f(f1H9|'wv ! 69111 1
Lea d(1A. uifenr Ned@Api1
weofbtezndepizdaistnerneegteApitient, nod0Ltenef)
1

dvvffi
pul1 vx @nraee1i@us@d(int @u@1 L
Lazaia, anenfinupnde " z ui
1

11
zdnn1tnfi\dLit@f(w Ge919i1ienzncanneienri1edLitf( L
v fi
pHh1\ vaia 86G0i1Q(GH@16U1f ruit (
L;G(1Gz "enentienrite " 1 f@u1t)
1

11

zdB(H@fizF)
zE911(1;

1_
bmi wxa f@fLi@@r(@@A1z1Lttf ttttef) {
thizdtrxtf ttiffg
1

auii axa fngm1tnf(naAp\znmitenr meqelxtnsrm 1


ihxzgtitnaf = dlllfi
1

utx vaia thanitfkagzaaritnu H6@111@@11 1


itzcrltr nsvtitnrg
1

// ei a Quglo vty fit


avalie vw fn@sf() 1
':l'Jf
850 iD%@11Oi:E8E(11
1

II hiE6el da nl b1v sivz


Cumulo 39 - Anamna wa

Dubllc votd dlscomnoct() { 979


LQ9-d(T^G. dlsconnect()");
f(G091eA0lCltent.lsConnected()) {
// Desliga as APIs do wear
Hearab
le.DataApl.removellstemer(mdooglempltllent tht
wearable.HessageApl.remo .Lt ' sdataLtSt"er)
V9 5t"@f(mGoogleAplCllent thls messagellste
} Nearable.NodeApl.removeLlstener(mGoogleA ltll ' ' net);
D ent. thls.nodeLlstener);
mGoogleAplCllent.dlsconnect();
}

/I DataAPI: Compartllha os dados


publlc vold put0ata(Strlnp path,Bundlo bundle) {
Log d(TAG, "> putData() " + path);
Put0ataMapRequest put0ataMapReq = PutDataHapRequest.create(path);
Datahap datahap = Datahap.fromBundle(bundle);
putDataMapReq.getDataMap().putAll(dataMap);
PutDataRequest putDataReq = putDataHapReq.asPutDataRequest();
Nearable.DataApl.putuataItem(mGoogleAplCllent, putDataReq);
}

// Message API: Envla uma mensagem


public vold sendHessaga(Strlng path,bytn[] msg) {
Log.d(TAG, ">> sendMessage() " + path + ", to: " + nodeld);
lf(nodeId != null) {
Hearable.MessageApl.sendessage(mGoogleAplCllent, nodeld, path, msg);
}

// Node API descobre o ld do outro device (mubil OU wf)


public vold nd0ovlcoNodoId() { _
Ch d sslncrona, lnforma o callback

now ResultCallback() l
aralz gogeApl.getConnectedNodes(mGoogleAplClet)-S@fR5U1tCl1bk(

@0verrlde
l.GetConnectedNodesResult getConmectedNodesResult) {
public vold onResult(Nod^P

n.`n;
LSt<Nde, ndgs = getConnctedNodesResult-9@fNd5()3
if (nodes yz null da !modes.lsEmpty()) {
Node node = nodes.9et(@)i
odald n mod! 90f1d()5
nQdQId, Q* nodeld)
}

);
l
930 Google Android - 4* edio
// Cria un Asset a partir de un Bitnap
public Asset getAssetFron8itnap(Bitnap bitnap) {
l- 3VteArray0utputStream byteStrean = new ByteArray0utputStrean();
bitnap.conpress(Bitnap.ConpressFornat.PNG, 196, byteStrean);
return Asset . createFromBytes(byteStrean. toByteArray( ) );
}

public Bitnap getBitnapFronAsset(Asset asset) {


if (asset == null) { return null; }
InputStrean in = Hearable.DataApi.getFdForAsset(
mGoogleApiClient, asset).await().getInputStream();
Bitmap bitmap = BitnapFactory.decodeStream(in);
return bitmap;
}

O cdigo desta classe grande, mas no se assuste, pois boa parte referente
conexo do Google Play Services, e o restante apenas para encapsular as APIs
do Wear. Essa classe encapsula o cdigo necessrio para se conectar no Google
Play Services e quando o mtodo onConnected(bundle) for chamado estamos adicio
nando os listeners da Data API, Message API e Node API caso eles tenham sido
informados pelo cdigo cliente.
Na classe tambm criamos o mtodo putData(ath,bundle), que demonstra como
compartilhar dados com a Data API, e o mtodo sendMessage(path, bytes[]), que
demonstra como enviar uma mensagem com a Message API.
O importante desse cdigo que para enviar uma mensagem para um dispositivo
necessrio descobrir o id do n de destino, ou seja, o id do outro dispositivo, seja
smartphone ou wearable. Para isso, foi criado o mtodo ndDeviceNodeId(), que utiliza a
Node API para buscar o id do dispositivo conectado. O id armazenado no atributo
nodeld da classe para depois ser utilizado para enviar as mensagens pela Message API.
Vamos prosseguir e criar um exemplo para enviar mensagens entre o smartphone e o
wearable. No mdulo mobile altere a HainMobileActivity, conforme demonstrado a seguir:

MainMobiIeActivity.java
public class MainMobileActivity extends BaseActivity {
private Heartil weartil;
private int count;
@0verride
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_man_nobile);
Captulo 39 I Android Wea;

WGFUI : n 981
ew HearUtil(this);
}

@0verrid@

protected void onResume() {


5UD@r.onResume();
wearUtil.connect();
}

@Override

protected void onPause() {


super.onPause();
wearUtil.disconnect();
}

// Envia uma mensagem pela Message API


public void onClickSendMessage(View view) {
count++;

} wearUtil.sendMessage("/mso", new byte[]{(byte) count});


// Compartilha um Bundle com a Data API
public void onClickPutData(View view) {
count++;
Bundle b = new Bundle();
b.putString("msg", "0l, Data API");
b.putInt("count", count);
// Cria o Asset

ac: y _
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ferrari_ff);
Asset asset = wearUtil.getAssetFromBitmap(bitmap);
b.putParcelable("foto", asset);
// Compartilha os dados com o wearable
wearUtil.putData("/msg", b);

H_
}

}
e wearUtil e dene
A tivit conecta-se ao Google Play Services com a ajuda da class
os mtodos onClickSendMessage(view) e onClickPutData(view) para envrar mensagens.
Para chamar esses mtodos, vamos adkjonarchslxnesnolayoutdaacvny:

/mobiIe/res/Iayout/activity_main_mobiIe.mI

' droid:
an . _ ex = _ _ ,, H
<LinearLayout . . .>
t t "@string/hello world"
<Tetb;1:Tlay0ut width="wrap content" android:laY0Ut-he19ht= "rap-Content />

androidzlayout _width="wrap_content android BVO


932 Google Android - 4' edio
androd:text="Send Hessage' android:onC1ick="onC1ickSendHessa9e" />
<Button
androtd : layout_width="wrap_content " androd :1ayout_height="wrap__content "
androd:tet="Put Data" android:onC1ick="onC1tckPutData' />

No mdulo wear vamos fazer a outra parte do aplicativo, a qual vai receber as
mensagens. Neste exemplo vamos receber uma mensagem simples pela Message
API e outra que vamos ler os dados compartilhados pela Data API.
No arquivo de layout vamos adicionar um Textview para receber o texto enviado e
um Imagevew que vai receber o Bitmap (Asset) quando os dados forem enviados pela
Data API. Para denir o layout da activity do wear, temos duas opes: a primeira
utilizar o layout HatehVewStub e especicar um layout diferente para cada tipo de
tela (redonda ou quadrada). A segunda utilizar 0 layout BoInsetLayout, 0 qual
permite utilizar o mesmo layout para ambos. Por padro, o wizard gera os arqui
vos seguindo a primeira opo, com os layouts separados para rect e round. Mas
para simplicar o exemplo estou usando a segunda opo, com um layout nico.

/wear/res/layout/artivity_main_wear.xmI
<?n1 version="1.6" encoding="utf-8"?>
<android.support.uearab1e.vieu.BoxInsetLayout
xn1ns:androd="http://schemas.androd.com/apk/res/androd"
xn1ns:app='http://schemas_androd.com/apk/res-auto"
androd:1ayout_wdth="match_parent" android:1ayout_height="match_parent"
androd:paddng="15dp">
<LinearLayout androd:orientation="vertica1"
androd:1ayout_width='match_parent" android:1ayout_heght="match_parent"
android:padding='5dp" app:layout_bo="a11"
TextVeu android:id="@+d/text"
android:layout_wdth="wrap_content" android:1ayout_height="wrap_content" />
<InageVieu androd:id='@+d/img"
android:1ayout_width='wrap_content" android:layout_height="wrap_content" />

</androd.support.wearable.vew.BoInsetLayut>

O prximo passo alterar a classe HainHearActvty no mdulo wear, conforme


demonstrado a seguir. Observe que o cdigo est utilizando a classe Hearutl
da mesma forma, mas tambm estamos informando as interfaces Datatstener c
Hessagestener para receber as mensagens que sero enviadas pelo smartphone.
Captulo 39 z Andfoid ww
Hi
l
MainWearActivity.java
public class Main
NearACtVY extends Activity im 1
Me559eAp.H . A D{ Gents DataApi.DataListener,
essageListener
private
static nal String TAG = "wear"
private Textview mTetView; ,
private Imageview img;
private NearUtil wearUtil;
@0verride
protected void onCreate
(Bundle savedlnstancetate) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nain_wear);
mTextView = (Textview) ndViewById(R.id.tet);
img = (lmageview) ndViewById(R.id.img);
weartil = new HearUtil(this);
wearUtil.setDataListener(this);
weartil.setMessageListener(this);
}

@0verride
protected void onResume() {
super.onResume();
wearUtil.connect();
}

@0verride
protected void onPause() {
super.onPause();
wearUtil.disconnect();
}

@0verride
public void onDataChanged(DataEventBuffer dataEventBuffer) {
for (DataEvent event : dataEventBuffer) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
// Dataltem changed
Dataltem tem z event.getDataItem();
if (trem.9eturi()-9@tPath().c0npf@T("/S9") == 0) {
// L a mensagem enviad pela Data API i M ()
oatmap dataMaD = DatMrIt@'-fff"afa1t'('")'gewata ap
final string uso = dfa"P-9'5""9(""'s9");
nal int count = dataMaP-9t"t("C""t")
A et asset = dataap.getAsset("f0t0");
lna 1 N '
fssl B.t ap bitmap - weartil.getBitnapFronAsset(BSSG);
run0nUiThread(new Runnable() {
@0verride
public void run() {
nTetView.setText(nsg + "\nCount: " + COUH);
img . setInageBitnap(bitnap);
}

}):
}

} else if (event.getType() == DataEvent.TYPE_DELETED) {


// Datalten deleted
}

i
@0verride
public void onHessageReceived(nal Hessagevent nessageivent) {
Log.d(TAG, "onMessageReceived(): " + messageEvent.getPath());
// L a mensagem
String path = nessageEvent.getPath(); // o "/msg"
String nodeId = messageEvent.getSourceNodeId();
byte[] bytes = messageEvent.getData();
nal int count = bytes[0]; // L os bytes
run0nUiThread(new Runnable() {
@0verride
public void run() { // Atualiza a view
mTextView.setTet("Count: " + count);
}

});
}

Como podemos ver, a classe wearutil facilita a conexo com o Google Play Services
e ainda encapsula a utilizao da Data API, Message API e Node API. Observe
que, quando o aplicativo do wear recebe a mensagem, utilizado o mtodo
run0nUiThread(runnable) para atualizar a interface, pois as mensagens so recebidas
em uma thread diferente da UI Thread.

Dica: ao lado do boto Run do Android Studio, voc pde selecionar qual mclul
ser executado, neste caso o mdulo mobile ou wear. Execute o mdulo mobile em
um smartphone e o mdulo wear no emulador d wear.

A figura 39.13 mostra o aplicativo executando no smartphone. O boto Send Message


vai enviar uma rpida mensagem, e o boto Put Data vai compartilhar um Bundle
com informaes.
Captulo 39 I Android Wear 985

.__f..,

l'i~_`ilU wirld'

SEND MESSAGE

PUT DATA

Figura 39.13 - Aplicativo mobile.

A figura 39.14 mostra o resultado no emulador do Wear. A primeira parte da gura


mostra a mensagem enviada pela Message API. O objetivo da Message API tro
car informaes de forma rpida, como se fosse um gatilho (trigger) ou um ping
que um lado da conexo pode fazer no outro. No exemplo da Message API foi
passada na primeira posio do array de bytes uin contador (int) que a activity
no mobile fica incrementando.

A segunda parte da figura inostta os dados compartilhados pela Data API. Neste
caso foi enviado o mesmo contador (int) por um Bundle, junto com uma string
para mostrar uin texto e inais uma imagein no formato Asset. Um Asset representa
algum dado binrio, como uma iinagem. O que zemos foi obter um Btnap dos
recursos do projeto e converte-lo para o tipo Asset. A vantagein de utilizar assets
e que a Data API faz cache deles com o objetivo de otimizar a conexo Bluetooth
entre os dispositivos.

Nota: no exemplo com a Data API, note que estamos trabalhando coin a classe
Bundle, nossa velha conhecida. Mas internamente o Bundle e convertido para o
objeto Dataap da API do Wear. A classe DataMap tambm funciona na estrutura
de chaves e valores.

Count. 5 Ola Data API


Count: 6

Figura 39.14 - /Iplltutivri IUULHL


Qt agua-4-ua.
39.l4IVIH\i'0lP!|iiP`i0R
No apimlo 21. sobre multimdia, aprendemos a tirar uma lioto com a mera e
0 proieto tnen. Para demonstrar tomo imagens dinamimmente
do smarttione para 0 :elogio vamos incrementar esse projeto e adicionar um
boto para enviar a tbto logo depois de utilizar a cmera.
Copie 0 projeto hifen do capitulo ll e renomeie para . ou se
ptetetir abra 0 projeto pronto que acompanha os exemplos do livro No layout
da activity: ao lado do boto de tirar foto. adicione um boto para enviar a toto
Feito isso. altere o cdigo da actity oontomie demonstrado a seguir. Note que
estou mostrando apenas as partes que precisam ser alteradas.
._.
ilnrt li.wet'|il.li.b.||ar.\h|'Itll;
Ilit class Bainktiarlty extends Basektivity {

Iivite itil nilitll;


public void onCreate(Buidle savedInstanteState) {
sner .un(reate{savedInstance.State);
settmtentviev _ layout _ activityjain) ;
ieartil = na lharUtil(this);

3'iaqI.|(l.id.btE:vi.atfotn).setihClidd.i.stener(neu Vieu.(h(lick'Listener() {
Overricb
ptblic void onElid(Vi.ev v) {
I/ Valida se o arquivo existe
if (Ele != null Il le.erlsts()) {
int u = 159; I/ Hmda a foto reduzida em 150x150 pixels
int h = 158;
BUW bit!!! = I!'El;lEsi1eltils. ` `.fruflle(Gle), uu, h, false);
/I Converte o bitnap para asset
Asset asset = uearutil.9etAssetfru6itna9(biu\ap);
il Envia o asset para o wear
! il! = 3 IIEO;
'l.e.Ithrtell.e("fv't|', sset);
IEUW-|ltht('Ifut1', bnile);
I
l
});

l
Captulo 39 I Android Wear 937
protected void onResume() {
super.onResume();
uearUtil.connect();
}

protected void onPause() {


super.onPause();
wearUti1.disconnect();

Eu adicionei a classe wearUtil que estudamos anteriormente na biblioteca


android-utils, portanto adicione esta dependncia no arquivo app/buildgradle, assim
como o Google Play Services.
compile 'com.google.android.gms:play-services:7.G.0'
compile 'br.com.livroandroid:android-utils:1.0.0'

No arquivo de manifesto declare a tag do Google Play Services.


</span></span> <span class='ocr_word' id='word_1_63' title="bbox 746 2063 902 2124"><span class='xocr_word' id='xword_1_63' title="x_wconf -2">Google</span></span> <span class='ocr_word' id='word_1_64' title="bbox 934 2063 1034 2124"><span class='xocr_word' id='xword_1_64' title="x_wconf -1">Play</span></span> <span class='ocr_word' id='word_1_65' title="bbox 1067 2066 1273 2114"><span class='xocr_word' id='xword_1_65' title="x_wconf -1">Services</span></span> <span class='ocr_word' id='word_1_66' title="bbox 1311 2082 1381 2108"><span class='xocr_word' id='xword_1_66' title="x_wconf 0">--</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_18' title="bbox 613 2157 2093 2221"><span class='ocr_word' id='word_1_67' title="bbox 613 2157 874 2206"><span class='xocr_word' id='xword_1_67' title="x_wconf -1"><meta-data</span></span> <span class='ocr_word' id='word_1_68' title="bbox 907 2157 2093 2221"><span class='xocr_word' id='xword_1_68' title="x_wconf -2">android:name="com.google.android.gms.version"</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_19' title="bbox 707 2250 2191 2315"><span class='ocr_word' id='word_1_69' title="bbox 707 2250 2107 2315"><span class='xocr_word' id='xword_1_69' title="x_wconf -3">android:value:"@integer/google_play_services_version"</span></span> <span class='ocr_word' id='word_1_70' title="bbox 2144 2255 2191 2315"><span class='xocr_word' id='xword_1_70' title="x_wconf 0">/></span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_9' title="bbox 515 2379 2962 2757">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_20' title="bbox 517 2383 2962 2463"><span class='ocr_word' id='word_1_71' title="bbox 517 2387 699 2459"><span class='xocr_word' id='xword_1_71' title="x_wconf -2">Agora</span></span> <span class='ocr_word' id='word_1_72' title="bbox 723 2404 918 2442"><span class='xocr_word' id='xword_1_72' title="x_wconf -1">vamos</span></span> <span class='ocr_word' id='word_1_73' title="bbox 942 2387 1082 2442"><span class='xocr_word' id='xword_1_73' title="x_wconf -1">criar</span></span> <span class='ocr_word' id='word_1_74' title="bbox 1103 2406 1138 2443"><span class='xocr_word' id='xword_1_74' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_75' title="bbox 1164 2383 1402 2444"><span class='xocr_word' id='xword_1_75' title="x_wconf -1">mdulo</span></span> <span class='ocr_word' id='word_1_76' title="bbox 1426 2384 1502 2444"><span class='xocr_word' id='xword_1_76' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_77' title="bbox 1524 2404 1632 2443"><span class='xocr_word' id='xword_1_77' title="x_wconf -1">wear</span></span> <span class='ocr_word' id='word_1_78' title="bbox 1654 2407 1729 2444"><span class='xocr_word' id='xword_1_78' title="x_wconf -1">no</span></span> <span class='ocr_word' id='word_1_79' title="bbox 1753 2391 1977 2463"><span class='xocr_word' id='xword_1_79' title="x_wconf -3">projeto.</span></span> <span class='ocr_word' id='word_1_80' title="bbox 2001 2390 2131 2463"><span class='xocr_word' id='xword_1_80' title="x_wconf -4">Faa</span></span> <span class='ocr_word' id='word_1_81' title="bbox 2158 2392 2270 2447"><span class='xocr_word' id='xword_1_81' title="x_wconf -1">isso</span></span> <span class='ocr_word' id='word_1_82' title="bbox 2295 2410 2422 2448"><span class='xocr_word' id='xword_1_82' title="x_wconf -1">com</span></span> <span class='ocr_word' id='word_1_83' title="bbox 2446 2410 2480 2448"><span class='xocr_word' id='xword_1_83' title="x_wconf -1">0</span></span> <span class='ocr_word' id='word_1_84' title="bbox 2503 2390 2708 2449"><span class='xocr_word' id='xword_1_84' title="x_wconf -1">wizard</span></span> <span class='ocr_word' id='word_1_85' title="bbox 2732 2394 2852 2448"><span class='xocr_word' id='xword_1_85' title="x_wconf -2">Fi|e></span></span> <span class='ocr_word' id='word_1_86' title="bbox 2869 2396 2962 2450"><span class='xocr_word' id='xword_1_86' title="x_wconf -2">New</span></span></span>
<span class='ocr_line' id='line_1_21' title="bbox 516 2483 2960 2566"><span class='ocr_word' id='word_1_87' title="bbox 516 2487 695 2553"><span class='xocr_word' id='xword_1_87' title="x_wconf -1">Module,</span></span> <span class='ocr_word' id='word_1_88' title="bbox 724 2483 1001 2544"><span class='xocr_word' id='xword_1_88' title="x_wconf -1">selecione</span></span> <span class='ocr_word' id='word_1_89' title="bbox 1032 2506 1067 2543"><span class='xocr_word' id='xword_1_89' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_90' title="bbox 1099 2484 1360 2562"><span class='xocr_word' id='xword_1_90' title="x_wconf -2">template</span></span> <span class='ocr_word' id='word_1_91' title="bbox 1392 2484 1467 2545"><span class='xocr_word' id='xword_1_91' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_92' title="bbox 1497 2490 1665 2545"><span class='xocr_word' id='xword_1_92' title="x_wconf -2">Android</span></span> <span class='ocr_word' id='word_1_93' title="bbox 1682 2492 1793 2545"><span class='xocr_word' id='xword_1_93' title="x_wconf -2">Wear</span></span> <span class='ocr_word' id='word_1_94' title="bbox 1821 2509 1850 2546"><span class='xocr_word' id='xword_1_94' title="x_wconf -2">e</span></span> <span class='ocr_word' id='word_1_95' title="bbox 1881 2486 1950 2547"><span class='xocr_word' id='xword_1_95' title="x_wconf -2">d</span></span> <span class='ocr_word' id='word_1_96' title="bbox 1981 2510 2015 2547"><span class='xocr_word' id='xword_1_96' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_97' title="bbox 2048 2510 2217 2547"><span class='xocr_word' id='xword_1_97' title="x_wconf -1">nome</span></span> <span class='ocr_word' id='word_1_98' title="bbox 2249 2488 2317 2548"><span class='xocr_word' id='xword_1_98' title="x_wconf -1">de</span></span> <span class='ocr_word' id='word_1_99' title="bbox 2347 2509 2454 2548"><span class='xocr_word' id='xword_1_99' title="x_wconf -2">wear</span></span> <span class='ocr_word' id='word_1_100' title="bbox 2483 2512 2613 2566"><span class='xocr_word' id='xword_1_100' title="x_wconf -1">para</span></span> <span class='ocr_word' id='word_1_101' title="bbox 2646 2512 2680 2550"><span class='xocr_word' id='xword_1_101' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_102' title="bbox 2713 2490 2960 2551"><span class='xocr_word' id='xword_1_102' title="x_wconf -1">mdulo.</span></span></span>
<span class='ocr_line' id='line_1_22' title="bbox 518 2586 2958 2665"><span class='ocr_word' id='word_1_103' title="bbox 518 2588 610 2643"><span class='xocr_word' id='xword_1_103' title="x_wconf -2">No</span></span> <span class='ocr_word' id='word_1_104' title="bbox 635 2590 866 2664"><span class='xocr_word' id='xword_1_104' title="x_wconf -2">arquivo</span></span> <span class='ocr_word' id='word_1_105' title="bbox 891 2589 1370 2662"><span class='xocr_word' id='xword_1_105' title="x_wconf -5">wear/buildgradle</span></span> <span class='ocr_word' id='word_1_106' title="bbox 1394 2586 1607 2646"><span class='xocr_word' id='xword_1_106' title="x_wconf -2">declare</span></span> <span class='ocr_word' id='word_1_107' title="bbox 1632 2609 1661 2646"><span class='xocr_word' id='xword_1_107' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_108' title="bbox 1685 2587 2070 2665"><span class='xocr_word' id='xword_1_108' title="x_wconf -1">dependncia</span></span> <span class='ocr_word' id='word_1_109' title="bbox 2095 2589 2196 2649"><span class='xocr_word' id='xword_1_109' title="x_wconf -1">das</span></span> <span class='ocr_word' id='word_1_110' title="bbox 2220 2589 2547 2651"><span class='xocr_word' id='xword_1_110' title="x_wconf -2">bibliotecas</span></span> <span class='ocr_word' id='word_1_111' title="bbox 2572 2591 2647 2650"><span class='xocr_word' id='xword_1_111' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_112' title="bbox 2671 2614 2813 2651"><span class='xocr_word' id='xword_1_112' title="x_wconf -1">wear</span></span> <span class='ocr_word' id='word_1_113' title="bbox 2835 2615 2863 2651"><span class='xocr_word' id='xword_1_113' title="x_wconf -1">e</span></span> <span class='ocr_word' id='word_1_114' title="bbox 2887 2592 2958 2652"><span class='xocr_word' id='xword_1_114' title="x_wconf -1">da</span></span></span>
<span class='ocr_line' id='line_1_23' title="bbox 516 2693 893 2746"><span class='ocr_word' id='word_1_115' title="bbox 516 2693 893 2746"><span class='xocr_word' id='xword_1_115' title="x_wconf -2">android-utils.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_10' title="bbox 608 2836 2220 3095">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_24' title="bbox 609 2839 1953 2901"><span class='ocr_word' id='word_1_116' title="bbox 609 2839 792 2899"><span class='xocr_word' id='xword_1_116' title="x_wconf -2">compile</span></span> <span class='ocr_word' id='word_1_117' title="bbox 831 2840 1953 2901"><span class='xocr_word' id='xword_1_117' title="x_wconf -3">'com.google.android.support:wearable:1.1.0'</span></span></span>
<span class='ocr_line' id='line_1_25' title="bbox 609 2932 2219 2995"><span class='ocr_word' id='word_1_118' title="bbox 609 2932 791 2993"><span class='xocr_word' id='xword_1_118' title="x_wconf -2">compile</span></span> <span class='ocr_word' id='word_1_119' title="bbox 830 2934 2219 2995"><span class='xocr_word' id='xword_1_119' title="x_wconf -3">'com.google.android.gms:play-services-wearable:7.0.0'</span></span></span>
<span class='ocr_line' id='line_1_26' title="bbox 608 3027 1900 3087"><span class='ocr_word' id='word_1_120' title="bbox 608 3027 791 3087"><span class='xocr_word' id='xword_1_120' title="x_wconf -2">compile</span></span> <span class='ocr_word' id='word_1_121' title="bbox 830 3028 1900 3079"><span class='xocr_word' id='xword_1_121' title="x_wconf -4">'br.com.livroandroid:android-utils:1.0.G'</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_11' title="bbox 512 3152 2958 3552">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_27' title="bbox 516 3159 2958 3240"><span class='ocr_word' id='word_1_122' title="bbox 516 3162 607 3218"><span class='xocr_word' id='xword_1_122' title="x_wconf -2">No</span></span> <span class='ocr_word' id='word_1_123' title="bbox 633 3163 864 3236"><span class='xocr_word' id='xword_1_123' title="x_wconf -1">arquivo</span></span> <span class='ocr_word' id='word_1_124' title="bbox 890 3159 959 3219"><span class='xocr_word' id='xword_1_124' title="x_wconf -2">de</span></span> <span class='ocr_word' id='word_1_125' title="bbox 986 3159 1173 3237"><span class='xocr_word' id='xword_1_125' title="x_wconf -1">layout</span></span> <span class='ocr_word' id='word_1_126' title="bbox 1199 3159 1270 3219"><span class='xocr_word' id='xword_1_126' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_127' title="bbox 1297 3164 1516 3237"><span class='xocr_word' id='xword_1_127' title="x_wconf -1">activity</span></span> <span class='ocr_word' id='word_1_128' title="bbox 1541 3159 1617 3220"><span class='xocr_word' id='xword_1_128' title="x_wconf -1">do</span></span> <span class='ocr_word' id='word_1_129' title="bbox 1645 3160 1882 3220"><span class='xocr_word' id='xword_1_129' title="x_wconf -1">mdulo</span></span> <span class='ocr_word' id='word_1_130' title="bbox 1906 3181 2029 3231"><span class='xocr_word' id='xword_1_130' title="x_wconf -2">wear,</span></span> <span class='ocr_word' id='word_1_131' title="bbox 2053 3161 2310 3221"><span class='xocr_word' id='xword_1_131' title="x_wconf -1">adicione</span></span> <span class='ocr_word' id='word_1_132' title="bbox 2337 3184 2433 3221"><span class='xocr_word' id='xword_1_132' title="x_wconf -1">um</span></span> <span class='ocr_word' id='word_1_133' title="bbox 2459 3162 2687 3240"><span class='xocr_word' id='xword_1_133' title="x_wconf -1">simples</span></span> <span class='ocr_word' id='word_1_134' title="bbox 2714 3173 2958 3234"><span class='xocr_word' id='xword_1_134' title="x_wconf -3">Imageview</span></span></span>
<span class='ocr_line' id='line_1_28' title="bbox 514 3260 2956 3340"><span class='ocr_word' id='word_1_135' title="bbox 514 3281 644 3337"><span class='xocr_word' id='xword_1_135' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_136' title="bbox 671 3260 890 3320"><span class='xocr_word' id='xword_1_136' title="x_wconf -2">receber</span></span> <span class='ocr_word' id='word_1_137' title="bbox 911 3284 939 3320"><span class='xocr_word' id='xword_1_137' title="x_wconf 0">a</span></span> <span class='ocr_word' id='word_1_138' title="bbox 965 3260 1089 3321"><span class='xocr_word' id='xword_1_138' title="x_wconf -2">foto.</span></span> <span class='ocr_word' id='word_1_139' title="bbox 1115 3266 1262 3321"><span class='xocr_word' id='xword_1_139' title="x_wconf -3">Feito</span></span> <span class='ocr_word' id='word_1_140' title="bbox 1287 3266 1411 3331"><span class='xocr_word' id='xword_1_140' title="x_wconf -1">isso,</span></span> <span class='ocr_word' id='word_1_141' title="bbox 1433 3261 1595 3321"><span class='xocr_word' id='xword_1_141' title="x_wconf -2">altere</span></span> <span class='ocr_word' id='word_1_142' title="bbox 1619 3284 1654 3321"><span class='xocr_word' id='xword_1_142' title="x_wconf -1">o</span></span> <span class='ocr_word' id='word_1_143' title="bbox 1678 3261 1878 3339"><span class='xocr_word' id='xword_1_143' title="x_wconf -1">cdigo</span></span> <span class='ocr_word' id='word_1_144' title="bbox 1902 3262 1973 3322"><span class='xocr_word' id='xword_1_144' title="x_wconf -1">da</span></span> <span class='ocr_word' id='word_1_145' title="bbox 1997 3268 2217 3340"><span class='xocr_word' id='xword_1_145' title="x_wconf -1">activity</span></span> <span class='ocr_word' id='word_1_146' title="bbox 2239 3262 2528 3323"><span class='xocr_word' id='xword_1_146' title="x_wconf -3">conforme</span></span> <span class='ocr_word' id='word_1_147' title="bbox 2552 3264 2956 3325"><span class='xocr_word' id='xword_1_147' title="x_wconf -2">demonstrado</span></span></span>
<span class='ocr_line' id='line_1_29' title="bbox 513 3361 2956 3442"><span class='ocr_word' id='word_1_148' title="bbox 513 3383 541 3420"><span class='xocr_word' id='xword_1_148' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_149' title="bbox 567 3366 760 3439"><span class='xocr_word' id='xword_1_149' title="x_wconf -1">seguir.</span></span> <span class='ocr_word' id='word_1_150' title="bbox 785 3361 1038 3422"><span class='xocr_word' id='xword_1_150' title="x_wconf -2">Observe</span></span> <span class='ocr_word' id='word_1_151' title="bbox 1063 3385 1173 3440"><span class='xocr_word' id='xword_1_151' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_152' title="bbox 1197 3381 1443 3422"><span class='xocr_word' id='xword_1_152' title="x_wconf -1">estamos</span></span> <span class='ocr_word' id='word_1_153' title="bbox 1468 3362 1780 3422"><span class='xocr_word' id='xword_1_153' title="x_wconf -2">utilizando</span></span> <span class='ocr_word' id='word_1_154' title="bbox 1803 3385 1831 3422"><span class='xocr_word' id='xword_1_154' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_155' title="bbox 1856 3363 2029 3423"><span class='xocr_word' id='xword_1_155' title="x_wconf -2">classe</span></span> <span class='ocr_word' id='word_1_156' title="bbox 2051 3372 2269 3424"><span class='xocr_word' id='xword_1_156' title="x_wconf -3">wearUtil</span></span> <span class='ocr_word' id='word_1_157' title="bbox 2294 3387 2424 3442"><span class='xocr_word' id='xword_1_157' title="x_wconf -2">para</span></span> <span class='ocr_word' id='word_1_158' title="bbox 2450 3365 2667 3425"><span class='xocr_word' id='xword_1_158' title="x_wconf -3">receber</span></span> <span class='ocr_word' id='word_1_159' title="bbox 2689 3388 2752 3425"><span class='xocr_word' id='xword_1_159' title="x_wconf -2">os</span></span> <span class='ocr_word' id='word_1_160' title="bbox 2776 3366 2956 3426"><span class='xocr_word' id='xword_1_160' title="x_wconf -1">dados</span></span></span>
<span class='ocr_line' id='line_1_30' title="bbox 513 3463 2264 3542"><span class='ocr_word' id='word_1_161' title="bbox 513 3463 996 3540"><span class='xocr_word' id='xword_1_161' title="x_wconf -2">compartilhados</span></span> <span class='ocr_word' id='word_1_162' title="bbox 1021 3463 1143 3541"><span class='xocr_word' id='xword_1_162' title="x_wconf -2">pela</span></span> <span class='ocr_word' id='word_1_163' title="bbox 1167 3468 1312 3524"><span class='xocr_word' id='xword_1_163' title="x_wconf -2">Data</span></span> <span class='ocr_word' id='word_1_164' title="bbox 1331 3469 1459 3535"><span class='xocr_word' id='xword_1_164' title="x_wconf -1">API,</span></span> <span class='ocr_word' id='word_1_165' title="bbox 1480 3486 1591 3542"><span class='xocr_word' id='xword_1_165' title="x_wconf -1">que</span></span> <span class='ocr_word' id='word_1_166' title="bbox 1615 3482 1766 3524"><span class='xocr_word' id='xword_1_166' title="x_wconf -1">neste</span></span> <span class='ocr_word' id='word_1_167' title="bbox 1789 3486 1919 3524"><span class='xocr_word' id='xword_1_167' title="x_wconf -1">caso</span></span> <span class='ocr_word' id='word_1_168' title="bbox 1943 3470 2062 3525"><span class='xocr_word' id='xword_1_168' title="x_wconf -1">ser</span></span> <span class='ocr_word' id='word_1_169' title="bbox 2086 3488 2115 3524"><span class='xocr_word' id='xword_1_169' title="x_wconf -1">a</span></span> <span class='ocr_word' id='word_1_170' title="bbox 2140 3465 2264 3525"><span class='xocr_word' id='xword_1_170' title="x_wconf -2">foto.</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_12' title="bbox 516 3646 1243 3736">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_31' title="bbox 516 3646 1243 3736"><span class='ocr_word' id='word_1_171' title="bbox 516 3646 601 3718"><span class='xocr_word' id='xword_1_171' title="x_wconf -7">i</span></span> <span class='ocr_word' id='word_1_172' title="bbox 701 3661 1243 3736"><span class='xocr_word' id='xword_1_172' title="x_wconf -2">MainWearActvity.java</span></span></span>
</p>
</div>
<div class='ocr_carea' id='block_1_13' title="bbox 510 3902 2632 4257">
<p class='ocr_par'>
<span class='ocr_line' id='line_1_32' title="bbox 511 3907 1511 3966"><span class='ocr_word' id='word_1_173' title="bbox 511 3908 665 3966"><span class='xocr_word' id='xword_1_173' title="x_wconf -3">import</span></span> <span class='ocr_word' id='word_1_174' title="bbox 698 3907 1511 3966"><span class='xocr_word' id='xword_1_174' title="x_wconf -4">livroandroid.lib.wear.NearUtil;</span></span></span>
<span class='ocr_line' id='line_1_33' title="bbox 511 4000 2631 4063"><span class='ocr_word' id='word_1_175' title="bbox 511 4000 665 4060"><span class='xocr_word' id='xword_1_175' title="x_wconf -1">public</span></span> <span class='ocr_word' id='word_1_176' title="bbox 697 4001 825 4052"><span class='xocr_word' id='xword_1_176' title="x_wconf -1">class</span></span> <span class='ocr_word' id='word_1_177' title="bbox 856 4003 1279 4062"><span class='xocr_word' id='xword_1_177' title="x_wconf -4">MainHearActivity</span></span> <span class='ocr_word' id='word_1_178' title="bbox 1309 4002 1491 4051"><span class='xocr_word' id='xword_1_178' title="x_wconf -2">extends</span></span> <span class='ocr_word' id='word_1_179' title="bbox 1520 4003 1730 4062"><span class='xocr_word' id='xword_1_179' title="x_wconf -1">Activity</span></span> <span class='ocr_word' id='word_1_180' title="bbox 1761 4001 2022 4061"><span class='xocr_word' id='xword_1_180' title="x_wconf -3">inplenents</span></span> <span class='ocr_word' id='word_1_181' title="bbox 2053 4003 2581 4063"><span class='xocr_word' id='xword_1_181' title="x_wconf -3">DataApi.DataListener</span></span> <span class='ocr_word' id='word_1_182' title="bbox 2614 4002 2631 4063"><span class='xocr_word' id='xword_1_182' title="x_wconf -2">{</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_34' title="bbox 605 4095 1286 4154"><span class='ocr_word' id='word_1_183' title="bbox 605 4096 787 4154"><span class='xocr_word' id='xword_1_183' title="x_wconf -1">private</span></span> <span class='ocr_word' id='word_1_184' title="bbox 817 4095 1025 4145"><span class='xocr_word' id='xword_1_184' title="x_wconf -4">wearutil</span></span> <span class='ocr_word' id='word_1_185' title="bbox 1056 4095 1286 4153"><span class='xocr_word' id='xword_1_185' title="x_wconf -3">weartil;</span></span></span>
</p>
<p class='ocr_par'>
<span class='ocr_line' id='line_1_35' title="bbox 605 4190 1179 4250"><span class='ocr_word' id='word_1_186' title="bbox 605 4190 786 4247"><span class='xocr_word' id='xword_1_186' title="x_wconf -2">private</span></span> <span class='ocr_word' id='word_1_187' title="bbox 820 4190 1054 4249"><span class='xocr_word' id='xword_1_187' title="x_wconf -2">Imageview</span></span> <span class='ocr_word' id='word_1_188' title="bbox 1084 4191 1179 4250"><span class='xocr_word' id='xword_1_188' title="x_wconf -1">img;</span></span></span>
</p>
</div>
</div>
</body>
</html>
988 Google Android - 4' edio

@0verride
protected void onCreate(Bund1e savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.1ayout.activity_main);
ueartil = new HearUti1(this);
wearUti1.setDataListener(this);
img = (lmageview) ndViewById(R.id.img);
}

// onResume() com wearUti1.connect();


// onPause() com wearUti1.disconnect();
@0verride
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
Dataltem item = event.getDataItem();
if (item.getUri().getPath().compareTo("/foto") == 9) {
// Recebe a foto
DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
Asset photo = dataHapItem.getDataHap().getAsset("foto");
/I Converte o asset para bitmap
nai Bitmap bitmap = wearUti1.getBitmapFromAsset(photo);
run0nUiThread(new Runnab1e() {
@0verride
public void run() {
// Mostra a foto no Imageview
img.setImageBitmap(bitmap);
}

});
}

Importante: observe que no lado do smartphone, antes de enviar a foto ao relgio,


ela foi redimensionada para diminuir seu tamanho. Lembrando tambm que
o recomendado trafegar no mximo 100lb de dados com a Data API.

A gura 39.15 mostra o resultado. Foi tirada uma foto pelo celular e enviado ao
wear utilizando a Data API.
989
(pll.IlO I Android wear

!
--

"***www

i1_Qll]~ _).l5 _ ,lll7lli'l`H() ;'f)( I,(H U uni

39.15 Criando views e layouts para wear


Lzcmlrncnrc. no \\'L`;ll` so cxilwidrs irilorrn;ic`~s P`]llL`ll.lS ~ olf\5 nr, ,,-,mU
ic czirrocs, c rsso polc scr lcrro ll`;lllL]llli1llllL`l1[L` com iirili';rco\s_ Mas gm 5r~_
l1L`L`CSSL1I'l) C piS5\'`l `I`i;1l` illI``l`l_;l``S p`l`Sn1;1liZ;ll;lSplll o \\`L`ll'. lllc pol`m \`.ll`l;ll`
lc crrtcs. lisms, grils corn rolngcrn. zilcrms crc.
Pznxr l:1L`lil[ll'SCll ;1prcnlizzilo.rccnncnlozrlwriroprojcroHeI|oWearViewsno.f\nlroil
Studio. o qual contcrn lois molulos: mobile c wear. O molnlo mobile mosrrxi .i lrsr.i
corn os cxcmplos cm urn Listvew. lzi mcsmn lormn inc outros proicros lc cxcrnplo
lo livro. /\ lilcrcncr c ]uc, crn vcx lc L`X``lll`1l`;l ;rcti\'iry no mobile, uma rncns.i;~rn
c L`ll\'llLi;l para o \\'c;n';1lwlc corn ;i l\lcss;1gc \l`l.
No rnolulo wear ;1 rncnsngcm c rccclnl;1 pclzi l\lcss;11c .f\l`l c c lisp.ir;rl.r .rl;mn;r
'l`l`l\'ll\' ou c'li;o p;1r;r lcrnonsrr;n' o cxcmplo .~\crclilo inc voc cm~nIcr.i o
1 No molulo mobile. o Lsrvew c cri.rlo corn o
proiclo lc cxcmplo scrn pr'olil'I111S
scgrrimc zirrxiy lc strings:
Stringl] items = new StF9[ll
"Notification",
dFra ment" "Cardl*rame".
"CardFra9""5`f\","CU50"lC3f 9'
GridViewPager".
uLStVewn,uvewpageru!n
"Full Screen". "Delayed (onllrmaLion".
"(onfirmation Success", "Como~m.1tioo Hf0""l
no soogizziamia-4-zm
Ao clicar em qualquer item da lista. enviada uma mensagem para o wearable.
No path (caminho) da mensagem enviado o nome do item. Por exemplo, ao
clicar no irem llotlcation, enviado o path /Notification. O array de bytes sempre
o mesmo. pois no ser usado no exemplo
public void onItenClick(AdapterView? parent, View view, int position, long id) {
String iten = parent.getAdapter().9etIten(position).toString();
/I Envia a nensagen con o texto do iten selecionado
|arUtil.send|Iessage("/" + iten, new byte[]{1});
if('VieuPager".equals(iten)) {
// Para o exemplo do ViewPager abre una activity aqui no nobile tanbn
startActivity(neu Intent(this,HelloViewPagerActivity.class));
}

No modulo Wear, a mensagem recebida e uma activity ou algum cdigo inicia


do para responder a mensagem. A seguir, temos um trecho de cdigo do mtodo
onHessageReceived(nsg) da classe HainHearActivity .

ublic void onHessageReceived(nal HessageEvent nessageEvent) {


String path = nessageEvent.getPath();
if('/Notication.equals(path)) {
Noticationtil.create(this, R.nipnap.ic_launcher, "Livro Android", 'Ol Hear");
} else if('/CardFragnent'.equals(path)) {
startActivity(new Intent(this, CardFragnentActivity.class));
l

isso! O mobile envia a mensagem para executar algum exemplo no relgio.


Nos prximos tpicos vamos estudar cada um destes exemplos Acompanhe a
explicao executando o projeto HeIIoWearVlews. Voc deve executar o modulo mobile
em algum smartphone e o mdulo Wear no emulador do Android Wear.

39.16 Criando cards (cartes)


Existem vrias formas de mostrar cards no wear. A primeira delas e a forma mais
utilizada por meio de notificaes. que automaticamente mostram uma interface
padro com os cards
Mas neste projeto de exemplo temos outros trs exemplos, que so: CardFraient.
CustonCardFragnent, CardFrane.
Captulo 39 n Android Wear 991
O primeiro desses exemplos mostra como utilizar o fragment CardFragment para
criar um card. Este exemplo apresenta uma activity com um layout de marcao
para adicionar 0 fragment do carto:

1 /wear/res/layout/activity_card_fragment.xmI

<android.support.wearabie.view.BoInsetLayout . . .>
<FraneLayout android:id="@+id/cardLayout"
android:iayout_width="match_parent" android:iayout_height="match_parent"
app:iayout_bo=bottom" /> `
</android.support.wearabie.view.BoInsetLayout>

No cdigo da activity basta adicionar o CardFragment no layout como qualquer


outro fragment. O mtodo CardFragment.create(tit1e,text,icon) utilizado para
criar o carto.

CardFragmentActivity.java

public class CardFragnentActivity extends Activity {


@0verride
protected void onCreate(Bundie savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.iayout.activity_card_fragnent);
FragmentManager fragmentanager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
CardFragnent cardFragnent = CardFragnent.create("HeI1o",
"Heu Primeiro Cardview",R.mipmap.ic_iauncher);
fragnentTransaction.add(R.id.cardLayout, cardFragment);
fragmentTransaction.commit();
}

Para testar esse exemplo, execute o projeto nos dois dispositivos e depois clique
no item Cardfragment na lista do smartphone. Feito isso, uma mensagem ser enviada
para o wear, resultando na chamada da activity CardFragmentActivity. O resultado
pode ser visto na gura 39.16.
992 Google Android - 4' edio

MDQOID

Meu Primeiro
l8HiVHHN

F@unz3QH-(ludfnuynwn.
(aso voc qucira customizar a vicw do carto, basta criar uma subclasse dc
CardFragnent c sobrcscrcvcr o mtodo onCreateContentVew( . . . ), conforme dcmonstrado
a seguir. Dcssc modo, voc pode rctornar o layout que quiser:

fu (ustom(ardFragment.java
public class CustonCardFragnent extends CardFragnent {
@0verrlde
protected View onCreateContentView(Layoutlnater nater, Vewroup container,
Bundle savedInstanceState) {
view view = inater.lnate(R.layout.fragnent_custon_card, container, false);
Textvew tTitle = (Textvew) vew.ndVlewById(R.d.tTtle);
tTitle.setTet(getArgunents().getStrng("ttle"));
Textvew tHsg = (Textview) view.ndVewById(R.id.tMsg);
tHsg.setTet(getArguments().getStrng("msg"));
return view;
}

Q* /wear/res/layout/fragment_custom_card.mI
LnearLayout android:orentaton="vertcal" . . .>
<TetVew androd:id="@+id/tTtle" androd:tetColor="#0G0000"
androld:layout_wdth="wrap_content" androld:layout_helght="wrap_content" /
<TextView android:id="@+d/tMsg" android:tetColor="#00G000"
androd:layout_wdth="wrap_content" androd:layout_heght="wrap_content" />
</LlnearLayout

Logo depois dc criar o tiragnicut customizado, adicioiic-o na activity:


.Y
aptulo 39 n Android Wear

(ustomCardFra
gmentActivity.java

Dublc class CustonCardFragnentActvty extends Activt {


prtvate Textvew nTetVew;
@0verrde

protected void onCreate(Bundle savedInstanceState) {


993

super.onCreate(savedInstanceState);
setContentVew(R . layout . actvity_card_fragment);
FragmentManager fragmentManager = getFragmentManager();
FragnentTransacton fragmenransacton =`fragmentlanager.begnTransacton();
CustonCardFragnent card = new CustonCardFragnent();
Bundle args = new Bundle();
args.putString("title","CardFragnent");
args.putString("msg","Card custonzado.");
card.setArgunents(args);
fragmenransacton.add(R.d.cardLayout, card);
fragmenransacton . conmt( );
}

A gura 39.17 mostra o resultado. Este o item (ustom(ardFragment do projeto de


exemplo Veja que agora voc responsvel por customizar o layout, e neste caso
deixei apenas um ttulo com uma mensagem, Sm n@nhum1m21g<=m <iUH1@1Sf@
no CardFragnent nativo.

CardFragment ,
Card customizado.

_ ~ _ adg ue. cpor


iomLda
Figura 39.17 _ cafdrmgmff f""Zd'
- eira de criar um cartao customiz ,IQ
Existe ainda outra man . ` -d dn[-Q
rlnscrl 8 de um CardScrollV1ew.
vie w CardFrame, QU Pde se
/wear/res/layout/activity_card_rame.xm|
ndrotd.sQport.uarabIe.viu.IoxIusetLayout . .
Qndmtd .support . uearable . view . C|rdScroI1Vieu
android:id="@+id/card_scrol1_vi.eu' app:layout_box="botton'
android:layout_height=natch__parent' android:1ayout_width="natch_parent' >
androiI.support.|earabIe.vieu.CardFrane
android : layout_height="urap_content " and roid : 1ayout_width='natch _parent'
<LinearLay0ut
android:1ayout_height='wrap_content" and roid : 1ayout_uidth='natch _parent'
androidzorientatonvertcal' android:paddingLeft='Sdp'
<TextVieu android:fontFan1y='sans-serif-light'
androd:layout_height=`wrap_content' android:1ayout_width="natch_parent`
android:text='Titu1o do Card' android:textColor='Qcolor/black'
androd:textSize='26sp"/>
<TetVev androd:fontFani1y='sans-serif-light'
android :1ayout_hei.ght="urap_content' android : layout_uidth='natch_parent'
android:text='Teto do Card aqui\nCon vrias linhas'
android:textCo1or='@co1or/black' android:textSize='14sp'/

landroi.d.support.vearable.vi.eu.CardFrane
/android . sipport .uearable . view . CardScro1lVieu
/android.support.vearab1e.vi.eu.BoxInsetl.ayout

A Figura 3918 mostra o resultado deste exemplo Este o item Cirdfrime do projeto
de exemplo

Titulo do
Card
Texto do Card aqui
Com ~za'as tinhas

Figura 39.18 - CardFrame.

Em minha opinio sempre que possivel a soluo mais simples e utilizar a classe
CardFragnent que j esta pronta. O codigo mais simples e ele cria facilmente 0
visual padro dos cartes alm de ja utilizar uma imagem de icone, dando uma
boa aparncia e acabamento ao carto Crie um carto customizado realmente
Capitulo 39 I Android Wear 995
. _, isso
se voc precisar customizar a view, pois conforme vimos exige` um pouco
mais de codigo.

39.17 Criando listas

A classe NearableListView utilizada para criar listas no wear e seu funcionamento


similar ao Listview que j conhecemos. Criar listas no wear a parte fcil, o mais
importante entender
que voce deve c o ^ nao
o que = `voce^ deve fazer com listas,
pois visualizar muitas informaes no relgio naturalmente nao o ideal.
A gura 39.19da documentao
retirada eocial
.1m principio
demonstri u bsico
sobre listas: mostre poucas informaes e com icones grandes, ao contrrio de
muitas informaes com icones pequenos.

. lmnulfs
t l*'i=f\*l*" . montes
.3
2mnutes 8 :mm
. o -z
;i:iz:i=,iis . mm
. B mnutcs

Figura 39.19 - Listas.

O exemplo que vou demonstrar vai exibir a lista de dez carros no relgio. Por ques
tes de usabilidade, isso talvez no seja pratico, mas o objetivo aqui e aprendcrmos
a criar a lista. Para continuar,crie~ a~l
c asse Carro, conforme demonstrado a seguir:

fi (arrojava
public class Carro inplenents Serializable {
public String none;
public int ing;
public Carro(String none, int img) {
this.none = none;
this.ing = img;
}

public static List<Carro getListEsportivoS() l


List<(arro list = new ArrayList<Carro();
list.add(new Carro("Ferrari FF", R drawable.ferrari_ff));
996 Google Android - 4' edio

list add(new Carro("AU I GT Spyder", R.drawable.audi_spyder));


list. add(new Carro("Porsche Panamera', R.drawable.porsche_pananera));
list add(new Carro("Lanborghini Aventador", R.drawable.lanborghini_aventador));
list add(new Carro("Chevrolet Corvette Z96", R.drawable.chevrolet_corvette_z06));
list add(new Carro("BHH HS", R.drawable.bnw));
list add(new Carro("Renault Megane", R.drawable.renault_negane_trophy));
list .add(new Carro("Haserati Grancabrio Sport", R.drawable.maserati_grancabrio_sport));
list add(new Carro("HcLAREN HP4-12C", R.drawable.mclaren));
list add(new Carro("MERCEDES-BENZ C63 AMG", R.drawable.nercedes_bens));
return list;
}

Esta classe contm o mtodo Carro.getListEsportivos(), que retorna uma lista de


carros esportivos, na qual as imagens esto xas como recursos do projeto.

/wear/res/layout/activity_heIIo_Iistview.xmI
<android.support.wearable.view.BoxInsetLayout . . >
<android.support.wearable.view.HearableListView android:id="@+id/listView"
android:layout_height="match_parent android:layout_width="match_parent" />

No cdigo da activity utilizar o wearableListView simples e basta informar o adapter,


conceito que j estamos bem habituados.

HeIIoList\ewActivity.java

public class HelloListViewActivity extends Activity


inplenents HearableListView.ClickListener {
private List<Carro carros;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_listview);
HearableListVieu listview = (HearableListView) ndViewById(R.id.listView);
carros = Carro.getListEsportivos();
listVieu.setAdapter(neu CarroAdapter(this, carros));
listview.setClickListener(this);
}

@0verride
public void onClick(HearableListView.Viewolder v) {
Integer position = (Integer) v.itemView.getTag();
Carro c = carros.get(position);
}I
Captulo 39 n Android Wear

Toast.nakeTet(th5,"(rro.
= Q I . _ HORl)_ .. + C
h nom
. T 997
Intent ntent ii ` - e, oast.LEN;rH 5
nt Nent.DutEtra( carro ,) , S mf),
W @"(thts,CarroActvty.c1ass)~
startACtVY(ntent)

@0verride

O c ` _
} public void onTopEnptyR@9n1ick() {}

adapter.

.7 O`._
d180 dO adapter nao vou mg strar
O resultado deste exem 10 d .
ue I _ P P0 e ser visto na um 397 _
C1 , ao se ecionar um carro estamos nave
8 paradoutra
E z __0_
no livro '
, pois c como qualquer outro

Inclusive
activity portantoveja
os
conceitos ue voc ` '
wear da mesma
7 Q Ja conhece forma ml
sobre desenvolvimento And 53
'd ~*
_' aplicados no

Q Ferrari FF

AUDIGT
Spyder

Porsche
Panamera

Figura 39.20 - Lista de carros com tela de detalhes.

39.18 Criando pginas (ViewPager)


O funcionamento do componente VewPager exatamente idntico no mobile e
wear. Sendo assim, no vou mostrar como criar o VewPager. pois isso ja sabemos. O
C

ar e um exemplo classico e ga er
que vamos mostr ' ` " ' * d * I' l* ii de fotos, a qual e visualizada
e sincronizada entre O smartphone e relgio.
A figura 39.19 mostra o exemplo. A mesma lista de dez carros toi criada para
. - - , . ' ' ' Har, ~, a nave tar
preencher o ViewPager, portanto voc pode navegar entre os carros lateralmente.
O VtewPager foi criado tantozno -mafaplicativo
.,tainbem
. ' z 1;sera
)\\'L`11I`,C\'iLL`-\'L`l'S;l.
mobile quanto
atualizac int no ue c tg zu
numa pgina do mobile, esta pag

C21
Isso e possvel utilizando a Message API, pois basta monitorar o evento de troca
d l Pgin 1 do ViewPager para enviar uma mensagem para o outro dispositivo tutor
mando a pgina que ele deve mostrar.
998 wgiz Andina - 4- zio

vie/Pager . set0nPage(hangeListener( new ViewPager . 0nPageChangeListener() {


@0verride
public void onPageSelected(int position) { // trocou de pgina
wearUtil.sendHessage("/page", new byte[]{(byte) position});
}...
});

Do outro lado da conexo. ao receber a mensagem, o ViewPager posicionado na


pgina desejada.
public void onMessageReceived(MessageEvent messageEvent) {
if("/page".equals(nessageEvent.getPath())) {
nal int position = messageEvent.getData()[6];
run0nUiThread(new Runnable() { public void run() {
viewPager.setCurrentItem(position);
}});
}

Pronto, com este simples truque, temos uma galeria de fotos sincronizada entre
o mobile e wear (Figura 3921). Na Figura o efeito da rolagem lateral no ca muito
claro. portanto execute o projeto no emulador do wear e em algum smartphone.

Figura <)..2l - Galeria dcfts com ViewPagfr;


Captulo 39 z Andmd WW

UmaCriando
39.19 va ` pginas
" - . em grid (GridViewPager) 999
' riaaoum
criar do V1ewPager
grid com muito utilizada
linh no wear 0 5,-dv. utilizado ara
1 WPHQHF,
Esse ad f as~e colunas,
, padrao conhecido como 20 Picker P

. `
P' mo de deslgn, tambem referenciado d
Stream* dene que os cards devem Cl- , na Ocumento Oclal P0f(iEXl
Seja necessrio O usurio pode fazer
ser aalicionados
ro agem aralistaa vertical
em uma d' ` ~e -caso.
detalhes
_ _ do
_30cart" .A demonstra
p . ,lrlta
-guraprincipio.
3922 para vlsuahzar
Note
esse ue a nmm ~

e'f'f '9;
deve ir no sentido para baixo e direita. q avegaao

:J V*
19.1

*ri
,ix
_.z

Figura 39.22 - Padro 2D Picken

No exatamente isso que faremos no prximo exemplo, mas nosso objetivo


apenas didtico para mostrar como criar esse grid com cards. No exemplo que
vamos criar, teremos um grid de dez linhas com trs colunas com os carros (cls
sicos, esportivos e luxos). Cada linha ter um card com informaes do carro.
Ento, vamos l! No layout da activity basta adicionar o GrdVewPager.

/wear/res/layout/activity_heIIo_gridviewpager.xmI
<androd.support.wearable.vew.BoInsetLayout . . >
<and roid . support . wearable . view . Gridviewliager androd : id="@+d/vewPB9@f "
and roid : i.ayout_wdth=" match_pa rent " android :1ayout_heght="match_parent"
android : keepScreen0n=" true" />

Dica:i neste
._ . O _ ,
</android . support.wearable.vew.BoxInsetLayout>

kw W 'b t kee Screen0n-true que faz a tela do relogio nao


layout utilizeioatri u. ' P , _ d .nados CaSOS_
apagar enquanto a view7A
estiver
id visivel.
_ ___ Isso pode ser util em etermi M

ustomizado para preencher o GrdvewPager.


No cdigo vamos utilizar um adapter C
O adapter vai receber as trs listas de CGFYO5
1000 Google Android - 4 edio
F! HelloGrdViewPagerAttivity.java

public class HelloGridViewPagerActivity extends Activity {


protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
set(ontentView(R.layout.activity_hello_gridviewpager);
List<Carro classicos = Carro.getListClassicos(); // dez carros clssicos
List<Carro esportivos = Carro.getListEsportivos(); // dez carros esportivos
List<Carro luxo = Carro.getListLuxo(); // dez carros luxo
GridVieuPager grid = (GridViewPager) ndViewById(R.id.viewPager);
grid.setAdapter(new CarrosGridPagerAdapter(this, getFragnentManager(), classicos,
esportivos, luxo));
}

O adapter deve herdar de FragmentGridPagerAdapter e implementar os mtodos


getRowCount(),getColunnCount(row) e getFragment(row,col).

1 (arrosGridPagerAdapter.java

public class CarrosGridPagerAdapter extends FragnentGridPagerAdapter [


private nal Layoutlnater mlnater;
private Context context;
private List<Carro classicos;
private List<Carro esportivos;
private List<Carro luxo;
public CarrosGridPagerAdapter(Context context,FragmentManager fm, List<Carro classicos,
List<Carro esportivos,List luxo) {
super(fn);
// inicializa os atributos...
}

public Fragment getFragment(int row, int col) {


Carro c = classicos.get(row);
if(col == 1) { c = esportivos.get(row); }
else if(col == 2) { c = 1uxo.get(row); }
CarroCardFragnent card = nen CarroCardFragnent();
Bundle args = new Bundle();
args.putString("none",c.none);
args.putInt(ing",c.ing);
card.setArgunents(args);
return card;
}
ubl` ' 1001
Captulo 39 n Android Wear

' z SO
IO
D lc lt QHROWCOUHO { return classicos.size(); }

} DUblC int getColunnCount(int row) { return 3' }

1- . _ A e e colunas para cada


O mtodo getColunnCou t(F0w) deve retornar a quantidad cl

inia. Neste caso deixeideve


0 9@'CRWC0U()

o ar F " ' ~
xasretornar
as tres colunas (clssicos,
a quantidade esportivos
de linhas e luxo)
do grid que negtgOCa
m

Sao (362 Carms' O metodo 9@tFf39P\(f0w,col) o mais importante e deve retornar


ul? agmet
ragnentque sera com
padrao inserido
esse em cadacaso
codigo, posio
fosse do grid. Poderamos
ne55 O. ter usado

CardFragment card = CardFragnent.create("Carro", c.nome, c.ing);

Porem, as imagens dos carros que coloquei no projeto so muito grandes, ento
criei um fragment especco para o carto dos carros. No layout desse carto deixei
apenas o nome e a foto do carro. O Inageview que vai mostrar a foto est configu
rado com o atributo scaleType="tCenter" para redimensionar a foto e centraliz-la
no espao disponvel do carto.

/wear/res/layout/fragment_carro_card.xm|
<LinearLayout android:orientation="vertical" . . >
<Tetvew andr0idzid="@+id/tNome" android:gravity="center"
android:layout_width="match_parent" android:layout_height="wrap_content" />
<mageVew ndr0d;d="@+id/img" android:scaleType="tCenter"
android:layout_width="natch_parent" android:layout_height="match_parent" />

Na classe CarroCardFragment criado o carto customizado com esse layout.

(arroCardFragment.java
public class CarroCardFragnent extends CardFra9\@l l
@0verride
ateContentView(Ly0Uatr "ater' Vewcroup container,
protected View onCre
Bundle savedInstanceState) { t fra ment carro_card, container, falS@);
' ' = inater.inate(R-GYOU ~ 9 _ _
VIEW new view.ndViewById(R - ld - tN'e) '
. . _'view)
z -vtew.ndViewByI
ara id img);
Textvew tNre : (TGXVEW)
N e serrer(9@t^r9Un@S<>-9t5""9(""e l);
-
Imagevtew U'\9 - (Image
in9-setIna9eResource(9@t^f9"'"t5(l'getlm mg W
return view;
}

}
1002 Google Android - 4' edto

( ) t1*\1||I;|11 tlcwtt' 1 \t'IIlt\|1|)t)t1t^\Ll \1\l11lI;1 I1,lI|.I 1..H.|11 |N*- W Ui * W


| WI1~111l11~1111|11|111~111
I tt'~. 1'|11111- ._ 11111 tutti 1111 kit/ |1|1|1.1 1' trs. 1|1111;1.
|11 t.11111 I.11`.1 .1 1I.111'111 |1.II'.l 1-1111 111111 IIIIL 111 llllt 11t

luclwt 1948 ll Bugatti Vvymn

!'1_1u1 Wii (.111\/1'11*I't_g'1 1111r1'_s.

L'II.l\lt'Il|l.t ;11c11g~11,1111 1~111l111"11'sc1'xc111|I1111l111|111111t1.11l11111111.11


111 ui Ill) |.11l1'.112DPd1er,11[111'st;11111cg11i111|cx;1t;1111c111u 11111 |1'1111`i|1 1|11|1t;111t1*
lt 1|1^11_z111|1';1|1|1u.1t1\fs|1;1t';111\\'c;1|,c|1;1t11;11|n1|c(0ntBXlSttEm(|>1'1'tttt'p1t`1't1^t1'|'|111
111 1 wttm' 1111/\|11|t'111 l)*si1;t1). S1'1;1|t11 vst' |)l'tl1L`|)i1, os L`I|.(]5 1|cv1'|11>1'11111|11111;11|w
1 111 11111;1 |1t;1 \'t'I|lL`l| 1~1';1-1 \t'|;I 111t1s11'11 lllllllt) pudt Iuzur il 1'|;*11;1'111 p;11;1 l
-1
1l11v1t.1 pz11*.1 \'llI.I|l/.II 111;11.1i1~t;1||1cs1i11"11't `l( 1. /\1g111.1 39,24 1|c11111st1';1 mtu 11111111
,.
|11_ f\I;1,;11;11';1 111 111 1 1 1|1'\f1'111-.c1;t1|| I I/L'I`ISSt).SLl 1.1 lllll |1|1111'1"1`11'11,11|11c11111119

zk

fit ) lt tf'
4111, 1_;

L..u~1i1-1

't1'l1 W 'I ( .1- '11 W111

A1...'1
Nota: t .IHi`- llllll HH |llI.tttII.1|t |I||\1{tlI_||| |||1Hl1lIl (I\|',[|'|| Il |/t'|lltI l|||l
tttltlttltltt lt*.|HIl |\1 t' lt'ttIl .1|tI1 |.|I t 1 ltll ttll ttttt |tI111ltI)tIt|| Itllll \

ItIt.11'_11||1 11' It1111|\ lI||||/K 11 .ttl t|\ttt1 1111111ttl;b.1B-q111tI1t1 1l1' 1|1|_||1|1|1~| |_|y.||
. , ioos
Calltulo 39 n Android Wear

39.20 Aplicativos em tela cheia (Full-Screen)


POT Padro os aplicativos ar a
* cartao
wear ' '
ou - - , _
S- -- .Ag 0. a ro ar `
Thene.DeviceDefault.Li9ht 6 Pari remover uLf:hZal;n Os femas Themeviceoefault ou
fazer O gesto de sWiPe da eSC1uerda d' ' echal um aphcamfo e memso
Par 21 a lI`l[3, conhecido como Svnpeto di;m;5
e por algum motivo voce precisa desabilitar esse est d
activity em tela cheia,basta criar um tema customizado alfrib ` 5 adelxar uma
wndowSwpeToDsmss para false. , um O a propriedade
<style name="FullScreenTheme" pare|it="androd:Theme.DeviceDefault.Lght">
<tem name="android:wlndowSwpeToDsmss"false</tem

No projeto de exemplo do livro, criei a classe FullScreenActvty e congurei esta


activity para utilizar este tema customizado.

<actvty androd:name=".FullScreenActivty" androd:theme:"@style/FullScreenTheme" />

Como essa activity desabilitou o gesto padro para fechar o aplicativo, para sair do
aplicativo preciso utilizar a view Dlsmiss0verlayVew, que permite ao usurio tocar
na tela e segurar por dois segundos para fech-la. Ao fazer isso, ser exibido um
alerta de conrmao para 0 usurio. Segundo as boas prticas de interface para
o wear, caso voc utilize este tipo de recurso para fechar a tela, recomendado
informar ao usurio no incio do aplicativo que para fechar a tela necessrio
tocar e segurar por dois segundos.
Ento vamos ao cdigo. No layout da activity basta adicionar um Dsmssoverlayview
por cima de tudo. Lembrando que o layout BoInsetLayout uma subclasse de

V' _ ,, II
FrameLayout, portanto ele vai empilhar as views.

/wear/res/Iayout/activity_full_screen.xmI
<androd.support.wearable.vew.Boxlnsetlayout . . >

an ro : _ '
<TeXatnd1::d'lay0ut width="wFP Cte"t" andrmdzlayout-helght: wrapfontent

`'t
d id layout 9ravi.ty'"center" androd:tet="Tela em Full Screen" />
androd . support.wearable.view.Disnss0verlayView
areia-ia-"@+d/d Vf1aY"
ghdroidilavout wdth="match_parent" androd:lay0U'_h@9h="Watch-par"t />

- ~ ' ' iar um>etec


</androd.Support.wearable.v1ew.BoInsetLaYU d t r deogcsto
. _ . - V' ew na activity e preciso cr _ 4 ~6
Para utilizar 0 Dtsmss0verlay 1 f 6 visualizado 8 Segum
sobrescrever o mtodo onTouchEvent(ev), con orm
1004 Google Android - 4-' edio
CarroCardFragment.java
public class FullScreenActivity extends Activity {
private GestureDetector mDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_full_screen);
nal DisnissOverlayView d = (DisnissOverlayView) ndViewBy1d(R-id-d5WSS_0V@FlY)J
d setIntroText("Para sair, clique e segure.");
// Mostra o texto de introduo na primeira vez
d.showIntroIfNecessary();
// Congura o detector de gestos
mDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent ev) {
d.show(); // Mostra o alerta para sair
}

});
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
return nDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}

O resultado deste exemplo pode ser visto na gura 39.25. Na primeira parte da

'* 1i
gura, podemos ver o alerta inicial que exibido para o usurio ao abrir o apli
cativo pela primeira vez. Logo depois, podemos ver que a aplicao mostrou o
layout da activity normalmente. A terceira parte da gura mostra o alerta que
exibido quando o usurio toca na tela e segura por dois segundos, permitindo
assim que ele feche o aplicativo. Esse padro conhecido como Long press to dismiss_

TdaemFuHScmen

Figura 39.25 - 'Rque longo para sair


(aPtuIo 39 n Android Wear
1005

_ _utilizar
g doosmodo
utilizados
de immo e elo seim'l'"`' *M
_
NW d@Pdd0
como
POr
para
padrao
._ gfld
paraQu
em troca, desair
- . , o da tela.
um
um mapa
fechar
' _ p Uesiiau
O gesto
o aplicativ
P pororesabilitaresse
av 1CaV
d de swtpe to dsnss utilizado
~
O po e interferir no funcionament
Justamente POI' 1550, alguns aplicativos o tam d
` d ~ - - gestopadrao*
___mm P melo o padrao Long press to dtsmss.

39.21 Animao de conrmao

No wear, um padro importante so os alertas de conrmao com um tempo


de espera (delay). Durante determinado intervalo de tempo, o aplicativo mostra
um alerta com uma animao, sendo que o usurio pode intervir cancelando a
execuo, ou deixar que o aplicativo execute a ao depois do tempo estipulado.
A gura 39.26 mostra esse alerta com a animao. Ao redor da imagem um cr
culo ca sendo animado at preencher toda a rea (execute no emulador para
conferir a animao). Na parte direita da imagem, podemos ver uma notificao
que criei no exemplo para informar que o tempo da animao terminou e a ao
pode ser executada.

Conrmando...

Timer
onT|merF|nished!

Figura 39.26 - Anima0 de C0"fm0'


~ ' ' r uma
Para
' sse tipoe de animao antes de executar uma aao, basta adiciona
criar

.. - _ I
View dg tipo De1ayedConrmatonVew no l3Y0Ut'

0 t _ H II
fg /wear/res/layout/act|v|tY_"f"mat'"-delayed Xm
. 1 . `ew.BoInsetLaYUt ' ' > H
<andro1d.support.we.arab e vt . _" horizontal
androd:1aYUt-I
dextvew ' ' cyontent" android:1aYout_h9h'f= wrap-<"t@t
andrmdowydotuhtjlgvirad/ti.

androd:tet="Conrmando..." />
1906 Google Android - 4* edio
android.support.wearable.view.DelayedConrnationView
android:id:"@+id/delayed_conrnation"
android:layout_width="wrap_content" android:layout_height="wfP_Cte"t"
android:layout_gravity="center" android:src="@drawable/C-1a"Chr"
app:circle_color="@color/blue" app:circle_border_color="@color/Ted"
app:circle_radius="@dinen/circle_radius"
app:circle_radius_pressed="@dimen/circle_radius_pressed"
app:circle_padding="@dimen/circle_padding"
app:circle_border_width="@dimen/circle_border_normal_width" />
</android_support.wearable.view.BoInsetLayout>

No cdigo o mtodo setTotalTimeMs(long) recebe o tempo em milissegundos que a


animao deve durar e o mtodo start() inicia a animao. Para receber o resultado
da animao (se o usurio interrompeu ou no a execuo), deve-se implementar
a interface DelayedConrnationListener. O mtodo onTinerSelected(view) chamado se o
usurio tocar na view da animao, indicando que a ao deve ser interrompida.
j o mtodo onTinerFinished(view) chamado se o usurio no zer nada, indicando
que a ao deve executar normalmente.

(onrmationDeIayedActivity.java
public class ConrmationDelayedActivity extends Activity
inplenents DelayedConrnationView.DelayedConrnationListener {
@0verride
protected void onCreate(Bundle savedInstanceState) {
super_onCreate(savedInstanceState);
setContentView(R.layout.activity_conrmation_delayed);
DelayedConrmationView d = (DelayedConrmationView) ndViewById(R.id.delayed_conrnation);
d.setTotalTineHs(5 * 1066); // cinco segundos
d.start();
d.setListener(this);
}

@0verride
public void onTinerFinished(View view) { // usurio no fez nada
Noticationtil.create(this,R.mipmap.ic_launcher,"Timer","onTimerFinished!");
nish();
}

@0verride

public void onTinerSelected(View view) { // usurio tocou na animao


Noticationtil.create(this,R.nipmap.ic_launcher,"Timer","onTinerSelected!");
nish();
}

}
(apltulo 39 I Android Wear 100 7

39.22 Alertas de sucesso e erro

No wear, tambm muito comum ver alertas com mensagens de sucesso e erro
quando o usurio executa
a asdetermin d' -esse
aoes. Para criar ` ` tipo
' de alerta.
deve-se adicionar a activity nativa ConrnationActivity no arquivo de manifesto:
<application

<activity
android:nane=" _ . .activity
android.support.wearable ionConrnat`
ctivity"
A` />
</application
No cdi go para criar as mensagens, basta
parardisuma intent com essa
activity O parmetro EXTRAJESSAGE recebe o texto da mensagem e o parmetro
EXTRA_ANIMATION_TYPE corresponde ao tipo do alerta, que pode ser mensagem de
sucesso SUCCESS_ANIMATION, mensagem de erro FAILURE_ANIltATION ou uma mensagem
para indicar que alguma activity foi aberta no smartphone 0PEN_0N_PHONE_ANIMATION.
Na classe llainwea rActivity do projeto Hellowearlews, demonstrei como criar as ani
maes de sucesso e erro, conforme mostra a gura 39.27. A seguir podemos ver
o cdigo que cria essas mensagens.

St'
public void onMessageReceived(nal MessageEvent nessageEvent) {
ring nsg - messageEvent.getPath();

else if("/Conrnation Success".equals(nsg)) {


Intent intent = new Intent(this, ConrnationActivity.class);
intent.putExtra(ConrnationActivity.EXTRA_AN1MATION_TYPE,
ConrnationActivity.SUCCESS_ANIHATION);
intent.putExtra(ConrnationActivity.EXTRA_HESSAGE, "Hensagen de sucesso!");
startActivity(intent);
} else if("/Conrnation Error".equals(nsg)) {
Intent intent = new Intent(this, ConrnationActivity.class);
intent.putExtra(ConrnationActivity.EXTRA_ANIMATION_IYPE,
ConrnationActivity.FAILURE_ANIHATION);
intent.putEtra(ConrmationActivity.EXTRA_MESSAGE, Mensagem de errol);
startActivity(intent);
l
l
1003 Google Android - 4= edio

Figura 39.27 - Alerta de sucesso e erro.

39.23 Interceptando eventos em background


Se voc reparou, os exemplos que zemos com a Node API, Message API e Data
API, todos utilizaram a classe wearUt1 para se conectar ao Google Play Services
e ativar os listeners de cada API. Isso foi feito dentro de uma activity assim e
obrigatrio que o aplicativo do relgio esteja aberto para receber as mensagens.
Entretanto, isso muitas vezes no o desejado no caso de aplicativos para relgios,
tendo em vista que na maioria das vezes eles cam fechados. Sendo assim, temos
de ter uma maneira de receber essas mensagens em background, pois o relgio
deve acordar e mostrar uma noticao relevante ao usurio em momentos chaves
do dia. Esse um princpio de interface e design importante do wear.
Felizmente, isso simples de fazer. Na API do Wear, existe um service especial
chamado wearab1eLstenerServce, que j implementa as interfaces dos listeners da
Node API, Message API e Data API. A classe wearab1eLstenerServce lha direta da
classe Service, a qual j estudamos, portanto ela ca executando em segundo plano.
O segredo criar uma subclasse dessa classe e cadastr-la no arquivo de manifesto
da seguinte forma:

AndroidManifest.mI
<servce androd:name=".He11oLstenerServce"
<ntent-Iter>
<acton androd:name="com.goog1e.androd.gms.wearab1e.BIND_LISTENER" />
</ntent-1ter>
</servce>
(|)\.|l0 39 Andmid wear

A seguir. podemos ver o cdi 1009


o dest ~
ti H II ' .
e oL|stenerServ|ce.java
El a subclasse de wearableListenerService`

@0Ve|-ride rvice {
Public class Helloliste
nerService extends HearableListenerSe '

super.o H '
public void onMessageReceived(MessageEvent nessageEvent) {
} n essageReceived(nessageEvent); // Message API
@0verride

DUb1C void D6tCh9ed(DataEventBuffer dataEvents) {

} super.onDataChanged(dataEvents); // Data Ap;


@0verride

public void onPeerConnected(Node peer) [


super.onPeerConnected(peer); // Node Ap1
}

@Override
public void onPeerDisconnected(Node peer) {
super.onPeerDisconnected(peer); // Node API
}

Pronto! O simples fato de criar esse service e registra-lo no arquivo de manifesto


j suciente para receber mensagens do aplicativo que est no smartphone. As
mensagens sero recebidas mesmo com o aplicativo do relgio fechado. Se quiser
testar a teoria, veja 0 exemplo wearableListenerService.

39.24 Localizao e sensores


como acelermetro, contador de passos,
Os dispositivos com wear tm sensores
batimentos cardacos e GPS. Porm, no garantido que cada dispositivo tenha
todos os sensores por isso importante checar para ver se determinado sensor
7

existe antes de utiliza-lo.


360 no tem o sensor de GPS enquanto o Samsung
Por exemplo, 0 primeiro Moto _ * I, _O tem O
' ' ` de ser utilizado para detectar se o re ogl
Gear tem. O seguinte codigo po
sensor do GPS:
ION GPS);

boo ean9etPacka9eMana9er()hS5Y5t@'F@at"(PackagenanagerFEATURE'L0CAT
l h GPS as = `
1010 Google Android - 4 edio
Caso o retorno seja verdadeiro, podemos utilizar a Location API do Google Play
Services, conforme j estudamos, pois o funcionamento idntico ao do smartphone.
Os restantes dos sensores tambm funcionam da mesma forma, sendo assim
podemos utilizar a classe SensorManager, conforme tambm j estudamos.
Sobre este assunto, vale ressaltar que segundo as boas prticas do Google recomen
da-se utilizar o sensor do smartphone sempre que possvel, caso os dispositivos
estejam pareados. Veja mais detalhes a respeito disso na apresentao Wear&Sensors
(Android Performance Patterns) disponvel no canal do YouTube do Google Developers.
Segundo o Google, a justicativa para preferir a utilizao dos sensores do
smartphone para economizar a bateria do relgio, at porque o smartphone tem
uma bateria melhor e um processamento muito maior. Enviar mensagens com
os dispositivos pareados pode custar menos do que ativar os sensores no relgio.
Por exemplo, se voc precisa do sensor de contador de passos possvel utilizar o
sensor do smartphone e enviar o valor para o relgio por meio da Message API.
Porm, caso os dispositivos no estejam pareados, justica-se utilizar diretamente
o sensor do relgio. o caso de um aplicativo de corrida, no qual o relgio pode
ser utilizado para acompanhar a quantidade de passos, batimentos cardacos e
coordenadas GPS sem a necessidade de estar pareado com o smartphone.

39.25 Links teis

Neste captulo estudamos o desenvolvimento para o Android Wear. O assunto


novo, empolgante e ainda pouco explorado. Como tudo que novidade, o mundo
dos wearables ainda vai crescer muito, portanto esteja preparado. Seguem alguns
links para continuar seus estudos.
Android.com - Android Wear

http://www.android.com/wear/
Android Developers - Android Wear

https://developer android.com/wear
Android Design - Android Wear

https://developer android.com/design/wear
\"*
cAPruLo 40

Google Play
*-1

Neste CHPIIUIO, vamos vericar os passos necessrios


para publ'
icar uma aplicao
no Google Play

1 io a eitura omo ' ` - - , .


Se voc che ou at a ui '
in,C_ d I _g f Cl Ci Pfabens- Epero que tenha gostado do livro. Desde o
mas isso S I , s o asico ao avanado e vimos muitos exemplos praticos,
I . 0 o comeo, e espero que voce nao pare por aqui. O prximo passo
e continuar seus estudos, principalmente lendo a documentao ocial e os
guidelines de interface, para criar aplicativos que encantem os usurios.

40.1 Controle da verso de sua aplicao


Antes de publicar um aplicativo no Google Play necessrio denir a verso do
aplicativo no arquivo app/buildgradle, o que feito congurando os itens versiontode
e versionName:

app/build.gradle
and roid {

defaultCong {
applcatonld "br.cormlivroandroid.carros"
mnSdkVerson 9 // API Level mnima
targetSdkVerson 22 // API Level tar9t/al-V0 (Sempre 3 ltima)
versonCode 1o dentcador
// Nmer . _ no Goo9l@ Play
da verso

} - , . z ' z lm
versionllane "1 6" // Verso que o usurio vai ver.

. . d utizado pelo Google Play para instalar ou atualizir tg


O item vers1onCo e I memssegcmpre deve ser maior que O da www
nu
aPlicativO Pela 1012, Pmtamo C l 1011
.._,1'I71
1012 Google Android - 4 edio
anterior. O item versionName e mostrado ao usuario para que ele saiba a verS1O CU
que a aplicao est.
Se necessrio, podemos recuperar a verso dentro do cdigo:
try {
Packagelnfo packagelnfo = getPackageManager().getPackageInfo(
"br.com.Ivroandrod.carros", G);
Log.d(TAG,"Pacote: " + packageInfo.packageName + ", verso: " +
packageInfo.versonCode + " - " + packageInfo.verstonName);
} catch (NameNotFoundExcepton e) {
Log.e(TAG,e.getMessage(),e);
}

Esse cdigo imprimir a seguinte mensagem no LogCat:


D/Ivro(328): Pacote: br.com.Ivroandroid.carros, verso: 1 - 1.9

40.2 Compilando o projeto corretamente


Outra congurao importante no arquivo app/buildgradle diz respeito compati
bilidade da aplicao com a verso da imagem do sistema instalada no dispositivo.
Sempre congure o atributo mtnSdkVerson para a menor verso que o aplicativo
vai suportar e o atributo targetSdkVerson para a ltima verso, para otimizar o
build e ativar os recursos dos novos dispositivos. O Google Play vai respeitar a
congurao do mnSdkVersion no momento da instalao; caso o dispositivo no
seja compatvel (API Level mnima), a aplicao no ser instalada.

Nota: a biblioteca de compatibilidade Support Library, criada para trazer a


compatibilidade com as verses antigas do Android, apresenta vrios recursos,
como action bar, Toolbar, fragments, ViewPager, notications, animaes,
entre muitos outros. A biblioteca de compatibilidade appcompat-v7 compatvel
com API Level 7 (Android 2.1). Atualmente, eu costumo criar aplicativos com
compatibilidade com API Level 9 (Android 2.3). Mas isso depende do aplicativo
que voc est desenvolvendo. Se seu cliente for algum grande banco, claro que
eles vo querer atingir o mximo de usurios possveis; porm, muitas vezes,
dependendo do aplicativo, pode-se denir a compatibilidade com Android 5.x
ou superior, tudo depende dos requisitos do projeto.

Para informaes sobre o nmero de cada API Level, visite esta pgina:
http://developexandroid.com/guide/appendix/api-levels.html
.. , ,__ '`P l1:
Captulo 40 Google Play
Para mlorma`es. 1 . - - ` ou
Q so ore a biblioteca de com patibilidade visite esta '
http.//dr vi Iopenandrord.com/tools/support-library/ 8 na

40.3 Assinando o aplicativo pelo Android Studio/Gradle

eNo. _assimmos
ca vtulo `
o ` a-criar. yo certicado
1 38, sobre Gradle, aprendemos e E le'storwe dI
projeto corn esse certificado. Para assinar o aplicativo podemos
_ _ azer o uild pelo ro G dl
utilizar o menu Build >Generate Signed APK ou f b ' - , .
Enm i ' i' ~ - _
temos os dois tipos de builds: debug e release. p pno ra el P015
* 55 e 5 um lembfff, POIS ja aprendemos a fazer isso no captulo 38.
Apenas lembre-seqde
p aue
puar bl'no Google Play deve ser utilizado o cer
icar
ticado de release.

Dica: nunca perca o certificado de release, pois somente com ele possvel
atualizar um aplicativo na loja do Google Play.

Embora seja mais fcil assinar o aplicativo com o Android Studio e o Gradle,
tambm possvel assinar o aplicativo utilizando as ferramentas keytool e jarsgner
disponveis no JDK. Caso precise de mais detalhes sobre isso, recomendo ler a
documentao ocial.
http://developerandroid.com/tools/publishing/app-signing.html

40.4 Publicando no Google Play

A publicao de uma aplicao no Google Play simples: basta entrar no seguinte


site, utilizando um login vlidO do Gm=1il=

. . . ` ' ` _ De ois disso voc poder


https://play.gogle. com/apps/pul7liSl1/

Ser nccesflv Pagar USS; 5OOO para dbnr sua wm,a, . [oe no precisa renovar.
fazer o upload de diversas aplicaoes. O pagamento e unic
Depois que .sua
- , -f `'~z2ublicar
f . uma
_ , no
va.
cont 1 for criada basta entrar no site de administrao do Go0ll
play para visualizar as aplicaoes Ja publicadas ou p
' ~ - v ,` V `
' ~~ ~ llLl[l\~t)
_ _ _ . r ara ublicar um .ipno_Google
A __.
_ _, . ) [111] t..lI'
Nota: o cadastro de dtsenvolvedo 'P PV [o dc crdito nm.nm(,,m1 mia
custa U5$ 50.00, sendo iitetslfl
fazer o pagamento.
1014 Google Android - 4 edio
Na pgina de console do Google Play voc pode clicar no boto Add New Application
para preencher o formulrio com uma breve descrio de sua aplicao e enviar
alguns screenshots. No formulrio, basta fazer o upload do arquivo .apk que
foi assinado, escolher a categoria da aplicao (logo, Finanas, Sade, Esp0rIS,
Educao etc.) e denir se ela gratuita ou paga. O formulrio que voc precisa
preencher simples, e h uma boa documentao de cada campo a ser preenchido,
ento voc no ter diculdades.
Depois de publicar uma aplicao, ela ser disponibilizada quase que instanta
neamente no Google Play e estar pronta para ser instalada.
Lembre-se de que, a cada atualizao da aplicao, necessrio incrementar o
parmetro versonCode no arquivo buildgmdle. Mas a boa notcia que se voc
tentar publicar um apk sem incrementar o nmero da verso, o Google Play vai
avis-lo, ento tudo tranquilo.
Depois de publicar, lembre-se de acompanhar os comentrios e relatrio de erros
enviados pelos usurios, para corrigir eventuais bugs e a m de melhorar seu
aplicativo. Ao atualizar o aplicativo na loja, nunca se esquea de fornecer uma
breve descrio com as melhorias da nova verso, para o usurio ter conhecimento
das novidades.

Nota: para aplicativos pagos, o desenvolvedor recebe 70% do valor do aplicativo


em cada venda, e o Google ca com 30% para manter o servio.

40.5 Monetizao com anncios


Ao publicar aplicativos no Google Play; talvez algo que voc tenha interesse em
aprender como colocar os famosos anncios no aplicativo, a m de tirar um
lucro com o aplicativo, mesmo se ele for gratuito.
Eu mesmo nunca coloquei nenhum anncio, pois na empresa fazemos aplicativos
comerciais em forma de projeto fechado para nossos clientes, ento no posso lhe
dizer se realmente os anncios do dinheiro ou no. O que todos sabem que, caso
seu aplicativo faa sucesso e seja baixado por milhes de pessoas no mundo inteiro,
ento com certeza voc car bem feliz. O que voc precisa ter uma boa ideia.
Caso tenha interesse em aprender mais sobre anncios, recomendo assistir duas
palestras feitas no Google I/ O No Google I/0 2010 foi apresentada a plataforma AdMobs
e todos os conceitos foram explicados durante uma palestra de 40 minutos. Esta
palestra pode ser encontrada no YouTube ou no site do Googie I/0, basta procurar pelo
Captulo 40 n Google Play

ttulo Google I/OY 2010


8 311~monetiz'
Anal zin d 1015

. uistraafo`' s ul
mamente recomendado assistir o vdeo oi Cl d mg Your mobile apps? eXn.e_
uma rand ' ~' O astasse
ame ur I/0 2
0 500916
nmeros so exibidos Como se n b P S 3 os muito interessantes, grcos e
app .ld e [palestra foi feita sobre o tema, com o ttulo Don't ju tllilllovameme
us1ness.E novamente recomendo que voc assista
o vdeo.a moblle

`
z
Durante a ale

~ `"' :
ca0 confrm ram expllcadas 35 diversas formas de rentabilizar a sua a li

i J 5 i . EE Q
, e a gura 40.1 que foi extrada de um dos slides da apresentalo

.Us iE2E il 1i 3 12
. f 5 "~ 1 1
fPaid downloads Freemium l |n_app purchase if Advemsmg l


4 functionality
itona. qgoodsgwithin ig lon.ads
lthe app 5
...~_. __-.. -7.-_.. --........- .z ,.-
- z-... _-.. _...-._ _ ,l._-_"__
Q _~__
... _... . ___ _._`->.-....
-v`.-,...-~.- ; L -~`-I
_--..E '.,_`_. - ,_`-zl
- `--...4-..v.-..-z.

Figura 40.1 -Apresentao Google I/O 2011.

Basicamente, existem quatro maneiras para 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 billing Esta outra maneira de disponibilizar a aplicao de forma gra
tuita, mas cobrar por compras dentro do aplicativo utilizando a
plataforma integrada do Google Play No caso de um jogo, poderiam
ser comprados novas fases e poderes especiais. E no caso de uma

Advertising _ _ _ aplicao poderiam ser adquiridos novos mdulos e recursos.


Neste mtodo a aplicao disponibilizada de forma gratuita, mas

_ ,. ~- H' " 'IIldSDl(.N Cl


anncios de publicidade so adicionados na aplicaao.

Sobre criar anuncios, chamados de Ads , e utilizado o Google Mobie s rir S


, . - ' ^ recise.
cumentao ocial tem um p asso a passo (quick start) que m0Str21 COFHO C
anuncios. simples e rapido, caso voce p
https://appsadmob.com/admob/
l
ndroid.com/google/play-services/ads. htm
http://developed
-ads-sdk/docs/admob/android/quiC2-Saff
https://developers.g008l@-com/mobile
1015 Google Android - 4 edio
Sobre o In-app billing, tambm recomendo estudar a documentao OC13l
http://developer android. com/google/play/billing/
Lembre-se tambm de que mostramos como criar builds customizados com o
Gradle, portanto voc pode criar verses gratuitas e pagas do seu apl1catlVO.

40.6 Links teis

Chegamos ao nal do livro e gostaria de parabeniz-lo pela grande conquista de


se tornar um desenvolvedor Android. Agora, voc est pronto para entrar em um
dos mercados que mais crescem no mundo.
Desejo-lhe boa sorte nessa jornada e aguardo os seus aplicativos no Google Play!
Para continuar seus estudos sobre como assinar o aplicativo e public-lo na loja,
segue o link com a documentao ocial.
Android Developers - Signing Your Applications

http://developexandroid.com/tools/publishing/app-signing. html
Conhea tambm
do mesmo autor

O objetivo deste livro ensinar o


desenvolvimento de aplicativos
para iPhone e iPad. Explica os
conceitos desde o bsico, com
uma introduo ao Xcode e a
linguagem Objective-C,
desenvolvimento para iOS e
componentes visuais que podem
ser utilizados. Aborda diversos
temas e boas prticas de
desenvolvimento, comunicao
com web services, XML e JSON,
gerenciamento de threads, banco
de dados e arquivos, mapas e
GPS, recursos de multimdia,
animaes, sensores de
acelermetro e giroscpio,
gerenciamento de memria no
Objective-C e ferramentas de
proler disponveis no Xcode.
sistema operacional mvel mais
vel para diversas
tablets, TV (Google TV),
Wear), ocu le Glass) e carros

a mergulhar no incrvel mundo


e a imaginao o limite. A obra traz todos
necessrios para desenvolver aplicativos para
smartphones, tablets e. relgios, desde o bsico - sobre a
instalao do emulador e congurao do ambiente de
desenvolvimento no Adroid Studio - at aplicaes que
utilizem recursos avanados, como mapas, localizao por
GPS, multimdia, consulta em web services, persistncia em
banco dedados, sensores, execuo de servios em
segundo plano e muito mais.
Ser desenvolvido um aplicativo completo passo a passo,
conforme as boas prticas de desenvolvimento e interface
e utilizando conceitos do Material Design.
Para mais informaes, visite o site do livro
www.livroandrod.com.br.

Ricardo R. Lecheta formado em Cincias da Computao e


ps-graduado em Gesto do Desenvolvimento de Software pela
PUC-PR. Atualmente, trabalha com desenvolvimento e consultoria
de tecnologias mobile para diversas plataformas.
Pode ser contatado pelo email r/echeta@gma/.com.

9
Fique conectado:

faceboolccom/novatec

D twittencom/novateceditora SW
www.novatec.com.br
8575 2

Anda mungkin juga menyukai