Sei sulla pagina 1di 168

Universidade Federal de So Carlos

Centro de Cincias Exatas e de Tecnologia


Departamento de Computao

Introduo s tecnologias para desenvolvimento de


aplicaes em plataformas mveis Android
Processo: 23112.003595/2012-35

Coordenadores:
Ricardo Menotti
Daniel Lucrdio
Autor/Bolsista:
Matheus Fernando Finatti

So Carlos - SP, 26 de janeiro de 2014

Sumrio

Lista de Figuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iv

Lista de Tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Lista de Algoritmos

vii

Lista de Algoritmos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

xi

Lista de Abreviaturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii


Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

xv

Introduo

Configurao do Ambiente

Linguagem do Android

3.1

Linguagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.2

Entendendo a estrutura de uma aplicao Android . . . . . . . . . . . . . . . .

Criando seu primeiro aplicativo

11

Design

27

5.1

Activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

5.2

Especifique a activity que inicia seu aplicativo . . . . . . . . . . . . . . . . . .

28

5.3

Tipos de Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

5.4

Listas (ListView) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

5.5

Listas Compostas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

5.6

Listas expansveis (ExpandableListView) . . . . . . . . . . . . . . . .

38

5.7

Grades (GridView) e imagens ImageView . . . . . . . . . . . . . . . . .

45

5.8

Fragmentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

5.9

Abas (Tabs) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

5.10 Trocar de pgina com gesto de arrastar usando ViewPager . . . . . . . . . .

60

5.11 Abas com gesto de arrastar . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

5.12 ActionBar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

64

Comunicao
6.1 Internet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Telefone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3 Short Message Service (SMS) . . . . . . . . . . . . . . . . . . . . . . . . . . .

71
71
76
77

Armazenamento
7.1 Shared Preferences: . . .
7.2 Armazenamento interno .
7.3 Armazenamento Externo
7.4 Banco de dados . . . . .

79
79
81
85
86

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

Cmera
91
8.1 Usando a API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
8.2 Gravando vdeos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
8.3 Usando um Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

udio
107
9.1 Gravando e tocando udio . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

10 Localizao e Mapas
111
10.1 Acessando a localizao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
10.2 Google Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11 Compartilhamento

119

12 Agenda e Contatos
125
12.1 Usando o Contacts Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
13 Acelermetro

135

14 Bluetooth

139

15 Internacionalizao

151

ii

Lista de Figuras

1.1

Distribuio das verses do Android . . . . . . . . . . . . . . . . . . . . . . .

4.1

Primeira janela de criao de novo aplicativo . . . . . . . . . . . . . . . . . .

12

4.2

Segunda janela de criao de novo aplicativo . . . . . . . . . . . . . . . . . .

12

4.3

Terceira janela de criao de novo aplicativo . . . . . . . . . . . . . . . . . . .

13

4.4

Quarta janela de criao de novo aplicativo . . . . . . . . . . . . . . . . . . .

13

4.5

Quinta janela de criao de novo aplicativo . . . . . . . . . . . . . . . . . . .

14

4.6

Selecionando o Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

4.7

activity com os elementos colocados na tela . . . . . . . . . . . . . . . . . . .

17

4.8

Criando uma nova activity . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

4.9

Primeira tela do primeiro aplicativo . . . . . . . . . . . . . . . . . . . . . . .

25

4.10 Primeira tela aps escrever texto na caixa de texto . . . . . . . . . . . . . . . .

25

4.11 Segunda tela mostrando a mensagem enviada . . . . . . . . . . . . . . . . . .

25

5.1

Ciclo de vida de uma activity . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

5.2

LinearLayout vertical ( esquerda) e horizontal ( direita) . . . . . . . . .

29

5.3

LinearLayout composto . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

5.4

Exemplo de RelativeLayout . . . . . . . . . . . . . . . . . . . . . . . . .

30

5.5

FrameLayout com exemplo de posicionamento usando layout_gravity

31

5.6

Exemplo de TableLayout . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

5.7

Esquema de uma lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

5.8

Detalhes de um elemento da lista . . . . . . . . . . . . . . . . . . . . . . . . .

32

5.9

Lista simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

5.10 Lista Composta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

5.11 Exemplo de lista expansvel rodando em um smartphone . . . . . . . . . . . .

44

5.12 Esquema de um GridView . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5.13 Demonstrao de um GridView . . . . . . . . . . . . . . . . . . . . . . . .

47

5.14 Exemplo GridView com imagem em tela cheia . . . . . . . . . . . . . . . .

50

5.15 Esquema da interface com abas . . . . . . . . . . . . . . . . . . . . . . . . . .

54

iii

5.16 Figura mostrando as 3 abas criadas no exemplo . . . . . . . . . . . . . . . . .


5.17 Exemplo de ActionBar no aplicativo Calendrio . . . . . . . . . . . . . . . . .
5.18 Exemplo de busca na ActionBar . . . . . . . . . . . . . . . . . . . . . . . . .
10.1
10.2
10.3
10.4

Ativando Maps API no Google API Console . . . . . . . . . . .


Registrando um app no Google API Console . . . . . . . . . . .
Passo final para obter a API Key . . . . . . . . . . . . . . . . .
Adicionando a Google Play Services como bilbioteca do projeto

iv

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

59
65
69
114
114
115
116

Lista de Tabelas

1.1

Tabela com as distribuies das verses do Android, todas as verses com menos de 0.1% de participao foram desconsideradas . . . . . . . . . . . . . . .

Lista de Algoritmos

4.1

Exemplo de configurao de verso do SDK no arquivo AndroidManifest.xml


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

4.2

Cdigo da caixa de texto no arquivo activity_main.xml . . . . . . . . .

17

4.3

Cdigo do boto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

4.4

Arquivo de strings com as duas strings adicionadas . . . . . . . . . . . . . . .

18

4.5

Adicionando mtodo classe MainActivity . . . . . . . . . . . . . . . . . . .

19

4.6

Exemplo de import de uma classe Android . . . . . . . . . . . . . . . . . . . .

19

4.7

Adicionando uma Intent . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

4.8

Obtendo o contedo da caixa de texto e enviando para outra activity . . . . . .

19

4.9

Constante como chave para um extra . . . . . . . . . . . . . . . . . . . . . . .

20

4.10 Obtendo a string passada como extra do Intent . . . . . . . . . . . . . . . .

22

4.11 Mtodo onCreate() recebendo um Intent e mostrando a mensagem . . . . .

22

5.1

Exemplo de Launcher activity . . . . . . . . . . . . . . . . . . . . . . . . . .

28

5.2

LinearLayout no arquivo de layout . . . . . . . . . . . . . . . . . . . . . .

33

5.3

Cdigo de uma ListView . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

5.4

string-array populada com elementos . . . . . . . . . . . . . . . . . . .

33

5.5

Cdigo de uma activity com lista clicvel . . . . . . . . . . . . . . . . . . . .

34

5.6

Cdigo do arquivo item.xml . . . . . . . . . . . . . . . . . . . . . . . . . .

36

5.7

Cdigo da lista customizada . . . . . . . . . . . . . . . . . . . . . . . . . . .

37

5.8

Cdigo XML de uma Lista expansvel . . . . . . . . . . . . . . . . . . . . . .

38

5.9

Layout list_item_parent.xml . . . . . . . . . . . . . . . . . . . . . .

39

5.10 Layout list_item_child.xml . . . . . . . . . . . . . . . . . . . . . . .

39

5.11 Classe Parent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

5.12 Classe CustomAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

5.13 Construindo a lista expansvel na activity . . . . . . . . . . . . . . . . . . . . .

43

5.14 Layout do GridView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5.15 Classe ImageAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

5.16 activity com grade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

vii

5.17 Layout full_image.xml . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

5.18 Classe FullImageActivity . . . . . . . . . . . . . . . . . . . . . . . . .

49

5.19 Cdigo da activity aps as modificaes . . . . . . . . . . . . . . . . . . . . .

49

5.20 Classe BasicFragment . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

5.21 Layout da activity com um fragmento . . . . . . . . . . . . . . . . . . . . . .

52

5.22 Layout da activity com o FrameLayout . . . . . . . . . . . . . . . . . . . .

52

5.23 activity com adio dinmica de fragmento . . . . . . . . . . . . . . . . . . .

53

5.24 Layout da activity TabHostLayout . . . . . . . . . . . . . . . . . . . . . .

54

5.25 Layout do fragmento da aba. . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

5.26 Classe Tab1Fragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

5.27 Classe TabInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

56

5.28 Primeira parte da classe TabLayoutActivity . . . . . . . . . . . . . . . .

56

5.29 Classe TabFactory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

5.30 Mtodo initialiseTabHost() . . . . . . . . . . . . . . . . . . . . . . .

57

5.31 Mtodo onTabChanged() . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

5.32 Mtodo onSaveInstanceState() . . . . . . . . . . . . . . . . . . . . .

59

5.33 layout do ViewPager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

5.34 Classe PagerAdapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

5.35 Activity com PagerAdapter . . . . . . . . . . . . . . . . . . . . . . . . . .

62

5.36 Layout das abas com adio do ViewPager . . . . . . . . . . . . . . . . . .

63

5.37 Mtodo initialiseViewPager() . . . . . . . . . . . . . . . . . . . . .

63

5.38 Mtodo onTabChanged() alterado . . . . . . . . . . . . . . . . . . . . . .

64

5.39 Mtodos da interface ViewPager.OnPageChangeListener . . . . . . .

64

5.40 Menu padro dos exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

5.41 Mtodo padro onCreateOptionsMenu() . . . . . . . . . . . . . . . . .

66

5.42 Mtodo OnOptionsItemSelected() . . . . . . . . . . . . . . . . . . . .

66

5.43 Adicionando novo item na ActionBar . . . . . . . . . . . . . . . . . . . . . .

67

5.44 Configurando ActionBar no mtodo onCreate() . . . . . . . . . . . . . . .

67

5.45 Criando a caixa de busca na ActionBar . . . . . . . . . . . . . . . . . . . . . .

68

6.1

Atribuindo permisso de acesso Internet no Manifest . . . . . . . . . . . . .

71

6.2

Classe RequestTask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72

6.3

Usando RequestTask na activity . . . . . . . . . . . . . . . . . . . . . . .

73

6.4

Modificando o mtodo para requisies POST . . . . . . . . . . . . . . . . . .

74

6.5

Classe JSONParser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

6.6

Criando JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

6.7

Fazendo uma chamada telefnica . . . . . . . . . . . . . . . . . . . . . . . . .

76

6.8

Permisso para fazer chamadas telefnicas . . . . . . . . . . . . . . . . . . . .

76

6.9

Permisso para enviar mensagens SMS . . . . . . . . . . . . . . . . . . . . . .

77

6.10 Mtodo sendSMS() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77

viii

6.11 Chamando mtodo sendSMS() . . . . . . . . . . . . . . . . . . . . . . . . .

78

7.1

Utilizando SharedPreferences para salvar dados primitivos . . . . . . . .

80

7.2

Passos iniciais do listener, criando um alerta. . . . . . . . . . . . . . . . . . .

81

7.3

Salvando um arquivo e mostrando um Toast . . . . . . . . . . . . . . . . . .

82

7.4

Fechando o alerta ao clicar em close . . . . . . . . . . . . . . . . . . . . . . .

83

7.5

Criando um alerta com os arquivos salvos . . . . . . . . . . . . . . . . . . . .

83

7.6

Criando um alerta com os arquivos salvos . . . . . . . . . . . . . . . . . . . .

84

7.7

Verificando se o armazenamento externo est disponvel . . . . . . . . . . . .

85

7.8

Classe CarOpenHelper do SQLite . . . . . . . . . . . . . . . . . . . . . .

87

7.9

Usando o CarOpenHelper na activity . . . . . . . . . . . . . . . . . . . . .

88

7.10 Mtodo openCarList() . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

7.11 Mtodo fetchCarList() . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

8.1

Requisitando permisso para usar a cmera . . . . . . . . . . . . . . . . . . .

91

8.2

Requisitando permisso para gravar no armazenamento externo . . . . . . . . .

91

8.3

Requisitando permisso para gravar udio . . . . . . . . . . . . . . . . . . . .

92

8.4

Classe CameraAccess . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93

8.5

Classe CameraPreview . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

8.6

Layout da Activity que ir conter a visualizao . . . . . . . . . . . . . . . . .

95

8.7

Configurando a orientao da activity no Manifest . . . . . . . . . . . . . . . .

96

8.8

Primeira parte da classe CameraActivity . . . . . . . . . . . . . . . . . .

96

8.9

Criando um Callback para imagens JPEG . . . . . . . . . . . . . . . . . . .

97

8.10 Mtodo getOutputMediaFile() . . . . . . . . . . . . . . . . . . . . . .

98

8.11 Mtodo getOutputMediaFile() . . . . . . . . . . . . . . . . . . . . . .

99

8.12 Melhorando a qualidade das fotos tiradas . . . . . . . . . . . . . . . . . . . .

99

8.13 Mtodo prepareForRecording() . . . . . . . . . . . . . . . . . . . . . 100


8.14 Mtodo releaseMediaRecorder() . . . . . . . . . . . . . . . . . . . . 101
8.15 Liberando a cmera no mtodo onPause() . . . . . . . . . . . . . . . . . . 101
8.16 Configurando o listener do boto de gravar vdeo . . . . . . . . . . . . . . . . 102
8.17 Chamando a activity de cmera com Intent . . . . . . . . . . . . . . . . . . 103
8.18 Mtodo getOutputMediaFileUri() . . . . . . . . . . . . . . . . . . . . 103
8.19 Mtodo onActivityResult() . . . . . . . . . . . . . . . . . . . . . . . . 104
8.20 Criando um Intent para vdeo . . . . . . . . . . . . . . . . . . . . . . . . . 105
9.1

Mtodo prepareRecording() para gravaes de udio . . . . . . . . . . 107

9.2

Mtodo releaseRecorder() . . . . . . . . . . . . . . . . . . . . . . . . 108

9.3

Configurando o boto de gravar udio . . . . . . . . . . . . . . . . . . . . . . 108

9.4

Mtodo startPlaying() . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

9.5

Mtodo stopPlaying() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

9.6

Configurando o boto de tocar udio . . . . . . . . . . . . . . . . . . . . . . . 110

9.7

Varivel testFilename . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110


ix

10.1 Permisses para obter localizao . . . . . . . . . . . . . . . . . . . . . . . . 111


10.2 Criando um LocationListener . . . . . . . . . . . . . . . . . . . . . . . 112
10.3 Configurando o LocationManager . . . . . . . . . . . . . . . . . . . . . . 113
10.4 Configurando a API Key no Manifest . . . . . . . . . . . . . . . . . . . . . . . 115
10.5 Adicionando o uso do OpenGL no Manifest . . . . . . . . . . . . . . . . . . . 116
10.6 Adicionando o mapa como um fragmento no XML . . . . . . . . . . . . . . . 116
10.7 Activity com Google Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
10.8 Mtodo setUpMapIfNeeded() . . . . . . . . . . . . . . . . . . . . . . . . 118
10.9 Mtodo onLocationChanged() modificado . . . . . . . . . . . . . . . . 118
11.1 Enviando um texto simples atravs de um Intent . . . . . . . . . . . . . . . 119
11.2 Chamando createChooser() . . . . . . . . . . . . . . . . . . . . . . . . 120
11.3 Botes para compartilhar texto e imagem . . . . . . . . . . . . . . . . . . . . 121
11.4 Configurando os intent-filter no Manifest . . . . . . . . . . . . . . . . 122
11.5 Obtendo os dados do Intent e mostrando ao usurio . . . . . . . . . . . . . 122
12.1 Permisso para acessar os contatos . . . . . . . . . . . . . . . . . . . . . . . . 125
12.2 Activity que ir conter a lista de contatos . . . . . . . . . . . . . . . . . . . . . 126
12.3 Variveis para o adaptador da lista . . . . . . . . . . . . . . . . . . . . . . . . 126
12.4 Variveis para o Cursor do conjunto resultante da busca . . . . . . . . . . . . 127
12.5 Variveis de controle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
12.6 Mtodo onActivityCreated() . . . . . . . . . . . . . . . . . . . . . . . 128
12.7 Mtodo onCreateLoader() . . . . . . . . . . . . . . . . . . . . . . . . . 128
12.8 Mtodo onItemClick() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
12.9 Interfaces das consultas dos contatos . . . . . . . . . . . . . . . . . . . . . . . 130
12.10Classe ContactDetailsActivity . . . . . . . . . . . . . . . . . . . . . 130
12.11Mtodo onCreate() de ContactDetailsActivity . . . . . . . . . . 131
12.12Mtodo setContact() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
12.13Mtodo onCreateLoader() . . . . . . . . . . . . . . . . . . . . . . . . . 132
12.14Mtodo onLoadFinished() . . . . . . . . . . . . . . . . . . . . . . . . . 133
13.1 Classe AccelActivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
13.2 Mtodo onCreate() de AccelActivity . . . . . . . . . . . . . . . . . . 136
13.3 Mtodo onSensorChanged() . . . . . . . . . . . . . . . . . . . . . . . . 136
13.4 Mtodos onResume() e onPause() . . . . . . . . . . . . . . . . . . . . . 137
14.1 Classe DeviceListActivity . . . . . . . . . . . . . . . . . . . . . . . . 139
14.2 Primeira parte do mtodo onCreate() . . . . . . . . . . . . . . . . . . . . . 140
14.3 Mtodo onActivityResult() . . . . . . . . . . . . . . . . . . . . . . . . 141
14.4 Segunda parte do mtodo onCreate() . . . . . . . . . . . . . . . . . . . . . 142
14.5 BroadcastReceiver que captura dispositivos Bluetooth . . . . . . . . . . 142
14.6 Registrando e removendo o BroadcastRegister . . . . . . . . . . . . . . 143
14.7 Classe AcceptThread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
x

14.8 Classe ConnectThread . . . . . . . . . . .


14.9 Mtodo manageConnection() . . . . . . .
14.10Classe ConnectedThread . . . . . . . . . .
14.11Mtodo writeMessage() . . . . . . . . . .
14.12Mtodo showMessage() . . . . . . . . . . .
14.13Handler e Runnable . . . . . . . . . . . .
14.14Implementao do mtodo onItemClick()
14.15Terceira parte do mtodo onCreate() . . . .
15.1 strings.xml padro . . . . . . . . . . . . .
15.2 strings.xml em portugus . . . . . . . . .

xi

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

145
145
146
147
147
148
148
148
151
151

Lista de Abreviaturas

xiii

xiv

Resumo

Esse material didtico oferece uma viso geral de como programar para o sistema mvel
Android e utilizar suas APIs nativas na criao de aplicativos. O material tentar cobrir desde
o bsico, como a configurao do ambiente de desenvolvimento, criao de layouts bsicos e
complexos, estrutura geral de um aplicativo e, finalmente, apresentar a programao de aplicativos mais complexos que utilizam APIs nativas.
O objetivo fornecer noes sobre como utilizar as ferramentas do Android, introduzir
os conceitos sem entrar em detalhes aprofundados do sistema operacional e assim disponibilizar uma viso bsica sobre o assunto. Aps a leitura desse material e realizao da prtica
o leitor dever estar preparado para construir seus prprios aplicativos nativos, e poder at
monetiz-los se desejar.

xv

C APTULO

1
Introduo

O Android hoje est em centenas de milhes de dispositivos mveis ao redor do mundo, e


vem crescendo. uma plataforma para desenvolvimento em dispositivos mveis como smartphones, tablets e outros.
Construdo em uma colaborao open-source com a comunidade de Linux, o Android se
tornou a plataforma mvel mais utilizada e que mais cresce no mundo. Sua abertura o tornou
o favorito de consumidores e desenvolvedores, levando a um rpido crescimento no nmero
de aplicativos e jogos. Est disponvel em centenas de dispositivos diferentes e de fabricantes
diferentes em verses diferentes.
Atualmente1 existem 4 principais verses do Android, so elas da mais atual para mais
antiga:
Jelly Bean verso 4.2 e 4.1 que trouxe otimizaes de performance, uma nova interface
do sistema e outros 2
Ice Cream Sandwich verso 4.0 trouxe uma interface refinada e unificada para smartphones e tablets alm de facilidade com multitasking e outros 3
1

Data em que foi escrito: 06/2013


Jelly Bean: http://developer.android.com/about/versions/jelly-bean.html
3
Ice Cream Sandwich: http://developer.android.com/about/versions/android-4.0-highlights.html
2

Introduo

Honeycomb verso 3.0 desenvolvida exclusivamente para tablets 4


Gingerbread verso 2.3 introduziu refinamentos da interface, mais performance e tornou
o sistema mais intuitivo 5
O Google coletou os dados referentes a distribuio das verses do Android:
Verso
1.6
2.1
2.2
2.3 - 2.3.2
2.3.3 - 2.3.7
3.2
4.0.3 - 4.0.4
4.1.x
4.2.x

Codinome
Donut
Eclair
Froyo
Gingerbread
Gingerbread
Honeycomb
Ice Cream Sandwich
Jelly Bean
Jelly Bean

API
4
7
8
9
10
13
15
16
17

Distribuio
0.1%
1.5%
3.2%
0.1%
36.4%
0.1%
25.6%
29.0%
4.0%

Tabela 1.1: Tabela com as distribuies das verses do Android, todas as verses com menos
de 0.1% de participao foram desconsideradas

Figura 1.1: Distribuio das verses do Android

4
5

Honeycomb: http://developer.android.com/about/versions/android-3.0-highlights.html
Gingerbread: http://developer.android.com/about/versions/android-2.3-highlights.html

Introduo

Esse material ir cobrir alguns tpicos no desenvolvimento de aplicativos para android, tais
como:
Configuraco do ambiente de desenvolvimento: Como configurar o ambiente para comear a desenvolver aplicativos, os primeiros passos para criar seu primeiro aplicativo de
maneira simples;
Elementos da interface: Como projetar seu aplicativo para usar as principais interfaces.
Listas, Listas compostas, Grades, Abas, Menus so as interfaces mais usadas nos diversos
aplicativos no mercado; e
Elementos de hardware: Como projetar seu aplicativo para usar as APIs de hardware:
Bluetooth, GPS, SMS, Chamadas.
Para esse material, algumas convenes sero seguidas:
Os cdigos estaro sempre com a sintaxe colorida para facilitar a leitura;
URLs das referncias estaro nas notas de rodap; e
Dicas estaro envoltas por uma caixa para facilitar a visualizao

Introduo

C APTULO

2
Configurao do Ambiente

A instalao e configurao do ambiente de desenvolvimento para Android simples, o


Google fornece um pacote chamado ADT (Android Development Tools) que contm o ambiente Eclipse com o plugin do Android, algumas ferramentas para instalao dos aplicativos nos
smartphones, o gerenciador do SDK e as imagens para o emulador do Android. Essas ferramentas so suficientes para o desenvolvimento na plataforma. O pacote ADT pode ser encontrado
em: Android SDK1 .
Basta fazer o download do pacote e extrair que tudo j est pr-configurado para iniciar o
desenvolvimento, portanto no h muito o que configurar.
Caso opte por utilizar uma instalao j existente do ambiente Eclipse, voc pode instalar o
plugin do Android automaticamente atravs da ferramenta de instalao de plugins do ambiente.
Aps a instalao ser necessrio abrir o SDK Manager e instalar:
Android SDK Tools;
Android SDK Platform-Tools; e
Para cada API que voc ir utilizar, instalar o SDK Platform e opcionalmente o Documentation for Android SDK e o Samples for SDK.
1

http://developer.android.com/sdk/

Configurao do Ambiente

C APTULO

3
Linguagem do Android

3.1

Linguagem

A linguagem usada para programar na plataforma Android Java. Ento antes de engajar
no aprendizado Android altamente recomendvel estudar material Java e principalmente o
paradigma de orientao a objetos.
O Android tem algumas particularidades na organizao e configurao que feita atravs
de arquivos XML especficos do Android. Alguns arquivos XML servem para configurar o
aplicativo, layout de cada tela e outros do suporte a strings para facilitar o suporte a mltiplos
idiomas. Felizmente o conjunto Eclipse com ADT j cuida disso automaticamente e possui uma
srie de facilidades alcanadas por meio de interfaces grficas para os programadores. Por esse
motivo, para qualquer iniciante nessa rea recomendvel a utilizaco do ambiente Eclipse.
A criao de layouts dos aplicativos pode ser feita inteiramente atravs da interface grfica
disponvel no ambiente, no estilo drag and drop.
7

3.2

Linguagem do Android

Entendendo a estrutura de uma aplicao Android

Uma aplicao Android consiste de uma ou mais activities. Uma activity uma tela com views que interagem com o usurio. Como o Android segue o padro MVC (Model-View-Control)
as activities so os controllers e as views, views. As activities so classes do Java, o layout e
outros recursos so definidos em arquivos XML.
Dentre os diversos arquivos XML existentes na configurao de um aplicativo Android o
mais importante o AndroidManifest.xml 1 pois nele que se exprimem as configuraes
gerais do aplicativo. Nesse texto no iremos adentrar muito nos detalhes das configuraes,
mas apenas deixar claro que nesse arquivo que se colocam as verses do Android que seu
aplicativo ser compatvel com, as permisses para usar os recursos do aparelho como Internet,
GPS, Bluetooth, etc.
A pasta src/ contm o pacote com as classes do seu aplicativo isto , o cdigo fonte do
seu aplicativo. Tanto activites como classes de suporte devem estar dentro do pacote.
Dentro da pasta res/ de recursos, encontram-se outros arquivos, referentes disposio
do layout, valores de strings e imagens que sua aplicao ir utilizar. A pasta layout/ junto
com as pastas drawable-*/ servem para dispor o layout. Cada drawable comporta imagens
para um tamanho diferente de tela, enquanto que a pasta de layout contm a disposio geral
do layout. So nesses arquivos que se colocam os itens (views) que iro nas telas, como botes,
caixas de texto, caixas de seleo, etc.
Na pasta values/ o mais importante o arquivo strings.xml que contm os valores
das strings do aplicativo. Sempre que voc quiser referenciar alguma string, a mesma dever estar expressa nesse arquivo. Fica fcil dessa forma fazer o aplicativo suportar mltiplos idiomas,
pois basta traduzir esse nico arquivo para alterar todos os textos do aplicativo.
A pasta menu/ contm os layouts do menus do aplicativo, esses so aqueles que podem ser
acessados atravs da Action Bar2 ou atravs dos botes fsicos do aparelho.

1
2

Documentao do AndroidManifest: http://developer.android.com/guide/topics/manifest/manifest-intro.html


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

Linguagem do Android
Resumindo:
AndroidManifest.xml: Configuraes gerais do aplicativo;
src/: Classes do aplicativo; e
res/: Recursos do aplicativo tais que:
strings/: Todos os textos da sua aplicao, suporte a mltiplos idiomas;
layout/: Todos os layouts de suas telas (activites);
drawable/: Todas as imagens, separados por tamanho de tela; e
menu/: layout dos menus do aplicativo.

10

Linguagem do Android

C APTULO

4
Criando seu primeiro aplicativo

Para exemplificar a criao de um aplicativo, seguiremos o exemplo dado pelo prprio manual do Google sobre o Android (Ver original1 ). Trata-se de aplicativo simples do tipo Hello
World.
Iniciaremos criando um novo projeto no Eclipse acessando o menu: File -> New -> Android
Application Project.
Na janela que apareceu voc deve colocar o nome do aplicativo, do projeto e do pacote. O
nome do pacote deve seguir a conveno do Java2 .
Minimum Required SDK: a verso mnima do sistema operacional Android que sua
aplicao ir suportar, o mais comum a verso 8 do SDK que se refere ao Android 2.2.
Alguns tipos de layouts mais complexos no so suportados em verses mais antigas;
Target SDK: a verso principal do Android para qual seu aplicativo est sendo desenvolvido;
Compile With: Verso do Android com qual seu aplicativo ser compilado; e
Theme: Cores do layout.
1
2

Original em: http://developer.android.com/training/basics/firstapp/creating-project.html


Conveno sobre nome dos pacotes: http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html

11

12

Criando seu primeiro aplicativo

Figura 4.1: Primeira janela de criao de novo aplicativo

Figura 4.2: Segunda janela de criao de novo aplicativo

Criando seu primeiro aplicativo

Figura 4.3: Terceira janela de criao de novo aplicativo

Figura 4.4: Quarta janela de criao de novo aplicativo

13

14

Criando seu primeiro aplicativo

Figura 4.5: Quinta janela de criao de novo aplicativo

Criando seu primeiro aplicativo

15

Observe na Figura 4.1 a janela de criao de uma nova aplicao Android. Em Application
Name voc deve colocar o nome do aplicativo, em Project Name, o nome do projeto e em
Package Name o nome do pacote. Para esse exemplo utilizaremos como Minimum Required
SDK a verso API 8, j que nesse exemplo no usaremos nenhum layout que no suportado
em verses mais antigas. Em Target SDK e Compile With optaremos pela verso mais nova, a
API 17. Por final o Theme eu optei pelo Holo Light with Dark Action Bar que um tema com
fundo branco e barra superior preta, um dos padres do Android.

Dica: Para obter o mximo de compatibilidade sempre procure utilizar layouts compatveis com verses antigas, observe na figura 1.1 que verses antigas ainda tem uma fatia
considervel do mercado.
A figura 4.2 mostra a segunda janela da configurao inicial do seu aplicativo. Voc pode
escolher um cone personalizado se marcar a caixa Create custom launcher icon o que te levar
para a janela da figura 4.3. Se marcar Create Activity o assistente de criao te levar para a
janela da figura 4.4 onde poder escolher qual activity vai ser criada para seu aplicativo. Em
todos os exemplos escolheremos a opo Blank Activity. Como nosso projeto no uma biblioteca no marcaremos Mark this project as a library. Se marcar Create Project in Workspace o
assistente ir salvar o projeto na pasta que foi configurada para o Workspace, caso contrrio ele
ir pedir para escolher outro caminho. Como no trabalharemos com Working Sets do Eclipse,
a opo Add project to working sets permanece desmarcada.
Finalmente a figura 4.5 mostra a janela para nomear a activity inicial, nesse exemplo mantive
MainActivity. O nome do layout dessa activity mantive como activity_main que o padro. Na
caixa Navigation Type existem algumas opes de layout pr-definidas pelo Android. So elas:
None: O layout vem apenas com uma Action Bar3
Fixed Tabs + Swipe: O layout vem com algumas abas e com gesto de arrastar entre as
abas (activities) pr-programados.

Scrollable Tabs + Swipe: O layout vem com algumas abas e com gesto de arrastar entre
as abas pr-programados, porm nesse o estilo das abas diferente, em vez de abas fixas,
3
4

Documentao da ActionBar: http://developer.android.com/guide/topics/ui/actionbar.html


Tabs: http://developer.android.com/design/building-blocks/tabs.html

16

Criando seu primeiro aplicativo

so abas que movem para dar espao a outras.


Dropdown: O layout vem com a troca de activites atravs de um menu na Action Bar.
Voc pode configurar a verso do SDK manualmente modificando os valores no manifest.
Como mostrado no exemplo abaixo:

1
2
3

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

Algoritmo 4.1: Exemplo de configurao de verso do SDK no arquivo AndroidManifest.xml

A tag uses-sdk serve apenas para o compilador saber quais verses do Android voc
pretende que seu aplicativo suporte. Dessa forma quando seu aplicativo for lanado na loja
Google Play o aplicativo s ser visvel para aqueles usurios que possuem a verso mnima do
Android indicada no atributo.
Primeiro vamos criar um layout para o aplicativo usando o construtor de interfaces presente
no ambiente, primeiro abra o arquivo res/layout/activity_main.xml , segundo o
manifest, essa activity que ser aberta quando o aplicativo for iniciado, isso configurado
atravs do intent-filter5 .
Selecione o Hello world e o remova da sua activity.

Figura 4.6: Selecionando o Hello world

A tela dever ficar parecida com a da figura 4.4. Agora arraste um Text Field -> Plain Text
e um Form Widgets -> Button para sua activity.
5

Mais informaes na seo ??

Criando seu primeiro aplicativo

17

Figura 4.7: activity com os elementos colocados na tela


Ao clicar duas vezes no elemento no modo visual, voc ser levado ao marcador desse
elemento no XML correspondente da activity. Clique duas vezes na caixa de texto, o seguinte
cdigo ser exibido:
1
2
3
4
5
6
7
8
9
10
11
12

<EditText
android:id="@+id/nameField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="28dp"
android:layout_marginTop="35dp"
android:ems="10"
android:hint="@string/name">
<requestFocus />
</EditText>

Algoritmo 4.2: Cdigo da caixa de texto no arquivo activity_main.xml


Primeiro, na linha 2 modifique o id do Text Field para um nome mais intuitivo, nesse exemplo chamaremos apenas de nameField. O Android definiu que todo novo atributo id deve ser
precedido de @+id/. O smbolo @ diz para o compilador que estamos acessando os recursos
do Android, esses recursos so compilados na classe R automaticamente. O smbolo + diz para
o compilador que estamos criando um novo recurso. Por fim, id diz que estamos especificando
um novo identificador para esse recurso e s ento damos o nome a esse identificador.
Dica: Existem vrios tipos de recursos, porm importante salientar os diferentes tipos
de id. Quando referimos aos recursos podemos usar @android:id/ para acessar recursos
que j esto definidos no sistema Android. Usamos @id/ para acessar recursos que j foram
definidos no seu projeto. Para criar um novo recurso, usamos @+id/.

18

Criando seu primeiro aplicativo

Os outros atributos so para definir o tamanho, alinhamento e margem da caixa de texto. O


valor wrap_content dos atributos layout_width e layout_height (largura e altura,
linhas 3 e 4) fora a view a mudar de tamanho automaticamente para abrigar seu contedo. Os
atributos layout_alignParentLeft e layout_alignParentTop servem (linhas 5 e
6) para alinhar essa view com a view pai dela, dessa forma ficar alinhado com a borda esquerda
e com a borda superior do pai. Os atributos layout_marginLeft e layout_marginTop
(linhas 7 e 8) deslocam o elemento colocando uma margem entre a borda e a view, esses valores
estaro diferentes pois so computados automaticamente quando a view colocada atravs do
construtor de interfaces. Note que isso s acontecer caso esteja usando RelativeLayout6
que o nosso caso. Por tlimo o atributo ems (linha 9) configura o tamanho da fonte atravs
da unidade de medida Em.
Depois adicione uma hint para essa caixa de texto, uma hint algo que vai estar escrito
na caixa de texto quando ela estiver vazia, indicando que tipo de texto voc pretende que seja
escrito nessa caixa de texto, Neste exemplo (linha 10) a hint uma referncia a string chamada
name que iremos definir depois.
Depois modifique o cdigo do boto que est no mesmo arquivo, troque o id do boto (linha
2), tambm edite o atributo text (linha 8) para fazer uma referncia a uma string definida
no arquivo de strings que iremos chamar de send_button. Por ltimo adicione um atributo
onClick (linha 9) que define o mtodo que ser chamado quando esse boto for pressionado.
1
2
3
4
5
6
7
8
9

<Button
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@id/nameField"
android:layout_below="@id/nameField"
android:layout_marginTop="48dp"
android:text="@string/send_button"
android:onClick="sendMessage" />

Algoritmo 4.3: Cdigo do boto


Agora iremos definir as strings usadas anteriormente no arquivo res/values/strings.xml.
Abra ele e o modifique para que fique como mostrado no Algoritmo 4.4.
1
2
3
4
5
6

<resources>
<string name="app_name">MeuApp</string>
<string name="action_settings">Settings</string>
<string name="send_button">Enviar</string>
<string name="name">Nome</string>
</resources>

Algoritmo 4.4: Arquivo de strings com as duas strings adicionadas


Aps terminar abra a classe MainActivity.java localizada na pasta src/com.example.meuapp/
do seu projeto e adicione um novo mtodo que chamei de sendMessage, ele ser responsvel
6

Mais informaes na seo 5.3.2

Criando seu primeiro aplicativo

19

por obter o contedo da caixa de texto e enviar para uma nova activity que ir mostrar esse
contedo.
1
2
3

public void sendMessage(View view) {


// Fazer alguma coisa em resposta ao clique do botao
}

Algoritmo 4.5: Adicionando mtodo classe MainActivity

Dica: Isso vai requer voc importe a classe View, voc pode apertar Ctrl+Shit+O no
Eclipse para importar classes que estejam faltando

import android.view.View;

Algoritmo 4.6: Exemplo de import de uma classe Android


Primeiro, crie um novo Intent7 , um Intent um objeto que prov uma facilidade para
realizar uma ligao entre cdigos de diferentes aplicaes. O uso mais significante a inicializao de novas activities.
1
2
3
4

public void sendMessage(View view) {


// Fazer alguma coisa em resposta ao clique do botao
Intent intent = new Intent(this, DisplayMessageActivity.class);
}

Algoritmo 4.7: Adicionando uma Intent


Agora iremos obter o texto que est escrito na caixa para fazer algo com ele, no caso iremos
enviar para outra activity que ir mostrar esse texto. Como feito no algoritmo 4.7.
1
2
3
4
5
6
7

public void sendMessage(View view) {


Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText textBox = (EditText) findViewById(R.id.nameField);
String message = textBox.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}

Algoritmo 4.8: Obtendo o contedo da caixa de texto e enviando para outra activity
7

Documentao Intent: http://developer.android.com/reference/android/content/Intent.html

20

Criando seu primeiro aplicativo

O cdigo na linha 3 est obtendo a referncia da caixa de texto usando o mtodo findViewById() passando o id da caixa de texto como parmetro, esse id obtido acessando uma
varivel esttica da classe R (observe que esse e o mesmo id que voce colocou no arquivo xml
do layout da activity). Em seguida usando o mtodo getText() da caixa de texto, obtem-se
a string que foi escrita pelo usurio.

Por fim, essa string colocada no Intent com o mtodo putExtra(), uma Intent
pode carregar consigo uma coleo de vrios tipos de dados como pares chave-valor chamados
extras, esse mtodo toma a chave como primeiro parmetro e o valor no segundo parmetro.
Para que a prxima activity consiga coletar esse valor, voc deve definir uma chave para seu
extra usando uma constante pblica. Para isso adicione a definio de EXTRA_MESSAGE no
topo da sua classe MainActivity.

1
2
3
4
5

public class MainActivity extends Activity {


public final static String EXTRA_MESSAGE
= "com.example.meuapp.MESSAGE";
...
}

Algoritmo 4.9: Constante como chave para um extra

Agora voc deve criar uma nova activity, para isso v em File -> New -> Other -> Android
Activity e selecione Blank Activity. Preencha a prxima janela como na figura 4.8, depois clique
Finish.

Criando seu primeiro aplicativo

21

Figura 4.8: Criando uma nova activity


Observe a figura 4.8. Em Project voc vai especificar o projeto em que a nova activity ser
adicionado. Em Activity Name especifique o nome da sua nova activity. Em Layout Name defina
o nome do arquivo XML que contm o layout da nova activity. A opo Title define o ttulo
da activity, isso pode ser modificado posteriormente no arquivo de strings pois o ttulo ser
definido ali aps a criao da activity. A opo Launcher Activity ficar desmarcada pois essa
activity no ser usada para inicializar o aplicativo. Em Hierarchical Parent voc vai definir o
pai da nova activity, isso usado para o Android implementar corretamente para qual activity
o boto de voltar ir voltar. Por ltimo Navigation Type deixe como None pois s queremos o
design padro. Clique em Finish para criar a nova activity.
Dica: O Eclipse adiciona automaticamente activities criadas por esse mtodo no Manifest. Observe no Manifest como feito caso voc precise adicionar manualmente.
Abra a nova classe que foi criada junto com a activity. A classe j vem com alguns mtodos
implementados, alguns no sero necessrios para esse aplicativo e sero explicados em outras
sees, mas mantenha-os na classe. Todas as classes que so subclasses de Activity precisam
implementar o mtodo onCreate()8 que define o procedimento a ser executado quando a
activity criada.
8

http://developer.android.com/reference/android/app/Activity.html#onCreate(android.os.Bundle)

22

Criando seu primeiro aplicativo

Agora, precisamos extrair os dados enviados a essa activity atravs do intent, voc pode
obter a referncia do intent que comeou a activity chamando o mtodo getIntent()9 .
1
2

Intent intent = getIntent();


String mensagem = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);

Algoritmo 4.10: Obtendo a string passada como extra do Intent


Aps obter a referncia do intent que inicou a activity, queremos coletar os extras que foram
passado junto com ele. Criamos uma string que ir armazenar a mensagem que veio junto do
intent e chamamos o mtodo getStringExtra() passando como parmetro a chave desse
extra, que definimos na classe MainActivity. Agora para mostrar a mensagem na tela, voc
precisa criar um TextView10 , essa view serve para mostrar texto.
1
2
3

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Show the Up button in the action bar.


setupActionBar();

5
6
7

//Obtem o conteudo da Intent


Intent intent = getIntent();
String mensagem = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);

8
9
10
11

//Cria o TextView
TextView textView = new TextView(this);
textView.setTextSize(40);
textView.setText("Hello " + mensagem);

12
13
14
15
16

//Estabelece o text view como o layout da atividade


setContentView(textView);

17
18
19

Algoritmo 4.11: Mtodo onCreate() recebendo um Intent e mostrando a mensagem


As linhas 3, 5 e 6 foram colocadas automaticamente na criao da activity, a linha 3 faz
uma chamada ao mtodo da superclasse, a linha 6 um mtodo que inicializa a Action Bar, que
nesse aplicativo a barra superior com o nome da activity e um menu de opes que j vem
pr-programado. Deixamos isso como est.
O algoritmo 4.10 foi colocado nas linhas 9 e 10 para obter a referncia ao Intent. Nas linhas
12-15 criamos um novo TextView, configuramos o tamanho da fonte e atribumos o texto que
ser mostrado na tela a view, respectivamente.
Agora que o aplicativo est pronto, necessrio testar, caso tenha um smartphone Android
voc pode conect-lo no seu computador e rodar diretamente, seno voc dever rodar em um
9
10

http://developer.android.com/reference/android/app/Activity.html#getIntent()
http://developer.android.com/reference/android/widget/TextView.html

Criando seu primeiro aplicativo

23

emulador. Lembrando que para ambos os casos necessria a instalao do SDK primeiro,
acesse Android SDK Manager e faa o download do SDK desejado.
Para rodar diretamente no smartphone:
1. Conecte seu smartphone no computador atravs do cabo USB. Se estiver desenvolvendo
no Windows ser preciso instalar os drivers USB do seu dispositivo. Se precisar de ajuda
para instalar os drivers acesse: OEM USB11
2. Ative o modo USB Debugging no dispositivo
Para Android 3.2 ou mais antigos, a opo deve estar em Configuraes -> Aplicativos -> Desenvolvimento
Para Android 4.0 e 4.1, a opo est em Configuraes -> Opes do desenvolvedor
Para Android 4.2 e mais novos, a opo est escondida por padro, para mostrar a
opo voc deve entrar em Sobre o telefone e clicar em Nmero da verso 7 vezes,
ao retornar para tela anterior dever aparecer Opes do desenvolvedor
Dica: Caso ocorra o erro Launch error: adb rejected command: device not found.
Verifique se o aparelho est conectado e se os drivers esto instalados corretamente. Na rea
de notificaes do aparelho deve ter uma notificao escrita: Android debugging enabled.

11

http://developer.android.com/tools/extras/oem-usb.html

24

Criando seu primeiro aplicativo

Para rodar no emulador:


1. Abra o SDK Manager atravs do Eclipse em: Window -> Android SDK Manager
2. Verifique se, para Android 4.2.2 (API 17) ou outro desejado os seguintes pacotes estejam
instalados
SDK Platform e;
ARM EABI v7a System Image ou;
Intel x86 Atom System Image
3. Verifique tambm se na aba Tools, os pacotes Android SDK Tools e Android SDK Platform-tools
esto instalados
4. Agora necessrio criar um AVD (Android Virtual Device12 ). No Eclipse acesse o menu
Window -> Android Virtual Device Manager
5. No AVD Manager clique em New
6. Complete as informaes do AVD, especificando um aparelho, nome, plataforma, espao
de armazenamento, quantidade de memria RAM. Em Device haver opes pr-configuradas
de aparelhos do google, os Nexus, e opes genricas de acordo com tamanho de tela. Em
Target voc dever escolher a verso do sistema Android que deseja. Em alguns casos
voc poder decidir pela CPU caso deseje ARM ou Intel Atom x86. A quantidade de
RAM no Windows fica limitada a 768MB, mais que isso pode acarretar em erros no
sistema.
7. Clique Create AVD
8. Ainda na janela Android Virtual Device Manager selecione o novo AVD e clique Start
9. Quando o emulador terminar de carregar, destrave a tela do emulador, usando o mouse.
Agora para rodar o aplicativo basta clicar em Run na barra de tarefas do Eclipse e selecionar
Android Application na janela Run as. O Eclipse ir instalar o APK e abrir o aplicativo automaticamente, no dispositivo ou no emulador. As figuras 4.9, 4.10 e 4.11 mostram a execuo do
aplicativo.
12

http://developer.android.com/tools/devices/index.html

Criando seu primeiro aplicativo

Figura 4.9: Primeira tela do primeiro aplicativo

Figura 4.10: Primeira tela aps escrever texto na caixa de texto

Figura 4.11: Segunda tela mostrando a mensagem enviada

25

26

Criando seu primeiro aplicativo

C APTULO

5
Design

5.1

Activity

Enquanto um usurio navega pelas variadas telas de um aplicativo, sai dele e volta depois,
as instncias de uma activity transitam dentre diferentes estados em seu ciclo de vida. Quando
um aplicativo iniciado, uma activity inicial criada o sistema invoca mtodos especficos
que correspondem a criao dessa activity. Durante todo o ciclo de vida vrios mtodos so
chamados, e todos eles correspondem a diferentes estgios desse ciclo de vida.
Observe na imagem abaixo os mtodos correspondentes a cada estado da vida de uma activity, quando ela criada o mtodo onCreate() o responsvel pela configuraco inicial. O
sistema ao criar uma nova instncia de uma activity, cada mtodo muda o estado da activity um
degrau pra cima na pirmide.

Figura 5.1: Ciclo de vida de uma activity


Assim que o usurio comea a sair da activity, o sistema invoca outros mtodos que movem
o estado para nveis mais baixos da pirmide para comear a desmontar a activity. Em alguns
27

28

Design

casos a activity ir apenas ir at certo ponto e esperar (por exemplo quando o usurio troca para
outro aplicativo) tal que ela possa voltar de onde parou caso o usurio volte.
No so todos mtodos que precisam ser implementados pois isso ir depender da complexidade do seu aplicativo. importante salientar porm que, implementar esses mtodos ir
garantir que seu aplicativo se comporte de maneira correta, por exemplo voc deve garantir que:
Seu aplicativo no falhe quando o usurio receber uma chamada telefnica ou quando o
usurio troca de aplicativo;
Seu aplicativo no consuma recursos do sistema enquanto no estiver sendo usado;
Seu aplicativo no perca o progresso do usurio; e
Seu aplicativo no falhe ou perca o progresso do usurio quando a tela rotaciona entre
retrato e paisagem.
Apenas trs dentre os estados so estticos, isto , a activity pode ficar nesse estado por um
longo perodo de tempo:
Retomado (Resumed)
Nesse estado a activity est em primeiro plano e o usurio pode interagir com ela.
Pausado (Paused)
Nesse estado a activity est parcialmente obscurecida por outra activity - a outra activity
que est em primeiro plano semi-transparente ou no ocupa todo espao da tela. A activity
quando pausada no consegur interagir com o usurio e no executa nenhum cdigo.
Parado (Stopped)
Nesse estado a activity est completamente oculto e no est visvel para o usurio, est
em plano de fundo. Quando est parada, uma instncia de uma activity e toda informao de
seu estado tais como variveis so mantidos, porm a activity no executa nenhum cdigo.

5.2

Especifique a activity que inicia seu aplicativo

Quando um usurio abre um aplicativo, o sistema chama o mtodo onCreate() da activity que foi declarada como sendo a iniciadora do aplicativo. Voc pode definir qual activity
que vai iniciar seu aplicativo no arquivo AndroidManifest.xml que est no diretrio raz
do seu projeto.
A activity que inicia seu aplicativo deve ser declarada no manifesto com um <intent-filter>1
que inclui a <action> MAIN e a <category> LAUNCHER. Por exemplo:
1
2
3
4
5
6

<activity android:name=".MainActivity" android:label="@string/app_name">


<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Algoritmo 5.1: Exemplo de Launcher activity

Documentao <intent-filter>: http://developer.android.com/guide/topics/manifest/intent-filter-element.html

Design

29

Dica: Quando voc cria um projeto Android no Eclipse, por padro includa uma
classe activity que est declarada no manifesto com esse filtro.

5.3

Tipos de Layout

Uma Activity contm Views e ViewGroups. Uma view um elemento que tm presena na
tela do dispositivo tais como botes, textos, imagens e etc. Um ViewGroup por sua vez um
elemento agrupador de views que prov um layout na qual voc pode ajustar a ordem e apario
das views.

5.3.1

LinearLayout

O LinearLayout arranja views em uma nica coluna ou uma nica linha, desse modo as
views podem ser arranjadas verticalmente ou horizontalmente. Como mostrado na figura 5.2:

Figura 5.2: LinearLayout vertical ( esquerda) e horizontal ( direita)


ViewGroups tambm podem ser agrupados entre si para a criao de layouts mais complexos, por exemplo possvel agrupar um LinearLayout horizontal dentro de um vertical
dessa forma possvel colocar views lado a lado em uma camadas do LinearLayout vertical,
representada na figura 5.3.

Figura 5.3: LinearLayout composto

30

5.3.2

Design

RelativeLayout

O RelativeLayout permite especificar como as views so posicionadas uma em relao


a outra. Cada view embutida no interior de um RelativeLayout tem atributos que permitem
o seu alinhamento com outras views. Esses atributos podem ser encontrados na documentao2

Figura 5.4: Exemplo de RelativeLayout

Novamente cabe comentar que possvel aninhar diferentes ViewGroups para formar um
layout com maior complexidade.

5.3.3

FrameLayout

O FrameLayout o mais simples e eficiente tipo de layout, pode ser usado apenas para
mostrar uma view ou views que se sobrepem. Geralmente usado como um recipiente para os
Fragments3 .
Uma view definida em um FrameLayout sempre ser colocado no canto superior esquerdo da tela do dispositivo ou do ViewGroup a que pertence o FrameLayout. Se mais de
uma view foi definida elas sero empilhadas uma em cima da outra. Isso significa que a primeira
view adicionada ao FrameLayout ser mostrada na base da pilha, e a ltima adicionada ser
mostrada no topo.
Voc pode fazer com que as views no sobreponham as outras usando o atributo layout_gravity4 , dessa forma uma view pode ficar posicionada na borda inferior e outra na borda
superior e no ficarem sobrepostas.
possvel posicionar as views dentro de um FrameLayout usando parmetros diferentes
no layout_gravity, no exemplo da figura 5.5 existe um FrameLayout com 3 elementos
e cada um com parmetros diferentes. possivel combinar os parmetros utilizando a barra
reta |.
2

http://developer.android.com/reference/android/widget/RelativeLayout.LayoutParams.html
Mais informaes na seo 5.8
4
http://developer.android.com/reference/android/widget/FrameLayout.LayoutParams.html
3

Design

31

Figura 5.5: FrameLayout com exemplo de posicionamento usando layout_gravity

5.3.4

TableLayout

TableLayouts podem ser usadas para apresentar dados tabulados ou alinhar contedo
como tabelas HTML em uma pgina web. Um TableLayout composto de TableRows,
uma cada para linha da tabela. Os contedos das TableRows so as views que vo em cada
clula da tabela. Cada linha ter zero ou mais clulas e cada clula pode conter uma view.
O aspecto da TableLayout vai depender de alguns fatores. Primeiro, o nmero de colunas da tabela inteira vai depender do nmero de colunas da linha que contm mais colunas.
Segundo, a largura de cada coluna definida como a largura do contedo mais largo da coluna.
Voc pode combinar colunas para formar uma clula maior, mas no pode combinar linhas.
Leia mais na documentao5
Embora TableLayouts possam ser usados para projetar interfaces, geralmente no a
melhor opo j que so derivadas de LinearLayouts. Se voc tem dados que j esto em
formato de tabela, como planilhas, ento pode ser uma boa opo.

Figura 5.6: Exemplo de TableLayout

http://developer.android.com/reference/android/widget/TableLayout.html

32

5.4

Design

Listas (ListView)

Listas so uma das formas mais simples e poderosas de se mostrar informaes ao usurio
de forma objetiva. A ListView capaz de aprensentar uma lista rolvel de itens.

Figura 5.7: Esquema de uma lista


Um item individual da lista pode ser selecionado, essa seleo pode acionar uma outra tela
com detalhes do item.

Figura 5.8: Detalhes de um elemento da lista

5.4.1

Adaptadores

Adaptadores so usados para providenciar dados a views. O adaptador tambm define como
item da view ser mostrada. Para ListViews o adaptador define como cada linha ser mostrada.
Um adaptador deve extender a classe base BaseAdapter. O Android j tem alguns adaptadores padro, os mais importantes so o ArrayAdapter e o CursorAdapter.
O ArrayAdapter usado para manipular dados em arrays ou listas (java.util.List).
J o SimpleCursorAdapter consegue manipular dados em banco de dados.
6

Documentao ListView:http://developer.android.com/reference/android/widget/ListView.html

Design

5.4.2

33

Construo

A construo desse tipo de design simples. No arquivo de layout da activity use o LinearLayout para conter a ListView.
1
2
3
4
5
6

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

Algoritmo 5.2: LinearLayout no arquivo de layout


Se estiver usando o construtor de interface grfica, pode arrastar uma ListView para dentro do layout. Caso contrrio pode construir manualmente no arquivo XML do layout da activity.
Voc deve colocar o LinearLayout como raz do arquivo XML, o elemento raz sempre
deve conter o atributo xmlns:android como mostrado na linha 2 do algoritmo 5.2, no
entraremos em detalhes sobre os outros atributos.
Adicione uma ListView, escreva o cdigo abaixo dentro do LinearLayout.
1
2
3
4
5

<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>

Algoritmo 5.3: Cdigo de uma ListView


Voc precisa popular a lista, para isso voc pode criar um string-array no arquivo
strings.xml com os elementos que deseja colocar na lista. Nesse exemplo do algoritmo 5.4
foi criado uma lista com nome listString e 4 itens que sero mostrados em forma de lista
pela ListView.
1
2
3
4
5
6

<string-array name="listString">
<item>Menu 1</item>
<item>Menu 2</item>
<item>Menu 3</item>
<item>Menu 4</item>
</string-array>

Algoritmo 5.4: string-array populada com elementos


Finalmente, voc deve escrever o cdigo que ir preencher a lista com as strings. Como
feito no algoritmo 5.5 abaixo.

34
1
2

Design

public class MainActivity extends Activity {


private ListView lv;

3
4
5
6
7

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

//Obtem o array de strings para popular a lista


String listStr[] = getResources().getStringArray(R.array.listString);

9
10
11

//Obtem a lista
ListView lv = (ListView) findViewById(R.id.listView1);

12
13
14

//Adaptador das strings para a lista


lv.setAdapter(new ArrayAdapter<String>
(this, android.R.layout.simple_list_item_1, listStr));

15
16
17
18

/* Acao para quando clica num elemento da lista


* precisa criar um listener e programa-lo para
* realizar uma acao. */
lv.setOnItemClickListener(new OnItemClickListener() {

19
20
21
22
23

@Override
public void onItemClick(AdapterView<?> parent,
View view, int position, long id) {
//Quando clicado, mostra um Toast
Toast.makeText(getApplicationContext(),
((TextView) view).getText(), Toast.LENGTH_SHORT).show();
}
});

24
25
26
27
28
29
30
31
32
33

}
...

Algoritmo 5.5: Cdigo de uma activity com lista clicvel


Primeiro, na linha 2, foi criada uma varivel do tipo ListView para guardar um ponteiro
para a view j definida no layout.
No mtodo onCreate() voc precisa criar e inicializar a lista na sua activity. Na linha
10 obtemos as strings do string-array e o guardamos na varivel listStr. Usamos o
mtodo getResources() para poder adquirir o ponteiro para os recursos do aplicativo. Na
linha 13 conseguimos o ponteiro pra lista e o guardamos na varivel criada.
Usamos o adaptador ao chamar o mtodo ListView.setAdapter() nas linhas 16-17
e passamos como parmetro a criao de um novo adaptador do tipo ArrayAdapter. Para o
construtor7 desse adaptador est sendo passado o contexto atual da activity, um layout pr-definido
do sistema, o simple_list_item_1, e os dados na forma de array.
Na linha 22 usamos o mtodo ListView.setOnItemClickListener para configurar uma ao a ser executada quando um item da lista for clicado. Neste exemplo criado um
7

http://developer.android.com/reference/android/widget/ArrayAdapter.html

Design

35

Toast, o Toast mostra uma mensagem em uma caixa de texto na parte inferior da tela por um
curto perodo de tempo, nesse caso ir mostrar o mesmo texto do item da lista que foi clicado.
Uma das aplicaes mais comuns fazer com que ao se clicar em um item da lista, uma nova
activity seja aberta com detalhes do item.

Figura 5.9: Lista simples

A figura 5.9 mostra como ficou o exemplo ao ser executado em um smartphone, o item
Menu 2 foi clicado e um Toast foi mostrado no momento do clique.

5.5

Listas Compostas

possvel compor um item da lista colocando mais elementos alm de um texto. Para
isso voc precisa criar um novo arquivo XML que ir definir a customizao de cada linha da
ListView, nesse exemplo iremos definir um arquivo chamado item.xml, mostrado abaixo.

36
1
2
3
4
5

Design

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

6
7
8
9
10
11
12

<ImageView
android:id="@+id/userIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp" >
</ImageView>

13
14
15
16
17
18
19
20
21

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="vertical"
android:paddingLeft="0px"
android:paddingRight="5dp" >

22
23
24
25
26
27
28
29
30

<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textColor="#FFF38585"
android:textSize="15sp" >
</TextView>

31
32
33
34
35
36
37
38
39

<TextView
android:id="@+id/usertext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="#FF444444"
android:textSize="13sp" >
</TextView>

40
41
42

</LinearLayout>
</LinearLayout>

Algoritmo 5.6: Cdigo do arquivo item.xml


O algoritmo 5.6 mostra como voc pode fazer a customizao de um item da lista. Nesse
exemplo h uma pequena imagem esquerda e dois textos de cores e tamanhos diferentes. Para
isso primeiro criamos um LinearLayout que ir conter uma ImageView para mostrar a
imagem e outro LinearLayout para colocar os dois textos. As cores do textos so configuradas com o atributo textColor e usa o padro HTML de cores.

Design

37

Agora voc precisa usar um adaptador para mostrar esse layout customizado em cada linha
da lista, usaremos a classe SimpleAdapter8 . Essa classe faz a adaptao de um ArrayList de Maps para um layout definido.
1
2

public class MainActivity extends Activity {


private ListView lv;

3
4
5
6
7

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

//Obtem a lista
ListView lv = (ListView) findViewById(R.id.listView1);

9
10
11

//Cria uma lista de maps(key->value) dos views de cada item do ListView


List<Map> list = new ArrayList<Map>();
Map map = new HashMap();
map.put("userIcon", R.drawable.miku);
map.put("userName", "Hatsune Miku");
map.put("userText", "Texto exemplo para o adaptador");
list.add(map);
map = new HashMap();
map.put("userIcon", R.drawable.luka);
map.put("userName", "Megurine Luka");
map.put("userText", "Texto exemplo para o adaptador");
list.add(map);

12
13
14
15
16
17
18
19
20
21
22
23
24

//Cria um adaptador pro layout customizado


SimpleAdapter adapter = new SimpleAdapter(this,
(List<? extends Map<String, ?>>) list, R.layout.item,
new String[] {"userIcon", "userName", "userText"},
new int[] {R.id.userIcon, R.id.username, R.id.usertext});

25
26
27
28
29
30

lv.setAdapter(adapter);

31
32

Algoritmo 5.7: Cdigo da lista customizada


Observando o algoritmo 5.7. Na linha 13 criamos um ArrayList de Maps. Na linha 14
e 19 criamos um HashMap onde a chave uma string que identifica o contedo, essas chaves
sero userIcon, userName e userText respectivamente. Em userIcon colocamos uma imagem,
essa imagem deve ser colocada nas subpastas da pasta drawable e acessada atravs da
classe R. Em userName colocamos um nome de usurio, por exemplo. Em userText poderia ser
colocada uma descrio, ou uma frase customizada do usurio mas nesse exemplo foi colocado
uma sentena qualquer. Nas linhas 18 e 23 adicionamos o Map criado no ArrayList.
Criamos o SimpleAdapter nas linhas 26-29. Para o construtor passamos o ArrayList de Maps que contm os dados, passamos tambm o layout que definimos anteriormente
8

http://developer.android.com/reference/android/widget/SimpleAdapter.html

38

Design

R.layout.item. Passamos um array de strings que contm as chaves que sero usadas
para obter os dados e por ltimo um array de inteiros que contm os ids das views em que os
contedos dos Maps sero colocados.

Figura 5.10: Lista Composta


A figura 5.10 mostra como ficou o exemplo acima ao ser executado em um smartphone.

5.6

Listas expansveis (ExpandableListView)

Listas expansveis so teis para agrupar conjuntos de itens semelhantes, funcionam da


mesma maneira que as listas comuns e podem ser customizadas. Comece colocando sua lista
no layout da activity desejada.
1
2
3
4
5

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

6
7
8
9
10
11
12
13

<ExpandableListView
android:id="@+id/expandableList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:transcriptMode="alwaysScroll"
android:listSelector="@android:color/holo_green_light">
</ExpandableListView>

14
15

</LinearLayout>

Algoritmo 5.8: Cdigo XML de uma Lista expansvel

Dica: O atributo transcriptMode=alwaysScroll vai fazer com que a lista


sempre role at o final quando voc expande ou contrai um grupo. O atrbuto listSelector colore o item da lista quando este clicado.
Agora crie 2 novos arquivos XML, um chamado list_item_parent.xml e o outro
chamado list_item_child.xml dentro da pasta res/layout.

5
1
2
3
4
5
6

Design

39

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

7
8
9
10
11
12
13
14
15

<TextView
android:id="@+id/list_item_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="20sp"
android:padding="10dp"
android:layout_weight="1"
android:layout_marginLeft="35dp" />

16
17

</LinearLayout>

Algoritmo 5.9: Layout list_item_parent.xml

Nesses dois layouts teremos apenas uma TextView para abrigar um texto.

1
2
3
4
5
6
7

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item_child"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical">

8
9
10
11
12
13
14
15

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/list_item_text_child"
android:textSize="20sp"
android:padding="10dp"
android:layout_marginLeft="5dp"/>

16
17

</LinearLayout>

Algoritmo 5.10: Layout list_item_child.xml

Em seguida precisamos criar uma classe que ir abrigar os dados dos elementos pai, elementos estes que sero expandidos quando clicados. Nesse exemplo criamos uma classe Parent,
como mostrado no algoritmo 5.11

40
1
2
3

Design

public class Parent {


private String mTitle;
private ArrayList<String> mArrayChildren;

public String getTitle() {


return mTitle;
}

5
6
7
8

public void setTitle(String mTitle) {


this.mTitle = mTitle;
}

9
10
11
12

public ArrayList<String> getArrayChildren() {


return mArrayChildren;
}

13
14
15
16

public void setArrayChildren(ArrayList<String> mArrayChildren) {


this.mArrayChildren = mArrayChildren;
}

17
18
19
20

Algoritmo 5.11: Classe Parent


Essa classe contm o texto do item, que ser guardado na string mTitle e um ArrayList
que ir comportar os sub-itens desse item. Os mtodos get e set so simples.
Em seguida, crie uma nova classe, CustomAdapter que ser o adaptador da lista expansvel para os dados, para esse exemplo estaremos adaptando apenas para o uso de texto. Essa
classe deve extender a classe BaseExpandableListAdapter.

1
2
3

public class CustomAdapter extends BaseExpandableListAdapter {


private LayoutInflater inflater;
private ArrayList<Parent> parent;

4
5
6
7
8

public CustomAdapter(Context context, ArrayList<Parent> parent){


this.parent = parent;
inflater = LayoutInflater.from(context);
}

9
10
11
12
13
14
15

@Override
//Obtem o nome de cada item
public Object getChild(int groupPosition, int childPosition) {
return parent.get(groupPosition).getArrayChildren().
get(childPosition);
}

16
17
18
19
20

@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}

Design

21
22
23
24
25

@Override
//Nesse metodo voce seta os textos para ver os filhos na lista
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View view, ViewGroup viewGroup) {

26

if(view == null){
view = inflater.inflate(R.layout.list_item_child, viewGroup,
false);
}

27
28
29
30
31

TextView textView = (TextView)


view.findViewById(R.id.list_item_text_child);

32
33
34

textView.setText(parent.get(groupPosition).getArrayChildren().
get(childPosition));

35
36
37

return view;

38
39

40
41
42
43
44
45

@Override
public int getChildrenCount(int groupPosition) {
//retorna o tamanho do array de filhos
return parent.get(groupPosition).getArrayChildren().size();
}

46
47
48
49
50
51

@Override
//Obtem o titulo de cada pai
public Object getGroup(int groupPosition) {
return parent.get(groupPosition).getTitle();
}

52
53
54
55
56

@Override
public int getGroupCount() {
return parent.size();
}

57
58
59
60
61

@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}

62
63
64
65
66

@Override
//Nesse metodo voce seta o texto para ver os pais na lista
public View getGroupView(int groupPosition, boolean isExpanded,
View view, ViewGroup viewGroup) {

67
68
69
70
71

if(view == null) {
//Carrega o layout do parent na view
view = inflater.inflate(R.layout.list_item_parent, viewGroup,
false);

41

42

Design

72
73

//Obtem o textView
TextView textView = (TextView)
view.findViewById(R.id.list_item_text_view);

74
75
76
77

textView.setText(getGroup(groupPosition).toString());

78
79

return view;

80

81
82

@Override
public boolean hasStableIds() {
return true;
}

83
84
85
86
87

@Override
public boolean isChildSelectable(int groupPosition,
int childPosition) {
return true;
}

88
89
90
91
92
93

Algoritmo 5.12: Classe CustomAdapter


Primeiro precisamos de um LayoutInflater9 que ir instanciar o layout XML nas views
correspondentes, e um array da classe Parents que criamos anteriormente, esses sero os
itens principais da lista.
Na linha 7 no construtor da calsse, usamos o mtodo LayoutInflater.from() para
obter o inflater do contexto da activity.
Ao extender a classe BaseExpandableListAdapter temos que programar alguns mtodos. O mtodo getChild() deve adquirir o ponteiro para um subitem de um item na lista.
O mtodo getChildId() deve obter o id de um subitem, porm nesse exemplo no temos
nada configurado ento usamos a prpria posio desse subitem como id e retornamos childPosition.
O mtodo getChildView na linha 24 vai atribuir o layout dos subitens na linha 28. Na
linha 32 obtemos o TextView desse subitem e com o mtodo TextView.setText() atribumos seu respectivo texto. Esse texto est guardado no array chamado mArrayChildren
da classe Parent, ento a fim de obter esse texto devemos obter o Parent correto. Quando
voc clica em um item da lista, o Android guarda qual item voc clicou no parmetro groupPosition. Em seguida se obtm o texto de cada subitem pelo parmetro childPosition.
Outro mtodo importante o getGroupView, funciona da mesma maneira que getChildView mas configurando os views dos itens pai em vez dos subitens.
Para finalizar, voc deve construir os objetos na classe da activity, nesse exemplo para popular a lista eu coloquei no arquivo de strings alguns fabricantes e modelos de carros, voc
pode obt-los no repositrio do projeto.
9

http://developer.android.com/reference/android/view/LayoutInflater.html

5
1
2

Design

43

public class MainActivity extends Activity {


private ExpandableListView mExpandableList;

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

4
5
6
7
8

mExpandableList = (ExpandableListView)
findViewById(R.id.listaExpandivel);

9
10
11

ArrayList<Parent> arrayParents = new ArrayList<Parent>();


ArrayList<String> arrayChildren;

12
13
14

//Array de fabricantes no arquivo de strings


String parentsNames[] = getResources().
getStringArray(R.array.Fabricantes);

15
16
17
18

for(int i = 0; i < parentsNames.length; i++){


/*Para cada pai "i" criar um novo objeto
Parent para setar o nome e os filhos */
Parent parent = new Parent();
parent.setTitle(parentsNames[i]);

19
20
21
22
23
24

arrayChildren = new ArrayList<String>();


/* Obtem os carros daquele fabricante
* primeiro obtendo o resource id (passando o nome do fabricante)
* depois usando esse resource id para obter o array de strings
*/
int resId = getResources().
getIdentifier(parentsNames[i], "array", getPackageName());
String childrenNames[] = getResources().getStringArray(resId);

25
26
27
28
29
30
31
32
33

for(int j = 0; j < childrenNames.length; j++){


arrayChildren.add(childrenNames[j]);
}

34
35
36
37

parent.setmArrayChildren(arrayChildren);
arrayParents.add(parent);

38
39

40
41

mExpandableList.setAdapter(
new CustomAdapter(MainActivity.this, arrayParents));

42
43

}
...

44
45
46

Algoritmo 5.13: Construindo a lista expansvel na activity

44

Design

O algoritmo 5.13 a construo da lista expansvel na activity, na linha 9-10 obtemos a


view usando findViewById(). A linha 30-31 so um pouco mais complicadas, primeiro
preciso obter o id do string-array que subitem do item atual no lao de repetio. Para
isso usamos getResources().getIdentifier() para obter o id do subitem a partir do
nome do item pai. Em seguida podemos acessar o string-array normalmente como feito
na linha 31.
Na linha 41 usamos o CustomAdapter que criamos anteriormente.

Figura 5.11: Exemplo de lista expansvel rodando em um smartphone

Design

5.7

45

Grades (GridView) e imagens ImageView

Grades so teis para mostrar imagens e fotos como uma galeria, ou permitir a seleo de
categorias semelhante a uma lista. A idia ter elementos lado a lado para mostrar ou para
selecionar e mostrar mais detalhes. Basicamente funciona como uma grade bi-dimensional que
pode ser arrastada para os lados ou de cima pra baixo.

Figura 5.12: Esquema de um GridView


Comece colocando um GridView10 no layout de sua activity.
1
2
3
4
5

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

6
7
8
9
10
11
12
13
14
15
16
17

<GridView
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:columnWidth="90dp"
android:horizontalSpacing="10dp"
android:verticalSpacing="10dp"
android:gravity="center"
android:stretchMode="columnWidth" >
</GridView>

18
19

</LinearLayout>

Algoritmo 5.14: Layout do GridView


Vamos criar uma nova classe que ser o adaptador de imagens para o GridView, chamemos a classe de ImageAdapter, ela mostrada no algoritmo 5.15.
10

http://developer.android.com/reference/android/widget/GridView.html

46
1
2

Design

public class ImageAdapter extends BaseAdapter {


private Context mContext;

//Mantendo todos os ids num array


public Integer[] thumbIds = {
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7
};

4
5
6
7
8
9
10
11

//Construtor
public ImageAdapter(Context c){
mContext = c;
}

12
13
14
15
16

@Override
//Retorna o tamanho do array
public int getCount() {
return thumbIds.length;
}

17
18
19
20
21
22

@Override
//Retorna um elemento do array
public Object getItem(int position) {
return thumbIds[position];
}

23
24
25
26
27
28

@Override
//Nao sera usado
public long getItemId(int position) {
return 0;
}

29
30
31
32
33
34

@Override
public View getView(int position, View convertView,
ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
imageView.setImageResource(thumbIds[position]);
imageView.setLayoutParams(new GridView.LayoutParams(200,200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
return imageView;
}

35
36
37
38
39
40
41
42
43
44

Algoritmo 5.15: Classe ImageAdapter


A classe ImageAdapter deve ser subclasse da classe BaseAdapter11 . Temos, como
11

http://developer.android.com/reference/android/widget/BaseAdapter.html

Design

47

varivel pblica, um array das imagens que queremos colocar na grade. Como todo id de um
recurso da classe R um inteiro, criamos um array de inteiros. Note que estamos considerando
que todas as imagens j foram devidamente colocadas na pasta drawable.
O mtodo mais importante o mtodo getView(). Nele criamos uma nova ImageView12 para abrigar a imagem que queremos colocar na grade. Em seguida configuramos
alguns parmetros desse ImageView, o mtodo ImageView.setImageResource()
responsvel por estabelecer um drawable como contedo do ImageView. J o mtodo
View.setLayoutParams() configura os parmetros de layout associados com essa view,
note que para esse mtodo passamos parmetros de layout de uma GridView, que por sua vez
recebe (200,200) como largura e altura de um elemento da grade.
ImageView.setScaleType controla como a imagem deve ser redimensionada para
condizer com o tamanho do ImageView, ImageView.ScaleType13 so as formas disponveis para escalar a imagem.
Agora basta criar a grade em sua activity.
1

public class MainActivity extends Activity {

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

3
4
5
6
7

GridView gridView = (GridView) findViewById(R.id.gridview);

8
9

// Instance of ImageAdapter Class


gridView.setAdapter(new ImageAdapter(this));

10
11

12
13
14

...
}

Algoritmo 5.16: activity com grade


Exemplo acima rodando em um smartphone na figura 5.13:

Figura 5.13: Demonstrao de um GridView


12
13

http://developer.android.com/reference/android/widget/ImageView.html
http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

48

Design

Para complementar, voc pode fazer com que a imagem abra em tela cheia quando clicada
na view, para isso necessrio que voc passe o id do recurso do GridView para uma nova
activity que ir mostrar a imagem em tela cheia. Para isso precisamos criar um novo layout
XML, a qual chamaremos de full_image.xml, nele teremos apenas uma ImageView e
um TextView que ser uma pequena legenda da imagem.

1
2
3
4
5

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativelayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

6
7
8
9
10

<ImageView
android:id="@+id/full_image_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

11
12
13
14
15
16
17
18
19

<TextView
android:id="@+id/myImageViewText"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:gravity="center"
android:background="#55555555"
android:textSize="16sp"
android:textColor="#FFFFFF" />

20
21

</RelativeLayout>

Algoritmo 5.17: Layout full_image.xml

Note que para o TextView usaremos o atributo layout_width como fill_parent


e layout_height como 40dp, dessa forma criamos um pequeno retngulo de altura fixa
mas de forma que a largura preecha a tela completamente. O atributo background com
o valor #55555555 faz com que a cor do retngulo seja cinza com transparncia, j que o
parmetro alfa tambm tem valor 0x55. Tambm deixamos o texto com cor branca com o
atributo textColor.
Em seguida, crie uma nova classe chamada FullImageActivity, essa a activity que
vai mostrar a imagem em tela cheia. A construo da classe simples, voc deve apenas obter
o id da imagem passado como extra atravs do intent e ento obter essa imagem da classe
ImageAdapter.

5
1
2
3
4

Design

49

public class FullImageActivity extends Activity {


public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.full_image);

//Obtem os dados do intent


Intent intent = getIntent();

6
7
8

//Seleciona o id da imagem
int id = intent.getExtras().getInt("id");
ImageAdapter imageAdapter = new ImageAdapter(this);

9
10
11
12

//Configura o ImageView para mostrar a imagem correspondente


ImageView imageView = (ImageView) findViewById(R.id.full_image_view);
imageView.setImageResource(imageAdapter.thumbIds[id]);

13
14
15
16

//Configura o TextView para mostrar uma descricao da imagem


TextView textView = (TextView) findViewById(R.id.myImageViewText);
textView.setText("Image id: " + id);

17
18
19

20
21

Algoritmo 5.18: Classe FullImageActivity

1
2
3
4
5
6

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridView = (GridView) findViewById(R.id.gridview);
gridView.setAdapter(new ImageAdapter(this));

//Cria um listener para o evento de clique em um elemento da grade


gridView.setOnItemClickListener(new OnItemClickListener() {

8
9
10

@Override
public void onItemClick(AdapterView<?> parent, View view,
int pos, long id) {
//Envia o id da imagem para o FullImageActivity
Intent intent = new Intent(getApplicationContext(),
FullImageActivity.class);
intent.putExtra("id", pos);
startActivity(intent);
}
});

11
12
13
14
15
16
17
18
19
20
21

Algoritmo 5.19: Cdigo da activity aps as modificaes

50

Design

No algoritmo 5.19, configuramos um View.setOnItemClickListener() de forma


que quando uma imagem da grade for clicada, um Intent seja enviado a uma nova activity que
por sua vez ficar encarregada de mostrar a imagem em tela cheia. Quando um item clicado
conseguimos obter a posio dele na grade com o parmetro pos da funo onItemClick(),
essa posio equivalente ao id da imagem no array criado na classe ImageAdapter. A
FullImageActivity por sua vez recebe esse Intent que possui o id da imagem que
deve ser mostrada e configura o ImageView de acordo.

Figura 5.14: Exemplo GridView com imagem em tela cheia

5.8

Fragmentos

Fragmentos so a soluo do Android para criar interfaces de usurio modulares, eles vivem
dentro das activity e uma activity pode conter vrios fragmentos. Assim como as activity os
fragmentos possuem um ciclo de vida.
Dentre as vantagens de um fragmento esto:
Modularidade e reuso de cdigo
Habilidade de construir interfaces com mltiplos painis
Facilidade de construir aplicativos para celulares e tablets
O primeiro conceito a ser coberto como construir um fragmento, comece definindo o
layout do fragmento.
Um layout bem simples, apenas com um boto para efeito de demonstrao. Agora crie
uma classe BasicFragment

5
1

Design

51

public class BasicFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState){

3
4
5
6

//Obtem o layout do fragmento em uma view


View view = inflater.inflate(R.layout.fragment, container, false);

7
8
9

//Obtem o botao da view


Button button = (Button) view.findViewById(R.id.fragment_button);

10
11
12

//Um listener simples para o botao


button.setOnClickListener(new OnClickListener() {

13
14
15

@Override
public void onClick(View v) {
Activity activity = getActivity();

16
17
18
19

if(activity != null){
Toast.makeText(activity,
"A toast to a fragment", Toast.LENGTH_SHORT).show();
}

20
21
22
23

}
});
return view;

24
25
26

27
28

Algoritmo 5.20: Classe BasicFragment

Caso voc esteja desenvolvendo para API menores que 11 (HoneyComb 3.0) voc vai precisar usar a API de retrocompatibilidade que o Google providenciou para essas APIs, voc precisa
importar a classe de suporte:

import android.support.v4.app.Fragment;

Agora para incluir o fragmento na activity existem duas opes. A primeira inlcuir o
fragmento no XML da activity como voc faria com qualquer view.

52
1
2
3
4

Design

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

5
6
7
8
9
10
11
12

<fragment
android:id="@+id/fragment_content"
android:name="com.example.fragmento.BasicFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</fragment>
</LinearLayout>

Algoritmo 5.21: Layout da activity com um fragmento


Voc pode usar o <fragment> quantas vezes quiser para incluir mltiplos fragmentos.
Note que voc precisa usar um nome qualificado em android:name, veja mais na documentao oficial: activity-element14
Novamente, caso esteja desenvolvendo para APIs menores que 11, voc vai precisar fazer a
activity extender a classe FragmentActivity e importar a classe de suporte:
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity
Simplesmente configurando a activity para usar o fragmento vai fazer com que o fragmento
seja adicionado e renderizado na tela, entretanto voc deve querer ter mais controle de quando
e como seus fragmentos sero adicionados durante o curso do seu aplicativo. Para isso existe
uma maneira alternativa de adicionar o fragmento em tempo de execuo. A fim de adicionar o
fragmento em tempo de execuo voc precisa fazer uma mudana no layout da activity:
1
2
3
4

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

<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

6
7
8
9
10
11

</LinearLayout>

Algoritmo 5.22: Layout da activity com o FrameLayout


E uma mudana na activity que vai mostrar o fragmento:
14

http://developer.android.com/guide/topics/manifest/activity-element.html#nm

5
1

Design

53

public class MainActivity extends FragmentActivity {

2
3
4
5
6

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

//Como estamos usando o pacote de suporte


//Precisamos usar o Manager desse pacote
FragmentManager fm = getSupportFragmentManager();
//Voce pode obter um fragmento da mesma forma que obtem
//qualquer outra view usando o FragmentManager
Fragment fragment = fm.findFragmentById(R.id.fragment_content);

8
9
10
11
12
13
14

if(fragment == null){
//Comeca uma transacao de fragmentos
FragmentTransaction ft = fm.beginTransaction();
//Adiciona o fragmento
ft.add(R.id.fragment_content, new BasicFragment());
//"Commita" a transacao
ft.commit();
}

15
16
17
18
19
20
21
22
23
24

}
...

Algoritmo 5.23: activity com adio dinmica de fragmento

E dessa forma obtemos o mesmo resultado, porm com a adio dinmica do fragmento,
voc pode experimentar e fazer com que o boto remova um fragmento e coloca outro diferente
no lugar.

5.9

Abas (Tabs)

Existem diversas maneiras de criar uma interface com abas no Android, uma delas usando
as interfaces TabHost e TabWidget, outra imitando o comportamento usando apenas
Fragments.

5.9.1

Usando TabHost e TabWidget

Abas usando essas interfaces so suportadas por todas as verses do Android.Vamos criar
uma interface com abas seguindo esse esquema:

54

Design

Figura 5.15: Esquema da interface com abas


Primeiro precisamos criar uma activity que servir como recipiente para as abas e seu contedo. O TabWidget15 o controle de seleo das abas. Todo contedo das abas ficar contido
dentro do FrameLayout, nele que as respectivas activities sero mostradas. O TabHost16
por sua vez serve como um recipiente para o TabWidget e o FrameLayout.
Crie uma nova activity, a chamaremos de TabLayoutActivity. No XML que define
o layout da activity, insira o TabHost, o TabWidget e FrameLayout como mostrado no
algoritmo abaixo.
1
2
3
4
5

<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

7
8
9
10
11

<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

12
13
14
15
16

<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

17
18
19
20
21
22
23

</LinearLayout>
</TabHost>

Algoritmo 5.24: Layout da activity TabHostLayout


15
16

http://developer.android.com/reference/android/widget/TabWidget.html
http://developer.android.com/reference/android/widget/TabHost.html

Design

55

Agora precisamos definir o layout dos fragmentos, isto , o layout de cada aba. Para simplificar o exemplo, as abas s tero um fundo colorido, de cores diferentes. Para isso usa-se o
atributo background.
1
2
3
4
5

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF0000" />

Algoritmo 5.25: Layout do fragmento da aba.


Precisamos definir as classes de cada fragmento de aba. Cada classe dever extender a classe
Fragment e inflar seu layout correspondente. Depois cada fragmeneto ser instanciado pela
nossa activity principal, TabLayoutActivity usando o fragment manager. No algoritmo
5.26 est definido a classe Tab1Fragment (as classes Tab2Fragment e Tab3Fragment)
so exatamente iguais, exceto que elas inflam seus respectivos layouts).
Dica: Voc deve importar a classe android.support.v4.app.Fragment para
suportar verses mais antigas do Android!

1
2
3

public class Tab1Fragment extends Fragment {


public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState){

if(container == null){
return null;
}

5
6
7
8

return (LinearLayout) inflater.


inflate(R.layout.tab_fragment1, container, false);

9
10

11
12

Algoritmo 5.26: Classe Tab1Fragment


Na classe TabLayoutActivity, note que estamos extendendo a classe FragmentActivity para poder usufruir das funcionalidades dos fragmentos. necessrio configurar o
mtodo onCreate(), esse o ponto de incio da nossa activity. O primeiro passo inflar o
layout com abas definido no algoritmo 5.24. O segundo passo inicializar as abas, para isso
invocamos o mtodo TabHost.setup(), adicionar as abas e suas informaes em um mapa
e determinar a primeira aba como ativa.
Primeiro criaremos uma classe que servir de suporte para guardar as informaes relevantes
sobre as nossas abas.

56
1
2
3
4
5

Design

public class TabInfo {


private String tag;
private Class klass;
private Bundle args;
private Fragment fragment;

TabInfo(String tag, Class klass, Bundle args){


this.tag = tag;
this.klass = klass;
this.args = args;
}

7
8
9
10
11
12

Algoritmo 5.27: Classe TabInfo

Em seguida, comearemos a escrever nossa classe TabLayoutActivity.

1
2
3
4
5
6

public class TabLayoutActivity extends Activity


implements TabHost.OnTabChangeListener {
private TabHost mTabHost;
private HashMap<String, TabInfo> mapTabInfo =
new HashMap<String, TabInfo>();
private TabInfo mLastTab = null;

7
8
9
10
11
12

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Estabelece o layout da activity
setContentView(R.layout.activity_tab_layout);

13

//Metodo para inicializar as abas


initialiseTabHost(savedInstanceState);
if(savedInstanceState != null){
//Determina a aba que esta selecionada
mTabHost.setCurrentTabByTag
(savedInstanceState.getString("tab"));
}

14
15
16
17
18
19
20
21

Algoritmo 5.28: Primeira parte da classe TabLayoutActivity

Antes de criar o mtodo initialiseTabHost() precisamos criar outra classe suporte,


essa classe necessria para criar o contedo de uma aba sob demanda. Crie uma classe que
chamaremos de TabFactory e ela deve implementar a interface TabContentFactory17 .
17

http://developer.android.com/reference/android/widget/TabHost.TabContentFactory.html

5
1
2

Design

57

public class TabFactory implements TabContentFactory{


private final Context mContext;

public TabFactory(Context context){


mContext = context;
}

4
5
6
7

@Override
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumHeight(0);
v.setMinimumWidth(0);
return v;
}

8
9
10
11
12
13
14
15

Algoritmo 5.29: Classe TabFactory


O parmetro tag do mtodo createTabContent() que define qual aba foi selecionada. O mtodo retorna uma view para mostrar os elementos da aba selecionada.
Na classe TabLayoutActivity precisamos criar o mtodo initialiseTabHost().
Siga o algoritmo 5.30 abaixo. Note o uso do mtodo onTabChanged(). Precisamos implementar esse mtodo em seguida atravs da interface TabHost.OnTabChangeListener.
1
2
3
4
5

private void initialiseTabHost(Bundle args) {


mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
TabInfo tabInfo = null;
String tag;

//Cria Tab1
tabSpec = mTabHost.newTabSpec("Tab1");
tabSpec.setIndicator("Tab 1");
tabSpec.setContent(new TabFactory(this));
tag = tabSpec.getTag();
tabInfo = new TabInfo("Tab1", Tab1Fragment.class, args);
tabInfo.fragment = getSupportFragmentManager().
findFragmentByTag(tag);
mTabHost.addTab(tabSpec);
mapTabInfo.put(tabInfo.tag, tabInfo);
/* Repete para Tab2 e Tab3 */

7
8
9
10
11
12
13
14
15
16
17
18

//Ajusta primeira aba como default


onTabChanged("Tab1");
mTabHost.setOnTabChangedListener(this);

19
20
21
22

Algoritmo 5.30: Mtodo initialiseTabHost()

58

Design

Primeiro obtemos a view TabHost usando o mtodo findViewById(). Observe que


estamos pegando um recurso j existente do sistema Android, j que estamos chamando a classe
R do sistema, e no do nosso aplicativo. Em seguida chamamos o mtodo setup(), a documentao diz que necessrio invocar esse mtodo antes de adicionar abas se carregamos o
TabHost usando findViewById().
Criamos um TabInfo e um TabSpec para nos auxiliar na adio das abas. Para adicionar as abas, primeiro chamamos o mtodo TabHost.newTabSpec() para obtermos um
novo TabSpec associado a esse TabHost, colocamos a tag Tab1 nele, como pode ser
observado na linha 9. Em seguida determinados um indicador (que ser mostrado ao usurio) a
essa aba usando TabSpec.setIndicator(), na linha 10. Criamos um novo TabInfo e
passamos a tag criada, a classe com o contedo da aba e uma srie de argumentos que podem
ser passados entre activities. Na linha 12 usamos o mtodo addTab() criado anteriormente.
Na linha 13 adicionamos a tag e um ponteiro para o recm-criado TabInfo no HashMap.
Repetimos o mesmo procedimento para as abas 2 e 3, porm mudando os valores da tag,
do indicador e da classe. Por ltimo definimos a primeira aba como default e determinamos o
argumento this para o mtodo setOnTabChangedListener() pois iremos implementar o
mtodo onTabChanged em seguida.
1
2
3

@Override
public void onTabChanged(String tag) {
TabInfo newTab = mapTabInfo.get(tag);

if(mLastTab != newTab){
FragmentTransaction ft =
getSupportFragmentManager().beginTransaction();

5
6
7
8

if(mLastTab != null){
if(mLastTab.fragment != null){
ft.detach(mLastTab.fragment);
}
}

9
10
11
12
13
14

if(newTab != null){
if(newTab.fragment == null){
newTab.fragment = Fragment.instantiate(this,
newTab.klass.getName(), newTab.args);
ft.add(android.R.id.tabcontent, newTab.fragment, newTab.tag);
} else {
ft.attach(newTab.fragment);
}
}
mLastTab = newTab;
ft.commit();
getSupportFragmentManager().
executePendingTransactions();

15
16
17
18
19
20
21
22
23
24
25
26
27

28
29

Algoritmo 5.31: Mtodo onTabChanged()

Design

59

Primeiro obtemos as informaes da aba que queremos do mapa com mapTabInfo.get(tag),


usamos a tag para obter o objeto que queremos. Em seguida testamos para saber se a aba selecionada a mesma que a anterior, pois no faria sentido recarregar a mesma aba. Na linha
9 testado para saber se a ltima aba no nula, isso deve ser feito para evitar uma falha do
aplicativo, testamos tambm se o fragmento nulo para ento usar detach() para retirar esse
fragmento do layout.
Fazemos o mesmo com a nova aba, caso o fragmento seja nulo isso quer dizer que ele no
foi instanciado ainda, isto , a primeira vez que o usurio seleciona essa aba nesse ciclo de
vida do aplicativo. Caso isso ocorra, ento usamos instantiate() para instanciar esse novo
fragmento e add para adiciona-lo ao layout. Caso ele j tenha sido instnciado, ento apenas
usamos attach() para coloca-lo de volta no layout.
Por final preciso salvar a aba que estavamos caso o aplicativo fique em segundo plano. O
mtodo onSaveInstanceState fica encarregado disso.

1
2
3
4

protected void onSaveInstanceState(Bundle outState){


outState.putString("tab", mTabHost.getCurrentTabTag());
super.onSaveInstanceState(outState);
}

Algoritmo 5.32: Mtodo onSaveInstanceState()

A figura abaixo mostra o resultado.

Figura 5.16: Figura mostrando as 3 abas criadas no exemplo

60

5.10

Design

Trocar de pgina com gesto de arrastar usando


ViewPager

possvel trocar entre fragmentos usando o gesto de arrastar, isto , arrastando a tela de um
lado para o outro acionar a troca entre os fragmentos.
Primeiro iremos definir o layout do ViewPager18 . Depois iremos definir o PagerAdapter19 . Por ltimo precisamos definir a activity que ir conter o visualizador de pginas.

1
2
3
4
5

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

6
7
8
9
10

<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

11
12

</LinearLayout>

Algoritmo 5.33: layout do ViewPager

Agora defina uma nova classe chamada PagerAdapter que ir extender a classe FragmentPagerAdapter. Primeiro crie uma lista que ir conter os fragmentos, isto , as pginas
que sero exibidas. Ao extender essa classe precisamos implementar dois mtodos: getItem() e getCount(). O mtodo getItem(), na linha 10 do algoritmo 5.34, deve retornar
o item que ser selecionado pelo parmetro position. O mtodo getCount(), na linha
15, deve retornar a quantidade de pginas. Depois crie o construtor como mostrado nas linhas
4-7.

18
19

http://developer.android.com/reference/android/support/v4/view/ViewPager.html
http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html

5
1
2

Design

61

public class PagerAdapter extends FragmentPagerAdapter{


private List<Fragment> fragments;

public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {


super(fm);
this.fragments = fragments;
}

4
5
6
7
8

@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}

9
10
11
12
13

@Override
public int getCount() {
return this.fragments.size();
}

14
15
16
17
18

Algoritmo 5.34: Classe PagerAdapter

Por ltimo devemos construir a activity que ir conter o PagerAdapter e ser reponsvel
por mostrar as pginas. Neste exemplo iremos reutilizar os fragmentos que fizemos na seo
anterior quando trabalhamos com abas.

Dica: Voc pode importar as classes e os arquivos de layout. No PackageExplorer


na IDE Eclipse, clique com o boto direito sobre a pasta que quer importar os arquivos,
depois clique em Import e selecione General -> File System. Agora selecione o caminho da
pasta que contm os arquivos no campo From directory. Selecione os arquivos que deseja
importar e clique em Finish.
Note que ao importar classes Java ser necessrio trocar o pacote a que a classe pertence.
Outra opo usar o import do java para importar as classes sem ter elas no pacote.

Essa activity, como mostrada no algoritmo 5.35 abaixo, apenas precisa instanciar os fragmentos (linhas 10,11 e 12), criar e determinar o adaptador, linhas 14, 16 e 17.

62
1
2

Design

public class ViewPagerLayout extends FragmentActivity {


private PagerAdapter mPageAdapter;

3
4
5
6
7

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

List<Fragment> fragments = new ArrayList<Fragment>();


fragments.add(Fragment.instantiate
(this, Tab1Fragment.class.getName()));
fragments.add(Fragment.instantiate
(this, Tab2Fragment.class.getName()));
fragments.add(Fragment.instantiate
(this, Tab3Fragment.class.getName()));

9
10
11
12
13
14
15
16

mPageAdapter = new PagerAdapter(getSupportFragmentManager(), fragments);

17
18

ViewPager pager = (ViewPager) findViewById(R.id.viewpager);


pager.setAdapter(mPageAdapter);

19
20
21
22
23

}
...
}

Algoritmo 5.35: Activity com PagerAdapter

5.11

Abas com gesto de arrastar

Ao juntar os dois conceitos, o de layout com abas e o gesto de arrastar, podemos fazer o
controle das abas arrastando a tela. Esse tipo de design comum em muitos aplicativos pela
facilidade e rapidez com o que o usurio pode visualizar vrios contedos. Nesse exemplo iremos reutilizar o cdigo dos exemplos anteriores com algumas modificaes. Sero reutilizadas
classes: TabInfo, TabFactory, PagerAdapter, Tab1Fragment, Tab2Fragment,
Tab3Fragment.
Primeiro modificaremos o layout das abas adicionando o ViewPager aps FrameLayout.
Note que esse o mesmo layout do algoritmo 5.24, por isso o algoritmo 5.36 no est completo.

5
1
2
3
4
5

Design

63

...
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />

7
8
9
10
11
12
13
14

</FrameLayout>
...

Algoritmo 5.36: Layout das abas com adio do ViewPager


Agora iremos usar a classe TabLayoutActivity do exemplo anterior e fazer algumas
alteraes. Nesse exemplo irei mudar o nome da classe para SwipeTabActivity. A primeira alterao a adio de algumas variveis para serem usadas na classe, adicione uma
varivel PageAdapter e uma ViewPager. Depois devemos alterar o mtodo onCreate(), adicione uma chamada ao mtodo initialiseViewPager(). A implementao
a mesma do mtodo onCreate() do exemplo anterior com a adio da chamada ao mtodo
setOnPageChangeListener(). Devemos tambm implementar a interface OnPageChangeListener. No mtodo initialiseTabHost() voc precisa remover a linha
onTabChanged(Tab1);.
1
2
3
4
5
6
7
8

private void initialiseViewPager(){


List<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(Fragment.instantiate
(this, Tab1Fragment.class.getName()));
fragments.add(Fragment.instantiate
(this, Tab2Fragment.class.getName()));
fragments.add(Fragment.instantiate
(this, Tab3Fragment.class.getName()));

mPageAdapter = new PagerAdapter(getSupportFragmentManager(), fragments);

10
11

mViewPager = (ViewPager) findViewById(R.id.viewpager);


mViewPager.setAdapter(mPageAdapter);
mViewPager.setOnPageChangeListener(this);

12
13
14
15

Algoritmo 5.37: Mtodo initialiseViewPager()


Em seguida precisamos alterar o mtodo onTabChanged(), no necessitamos mais fazer
verificaes j que o prprio design ir limitar as falhas que poderiam ocorrer. Precisamos
apenas determinar para o ViewPager o item atual.

64
1
2
3
4
5

Design

@Override
public void onTabChanged(String tag) {
int pos = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(pos);
}

Algoritmo 5.38: Mtodo onTabChanged() alterado

Por fim, devemos implementar os mtodos da interface ViewPager.OnPageChangeListener,


somente o mtodo onPageSelected() ser usado, para selecionar a aba correspondente
pagina atual.

1
2
3
4

@Override
public void onPageScrollStateChanged(int arg0) {
//Nada
}

5
6
7
8
9

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
//Nada
}

10
11
12
13
14

@Override
public void onPageSelected(int position) {
mTabHost.setCurrentTab(position);
}

Algoritmo 5.39: Mtodos da interface ViewPager.OnPageChangeListener

Dica: Pela dificuldade em acompanhar passo a passo a construo desse design, recomendvel obter o cdigo do projeto no repositrio. Est sob o nome SwipeableTabs.

5.12 ActionBar
A ActionBar aquela barra presente em em todos os aplicativos que fizemos de exemplo
at agora. Ela pode mostrar o nome da activity, cones, aes que podem ser acionadas, outras
views ou botes interativos. Tambm pode ser usada para navegar entre as actvities do seu
aplicativo.
Dispositivos Android mais antigos possuem um boto fsico chamado Option que abre um
menu na parte inferior do aplicativo. A ActionBar melhor que esse menu pois est claramente visvel para o usurio, enquanto que o menu antigo era escondido e o usurio pode no
reconhecer que as opes esto disponveis.

Design

65

Figura 5.17: Exemplo de ActionBar no aplicativo Calendrio


A figura 5.18 mostra o uso da ActionBar no aplicativo Calendrio, padro dos aparelhos
Android mais atuais. possivel observar trs principais componentes. O primeiro um menu
drop-down que permite ao usurio mudar o modo de vizualizao do calendrio. O segundo
um boto com o dia atual, 19, que ao ser pressionado faz com que o calendrio posicione um
cursor no dia e hora atuais. O terceiro um outro menu drop-down com algumas opes que
podem ser interessantes ao usurio.

5.12.1

Implementando a ActionBar

A activity popula a ActionBar em seu mtodo onCreateOptionsMenu(). Entradas na


ActionBar so chamadas de aes (actions).
As aes para a ActionBar so definidas em arquivos XML posicionados na pasta menu/. O
algoritmo abaixo mostra o menu padro dos exemplos que construmos at agora. Ele s contm
um item Settings que est no dropdown menu que pode ser acessado atravs da ActionBar,
direita. O fato dele estar escondido se deve ao atributo showAsAction estar com valor never,
ao ser mudado para always o acesso ao Settings ser diretamente atravs da ActionBar. Existe
tambm o valor ifRoom que ir mostrar apenas se houver espao disponvel.
1
2

<menu
xmlns:android="http://schemas.android.com/apk/res/android" >

3
4
5
6
7
8

<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="always"
android:title="@string/action_settings"/>

9
10

</menu>

Algoritmo 5.40: Menu padro dos exemplos

66
1
2
3
4
5

Design

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

Algoritmo 5.41: Mtodo padro onCreateOptionsMenu()

Se uma ao selecionada, o mtodo onOptionsItemSelected() chamado. Ele


recebe a ao selecionada como parmetro MenuItem. Baseando-se nessa informao voc
pode decidir o que fazer. Nesse exemplo iremos abrir uma nova activity que seria a tela de
configurao do aplicativo.
No seu projeto, crie uma nova Activity do tipo Settings Activity.

Dica: Para criar uma nova activity, clique com o boto direito sob o projeto selecione
New -> Other (ou pressione Ctrl+N). Selecione Android Activity e selecione o tipo desejado.

Agora voc deve fazer com que o mtodo OnCreateOptionsMenu() abra essa nova
activity.

1
2
3
4
5
6

public boolean onOptionsItemSelected(MenuItem item){


if(item.getItemId() == R.id.action_settings){
startActivity(new Intent(this, SettingsActivity.class));
}
return true;
}

Algoritmo 5.42: Mtodo OnOptionsItemSelected()

Para melhorar nossa ActionBar vamos adicionar um campo para pesquisa. Voc pode adicionar views em sua ActionBar. Para isso voc deve usar o mtodo setCustomView() da
classe ActionBar e passar uma view como parmetro. Voc tambm precisa ativar a exibio
de views com o mtodo setDisplayOptions() e passar a flag ActionBar.DISPLAY_SHOW_CUSTOM.
Primeiro vamos adicionar um cone de busca na nossa ActionBar, voltando ao arquivo do
layout da ActionBar, adicione um novo item acima do primeiro como mostrado no algoritmo
abaixo.

5
1

Design

67

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

<item
android:id="@+id/action_search"
android:orderInCategory="100"
android:showAsAction="always"
android:title="Search"
android:icon="@android:drawable/ic_menu_search" />

3
4
5
6
7
8
9

<item
android:id="@+id/action_settings"
android:showAsAction="never"
android:title="@string/action_settings"/>

10
11
12
13
14
15

</menu>

Algoritmo 5.43: Adicionando novo item na ActionBar


Observe o atributo android:icon, estamos obtendo um drawable que j existe no sistema Android, e se chama ic_menu_search. Esse o cone da busca, a lupa. Depois vamos
adicionar uma ao a ser executada quando esse cone for clicado. Iremos criar nesse exemplo,
uma caixa de texto de forma programtica, isto , em vez de defini-la no XML iremos cri-la
com cdigo na Activity.
No mtodo onCreate() voc precisa configurar a ActionBar para mostrar views. Use
o mtodo getActionBar() para obter uma referncia da ActionBar e setDisplayOptions() para configur-la. Depois, no mtodo onOptionsItemSelected() voc vai
programar a ao do novo boto. Inicialmente cria-se uma nova view do tipo EditText,
colocamos algumas configuraes e a adicionamos na ActionBar. Por no termos uma busca
devidamente implementada, vamos mostrar um Toast com o contedo da busca, a fim de
demonstrao.
1
2
3
4

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

getActionBar().setDisplayOptions
(ActionBar.DISPLAY_SHOW_CUSTOM |
ActionBar.DISPLAY_SHOW_HOME);

6
7
8
9

Algoritmo 5.44: Configurando ActionBar no mtodo onCreate()


No algoritmo 5.45, abaixo, criamos um EditText e o configuramos caso o boto com o
cone de busca seja pressionado. Na linha 5-6 criamos um LayoutParams que configura o
tamanho da view. Na linha 8 o mtodo EditText.setImeOptions() responsvel por
configurar o teclado para uma busca, junto com a linha 9 que diz para o EditText que a

68

Design

entrada ser texto, essa combinao faz com que o teclado mostre o cone da lupa no lugar da
tecla Enter. A linha 10 configura a cor do texto para branca. Adicionamos a view na ActionBar
com o mtodo ActionBar.setCustomView() e passamos como parmetro a view criada
e os parmetros de layout criados. O mtodo EditText.requestFocus() faz com que
o foco seja dado nova caixa de texto, para que possamos edit-la. Precisamos ainda fazer
com que o teclado abra para que possamos editar a caixa de texto, isso que as linhas 13-14 e
15 esto fazendo. O mtodo getSystemService() obtm a referncia de um servio do
Android, e nesse caso estamos pedindo pelo servio de mtodo de entrada, o teclado. O mtodo
InputMethodManager.showSoftInput() abre o Soft Input, ou seja, o teclado virtual
para edio da view.
Finalmente fazemos com que ao boto de busca no teclado ser clicado, um Toast mostre
para o usurio o contedo da caixa de texto. isso que a interface onEditorActionListener faz com o mtodo onEditorAction().
A inteno desse exemplo mostrar como voc pode adicionar novas views na ActionBar
de forma dinmica.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public boolean onOptionsItemSelected(MenuItem item){


if(item.getItemId() == R.id.action_settings){
startActivity(new Intent(this, SettingsActivity.class));
} else if(item.getItemId() == R.id.action_search){
LayoutParams lp = new LayoutParams
(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
EditText search = new EditText(this);
search.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
search.setTextColor(Color.WHITE);
search.setInputType(InputType.TYPE_CLASS_TEXT);
getActionBar().setCustomView(search, lp);
search.requestFocus();
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(search, InputMethodManager.SHOW_IMPLICIT);
search.setOnEditorActionListener(new OnEditorActionListener() {

17

@Override
public boolean onEditorAction
(TextView v, int actionId, KeyEvent event) {
Toast.makeText
(MainActivity.this, v.getText(), Toast.LENGTH_SHORT).show();
return true;
}
});

18
19
20
21
22
23
24
25

}
return true;

26
27
28

Algoritmo 5.45: Criando a caixa de busca na ActionBar

Design

69

Figura 5.18: Exemplo de busca na ActionBar

70

Design

C APTULO

6
Comunicao

Agora iremos explorar as funcionalidades de comunicao dos aparelhos Android como


acesso a Internet no caso de Tables e Smartphones e envio de SMS e chamadas de voz com os
os Smartphones.

6.1

Internet

Antes de iniciar sua aplicao que faz acesso Internet, devemos dar permisso ao aplicativo atravs do arquivo de Manifest. Logo antes da tag uses-sdk voc deve adicionar a tag
uses-permission, como mostrado abaixo:

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

Algoritmo 6.1: Atribuindo permisso de acesso Internet no Manifest

Nesta seo, veremos como se faz requisies HTTP para obter pginas HTML, sadas de
um script server-side como PHP ou ASP.NET e parsing de respostas JSON ou XML.

6.1.1

HTTP GET

Para fazer uma requisio HTTP GET usaremos as classes da biblioteca Apache, tais como
HttpClient, HttpGet e HttpResponse. Primeiros crie uma classe que iremos chamar de RequestTask e ela deve extender a classe AsyncTask1 , ela vai permitir que faamos a requisio na thread da interface do usurio sem precisar criar e manipular uma thread diferente.

http://developer.android.com/reference/android/os/AsyncTask.html

71

72
1

Comunicao

public class RequestTask extends AsyncTask<URI, Integer, String>{

@Override
protected String doInBackground(URI... uri) {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response;
String responseString = null;

3
4
5
6
7
8

try {
response = httpclient.execute(new HttpGet(uri[0]));
StatusLine statusLine = response.getStatusLine();

9
10
11
12

if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else {
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseString;

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

28
29

Algoritmo 6.2: Classe RequestTask


O mtodo doInBackground() o responsvel por realizar a operao sem o usurio
perceber. Existem outros dois mtodos da classe AsyncTask que podem ser utilizados: onProgressUpdate(), caso queira mostrar o progresso de um download para o usurio e
onPostExecute() para executar alguma ao aps o termino do download.
Observe o algoritmo 6.2, na linha 5-7 criamos um novo HttpClient, HttpResponse e
uma string para armazenar a resposta do servidor. Depois, envolto por um bloco try-catch
temos uma chamada ao mtodo HttpClient.execute() e passado para ele um novo
HttpGet com o parmetro uri[0], uri um dos parmetros do mtodo e contm uma
URI2 que identifica o destino da requisio.
Na linha 11 foi criado um novo StatusLine3 para guardar a resposta da requisio HTTP
que obtemos com o mtodo HttpResponse.getStatusLine(). Depois comparamos o
cdigo do status com HttpStatus.SC_OK (equivalente ao cdigo 200), o status OK significa
que a requisio e a resposta ocorreram como esperado e temos a resposta correta vindo do
servidor.
2
3

http://developer.android.com/reference/java/net/URI.html
http://developer.android.com/reference/org/apache/http/StatusLine.html

Comunicao

73

Em seguita criamos um novo ByteArrayOutputStream que responsvel por guardar


a resposta do servidor na chamada ao mtodo HttpResponse.getEntity().writeTo().
Por ltimo convertemos o byte stream em uma string usando o mtodo toString() e a retornamos.
Agora precisamos de uma Activity para mostrar a resposta, nesse exemplo obteremos o cdigo HTML da pgina do DC UFSCar: http://www.dc.ufscar.br. Mostraremos o cdigo HTML
como uma pgina Web usando o WebView.
1
2
3
4

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

String uri = "http://www.dc.ufscar.br";


AsyncTask<String,String,String> task;
String response = null;

6
7
8
9

try {
task = new RequestTask(this).execute(uri);
response = task.get();
} catch (Exception e) {
e.printStackTrace();
}

10
11
12
13
14
15
16

if(response != null){
WebView webview = new WebView(this);
setContentView(webview);

17
18
19
20

webview.loadData(response, "text/html; charset=UTF-8", null);

21

22
23

Algoritmo 6.3: Usando RequestTask na activity


Primeiro criamos uma nova varivel task do tipo AsyncTask nos moldes da classe que
criamos, dentro de um bloco try-catch criamos uma nova URI e atribumos a ela o endereo da pgina do DC UFSCar. Em seguida iniciamos a varivel task criando uma nova
RequestTask e chamamos o mtodo execute(), que ao ser chamado ir executar o mtodo doInBackground().
Por fim, criamos uma nova WebView4 e carregamos o contedo da string response nela
usando o mtodo WebView.loadData(). A pgina mostrada sem estilo pois no estamos
puxando as folhas de estilo junto, somente o cdigo HTML.
Dica: Usamos a WebView para mostrar o cdigo HTML como pgina apenas para
demonstrao. Caso queira carregar uma pgina corretamente deve fornecer a URL da
pgina diretamente para a WebView.
4

http://developer.android.com/reference/android/webkit/WebView.html

74

6.1.2

Comunicao

HTTP POST

Diferentemente do GET, onde os parmetros para o servidor vo codificados na URL, no


POST os parmetros vo codificados no final do cabealho HTTP. Para enviar uma requisio
do tipo POST, usaremos a classe HttpPost. O algoritmo abaixo reaproveitado da classe
RequestTask e do mtodo doInBackgrond(), onde mudaremos apenas algumas linhas.
Agora estaremos enviando a requisio para essa pgina: http://httpbin.org/post, ela s aceita
requisies do tipo POST, se tentar com GET ir receber uma mensagem de erro.
1
2
3
4
5

try {
HttpPost post = new HttpPost(uri[0]);
List<NameValuePair> nvp = new ArrayList<NameValuePair>();
nvp.add(new BasicNameValuePair("testing", "Post"));
nvp.add(new BasicNameValuePair("user", "You"));

6
7
8
9
10
11

post.setEntity(new UrlEncodedFormEntity(nvp));
response = httpclient.execute(post);
...
...
}

Algoritmo 6.4: Modificando o mtodo para requisies POST


Criamos um novo HttpPost5 passando a URI como parmetro. Criamos uma lista de
tuplas chave-valor que representa a varivel e seu valor no cabealho HTTP e adicionamos
dois valores. Em seguida chamamos o mtodo HttpPost.setEntity() e passamos um
objeto do tipo UrlEncodedFormEntity6 que recebe a lista como parmetro, esse objeto
ir codificar a lista em variveis aceitas pelo padro de uma requisio POST.
Por final, executamos a requisio como feito anteriormente. O resto do cdigo igual.
Note que se colocar a resposta dessa requisio em uma WebView ou TextView ir observar que est codificado no formato JSON7 .

6.1.3

Decodificando JSON

Vamos obter dados do objeto JSON retornado pelo exemplo anterior. Se voc observar a
string na tela, ir perceber os dados que foram enviado via POST dentro de um objeto JSON
chamado form. Queremos obter esses dados, para isso precisamos criar uma classe que ser
responsvel por obter especificamente esses dados do form.

Dica: Em JSON, { representa um objeto JSON e [ representa um array dentro de um


objeto JSON

Crie uma classe JSONParser, como mostrado abaixo.


5

http://developer.android.com/reference/org/apache/http/client/methods/HttpPost.html
http://developer.android.com/reference/org/apache/http/client/entity/UrlEncodedFormEntity.html
7
O que JSON: http://www.json.org/
6

6
1

Comunicao

75

public class JSONParser {

public String getFormData(String jsonstr){


String formData = null;

3
4
5

try {
JSONObject jObj = new JSONObject(jsonstr);
JSONObject form = jObj.getJSONObject("form");
formData = form.getString("testing");
formData += "\n" + form.getString("user");

6
7
8
9
10
11

} catch (JSONException e) {
e.printStackTrace();
}

12
13
14
15

return formData;

16

17
18

Algoritmo 6.5: Classe JSONParser

Para obter dados do form, criamos um mtodo getFormData(). Primeiro obtemos o


objeto JSON dado pela string retornada pela requisio POST do exemplo anterior ao criar
um novo JSONObject e passando a string como parmetro. Em seguida queremos obter outro objeto JSON, aquele cujo nome form, para isso chamamos o mtodo JSONObject.getJSONObject() passando o nome do objeto junto. Aps obter o objeto desejado,
usamos o mtodo JSONObject.getString() e passamos o nome do valor para obter o valor. Ento ao passar "testing" e "user" esperamos como retorno POST e You, respectivamente.

6.1.4

Codificando JSON

Podemos tambm codificar objetos JSON, e simples. Basta criar um JSONObject ou


JSONArray e usar o mtodo toString(). Por exemplo:

1
2
3
4
5
6
7
8
9
10

public void writeJSON() {


JSONObject object = new JSONObject();
try {
object.put("name", "Matheus");
object.put("age", new Integer(22));
object.put("university", "UFSCar");
} catch (JSONException e) {
e.printStackTrace();
}
}

Algoritmo 6.6: Criando JSON

76

6.2

Comunicao

Telefone

possvel fazer uma chamada telefnica, porm voc ter que usar o discador padro do
Android uma vez que ele no prov uma API pblica para fazer chamadas diretamente pelo
seu aplicativo. A nica forma atravs do interemediador ACTION_CALL. O Exemplo abaixo
mostra como fazer uma chamada usando esse intermediador.
1
2
3
4

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

Button call = (Button) findViewById(R.id.button1);


call.setOnClickListener(new OnClickListener() {

6
7
8

@Override
public void onClick(View v) {
EditText tv = (EditText) findViewById(R.id.editNumber);
String phone = tv.getText().toString();

9
10
11
12
13

Intent callIntent = new Intent(Intent.ACTION_CALL);


callIntent.setData(Uri.parse("tel:" + phone));
startActivity(callIntent);

14
15
16

}
});

17
18
19

Algoritmo 6.7: Fazendo uma chamada telefnica


Para esse exemplo, foi criado uma caixa de texto e um boto, o nmero de telefone colocado na caixa de texto e o boto Call inicia a activity responsvel por realizar a chamada.
Observe que necessrio fazer um parse do nmero de telefone para uma URI com formato
tel:0123456789. Cria-se uma Intent para o ACTION_CALL, coloca-se os dados na
Intent e chama o mtodo startActivity() para fazer a chamada telefnica. preciso
dar permisso ao aplicativo para fazer chamadas adicionando essa linha ao Manifest.
1

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

Algoritmo 6.8: Permisso para fazer chamadas telefnicas


Usando a classe TelephonyManager8 , no entanto, o Android nos d a possibilidade
obter dados do Sim Card e tambm um listener para saber o status atual do telefone, se ele est
fazendo uma ligao ou no.
Voc pode usar os mtodos getDeviceId() para obter o nmero IMEI do aparelho,
getSimSerialNumber() para obter o nmero de srie do Sim Card, isNetworkRoaming() para saber se est em modo roaming, etc.
8

http://developer.android.com/reference/android/telephony/TelephonyManager.html

Comunicao

6.3

77

Short Message Service (SMS)

O Android prov uma API pblica para mandar mensagens SMS, atravs da classe SMSManager9 . Assim como fazer ligaes, enviar SMS tambm precisa configurar a permisso
no Manifest.

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

Algoritmo 6.9: Permisso para enviar mensagens SMS

Agora vamos criar um aplicativo simples que chama a classe SMSManager para enviar
uma mensagem para um nmero de telefone. Teremos duas EditText views para abrigar a
mensagem e o nmero, e um boto para enviar. Inicialmente crie um mtodo sendSMS().

1
2
3
4
5
6
7
8
9
10

private void sendSMS(String message, String phone){


try{
SmsManager smsMan = SmsManager.getDefault();
smsMan.sendTextMessage(phone, null, message, null, null);
} catch(IllegalArgumentException e) {
e.printStackTrace();
Toast.makeText(this, "Error sending message", Toast.LENGTH_SHORT)
.show();
}
}

Algoritmo 6.10: Mtodo sendSMS()

Dentro de um bloco try-catch crie um novo SMSManager como mostrado na linha


3 do algoritmo 6.11, use o mtodo SmsManager.sendTextMessage() para enviar uma
mensagem. O primeiro parmetro o nmero de telefone para qual a mensagem ser enviada,
o segundo o telefone de origem, colocando null o valor ser o nmero do prprio aparelho
ou servio, o terceiro parmetro o texto da mensagem.
Completanto, com um algoritmo simples para chamar o mtodo quando o boto for pressionado.

http://developer.android.com/reference/android/telephony/SmsManager.html

78
1
2
3
4

Comunicao

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

Button btnSend = (Button) findViewById(R.id.buttonSend);


btnSend.setOnClickListener(new OnClickListener() {

6
7
8

@Override
public void onClick(View v) {
EditText editMsg = (EditText) findViewById(R.id.editMessage);
String message = editMsg.getText().toString();
EditText editPhone = (EditText) findViewById(R.id.editPhone);
String phone = editPhone.getText().toString();

9
10
11
12
13
14
15

sendSMS(message, phone);

16
17

//Clean text fields and show toast


editMsg.setText("");
editPhone.setText("");
Toast.makeText
(getApplicationContext(), "Message sent", Toast.LENGTH_SHORT)
.show();

18
19
20
21
22
23

}
});

24
25
26

Algoritmo 6.11: Chamando mtodo sendSMS()

C APTULO

7
Armazenamento

O Android prov algumas opes para que voc possa salvar dados persistentes de sua aplicao. A escolha da opo de armazenamento vai depender das necessidades da sua aplicao,
isto , se os dados vo ser visveis somente pela aplicao ou se o usurio ter acesso a eles,
quanto de espao ser necessario, que tipo de dados voc pretende salvar. As opes so:
Shared Preferences: Usada principalmente para salvar preferncias do usurio, esse mtodo guarda primitivas em duplas chave-valor;
Armazenamento Interno: Salva dados privados na memria do dispositivo;
Armazenamento Externo: Salva dados pblicos na memria externa compartilhada;
Banco de Dados SQLite: Salva dados estruturados em um banco de dados privado.
Conexo com a rede: Salva dados na web em seu prprio servidor na rede.

7.1

Shared Preferences:

A classe SharedPreferences1 fornece um framework que te permite salvar e recuperar


dados primitivos persistentes no formato chave-valor. Voc pode salvar qualquer tipo de dado
primitivo: booleans, floats, inteiros, longs e strings.
Voc pode usar essa classe para armazenar dados durante o ciclo de vida de uma activity.
Isto , antes da activity ser destruda na funo onDestroy(), os dados podem ser salvos
durante a execuo de onStop() e recuperados na execuo de onCreate().
Para usar a SharedPreferences na sua aplicao voc deve chamar getSharedPreferences() se precisar de vrios arquivos de preferencias que sero identificados pelo
nome ou getPreferences() se precisar de apenas um arquivo para sua activity, esse no
necessita de nome pois ser nico para a activity. Em seguida para escrever valores voc deve
chamar o mtodo edit() para obter um SharedPreferences.Editor, e usar os mtodos como putString() para adicionar novos valores. Por ltimo commit() deve ser
1

http://developer.android.com/reference/android/content/SharedPreferences.html

79

80

Armazenamento

chamado para salvar os valores. Para ler os valores, voc deve chamar mtodos como getBoolean() ou getString().
O exemplo abaixo salva os dados de uma caixa de texto, uma barra de progresso e uma
chave em uma SharedPreferences e depois os l quando o aplicativo aberto novamente.
1
2
3
4

public class MainActivity extends Activity {


private EditText mEditText;
private SeekBar mSeekBar;
private Switch mSwitch;

5
6
7
8
9

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

10

mEditText = (EditText) findViewById(R.id.editText1);


mSeekBar = (SeekBar) findViewById(R.id.seekBar1);
mSwitch = (Switch) findViewById(R.id.switch1);

11
12
13
14

SharedPreferences prefs = getPreferences(MODE_PRIVATE);


String text = prefs.getString("text", "");
int progress = prefs.getInt("seek", 0);
boolean checked = prefs.getBoolean("switch", false);

15
16
17
18
19

mEditText.setText(text);
mSeekBar.setProgress(progress);
mSwitch.setChecked(checked);

20
21
22
23

24
25
26
27

@Override
protected void onStop() {
super.onStop();

28

SharedPreferences prefs = getPreferences(MODE_PRIVATE);


SharedPreferences.Editor editor = prefs.edit();

29
30
31

editor.putString("text", mEditText.getText().toString());
editor.putInt("seek", mSeekBar.getProgress());
editor.putBoolean("switch", mSwitch.isChecked());
editor.commit();

32
33
34
35
36
37
38

}
...
}

Algoritmo 7.1: Utilizando SharedPreferences para salvar dados primitivos


Observe no algoritmo 7.1 a utilizao da classe SharedPreferences, no mtodo onStop() chamamos o mtodo getPreferences(MODE_PRIVATE) para obter uma instncia da classe no modo privado, isto , os dados somente so acessveis por esta activity deste

Armazenamento

81

aplicativo. Na linha 30 obtemos o SharedPreferences.Editor ao chamar o mtodo


SharedPreferences.edit().
O Editor usado ento para adicionar os valores que obtemos das views. Na linha 32
usamos putString() para guardar o contedo da caixa de texto, putInt() para guardar a
posio da SeekBar e putBoolean() para guardar o estado da chave. Finalmente invocamos commit() para salvar os dados.
No mtodo onCreate() usamos os mtodos get para obter os dados que foram salvos.
Esses mtodos requerem a passagem de um valor default para quando no h dados previamente
salvos. Observe ento nas linhas 16-18 que para a caixa de texto usamos uma string vazia, para
a SeekBar o valor 0 e a chave desligada.

7.2

Armazenamento interno

Voc pode salvar arquivos diretamente na memria interna do dispositivo. Por padro os
arquivos salvos no armazenamento interno so privados a seu aplicativo e no podem ser acessados por outros aplicativos.
Para criar um novo arquivo voc deve invocar o mtodo openFileOutput() passando o
nome do arquivo e o modo de operao. Esse mtodo ir retornar um FileOutputStream.
Voc poder escrever nesse arquivo chamando o mtodo FileOutputStream.write() e
FileOutputStream.close() para fechar o arquivo.
Para exemplificar, faremos um aplicativo do tipo bloco de notas. Ser composto de dois
botes, um para abrir um arquivo e outro para salvar um arquivo, e uma caixa de texto para
escrever. Tudo ser feito no mtodo onCreate(), primeiro usamos findViewById()
para obter as views. O boto de salvar ir abrir um alerta perguntando o nome do arquivo, e o
boto de abrir mostrar um alerta permitindo ao usurio escolher dentre os arquivos j salvos
anteriormente.
No cdigo abaixo criamos o listener do boto de salvar, dentro dele criamos um alerta e
precisamos de um listener para confirmar e um para cancelar.
1

saveBtn.setOnClickListener(new OnClickListener() {

2
3
4

@Override
public void onClick(View v) {

5
6
7
8
9
10
11
12

AlertDialog.Builder builder =
new AlertDialog.Builder(MainActivity.this);
LayoutInflater inflater = getLayoutInflater();
final View fnameEntry =
inflater.inflate(R.layout.save_dialog, null);
builder.setView(fnameEntry).setTitle("Save as...")
.setPositiveButton("Save", new DialogInterface.OnClickListener(){

13
14

...

Algoritmo 7.2: Passos iniciais do listener, criando um alerta.

82

Armazenamento

Com o AlertDialog.Builder comeamos a construir um novo alerta. Usamos o


LayoutInflater para carregar o layout com uma EditText. Chamamos setPositivoButton() para criar um listener que ir salvar o arquivo quando o boto for clicado.

1
2

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

3
4
5
6
7
8
9
10

@Override
public void onClick(DialogInterface dialog, int which) {
EditText fnameEt = (EditText)fnameEntry.findViewById(R.id.saveas);
String fname = fnameEt.getText().toString();
if(fname.isEmpty())
fname = "untitled";
String text = textEt.getText().toString();

11
12
13
14
15
16
17
18
19
20

try {
FileOutputStream fos = openFileOutput(fname, MODE_PRIVATE);
fos.write(text.getBytes());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

21
22
23
24
25
26

Toast.makeText(getApplicationContext(),
fname + " saved", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
...

Algoritmo 7.3: Salvando um arquivo e mostrando um Toast

Vamos analisar o algoritmo 7.3. Na linha 5 queremos a referncia ao EditText contido no


alerta, para isso precisamos usar a varivel fnameEntry criada ao chamar o mtodo LayoutInflater.inflate(). Depois, na linha 7-8 certificamos que o nome do arquivo no ser
vazio (se for vazio, o arquivo no salvo), para isso atribuimos o nome untitled a ele. Obtemos
tambm o texto escrito na caixa de texto da activity na linha 9.
Agora que j temos o nome e os dados do arquivo, precisamos efetivamente salv-lo no armazenamento interno. Com o mtodo openFileOutput() teremos um FileOutputStream que servir para escrever os dados no arquivo. Isso feito nas linhas 13-14 com o
mtodo write() e em seguida o mtodo close(). Para certificar ao usurio que o arquivo
foi salvo, mostramos um Toast com a mensagem que o arquivo foi salvo.
Se o usurio clicar em cancel no alerta, precisamos fech-lo, basta invocar dialog.close()
no listener.

7
1
2

Armazenamento

83

...
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener(){

3
4
5
6
7
8
9

@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
...

Algoritmo 7.4: Fechando o alerta ao clicar em close

Depois chame builder.create().show() no listener do boto Save para mostrar o alerta.


Para o boto Open iremos mostrar um alerta com os nomes dos arquivos salvos anteriormente, quando um desses for clicado o arquivo se abrir preenchendo a caixa de texto.

openBtn.setOnClickListener(new OnClickListener() {

2
3
4

@Override
public void onClick(View v) {

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

6
7
8

builder.setTitle("Choose a File")
.setItems(fileList(), new DialogInterface.OnClickListener() {

9
10
11

...

12

});

13
14

builder.create().show();

15
16
17

}
});

Algoritmo 7.5: Criando um alerta com os arquivos salvos

Dessa vez usamos o mtodo builder.setItems() para construir uma lista no alerta.
O mtodo fileList() retorna um array de nomes de arquivo que foram armazenados
internalmente no aplicativo.

84
1
2

Armazenamento

...
.setItems(fileList(), new DialogInterface.OnClickListener() {

3
4
5

@Override
public void onClick(DialogInterface dialog, int which) {

try {
File f = getFilesDir().listFiles()[which];
BufferedReader in = new BufferedReader(new FileReader(f));
StringBuilder text = new StringBuilder();

7
8
9
10
11

try{
String line = null;
while ((line = in.readLine()) != null){
text.append(line);
text.append(System.getProperty("line.separator"));
}
} finally {
in.close();
}

12
13
14
15
16
17
18
19
20
21

textEt.setText(text);
in.close();
} catch(FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

22
23
24
25
26
27
28
29
30
31

}
});
...

Algoritmo 7.6: Criando um alerta com os arquivos salvos


Na linha 7 do algoritmo 7.6 queremos obter a referncia do arquivo selecionado. Chamando
o mtodo getFilesDir() nos fornecido o diretrio contendo os arquivos, com listFiles() temos um array de Files e o selecionamos com a varivel which, que a posio
clicada na lista do alerta. Para ler os dados desse arquivo, usamos a classe BufferedReader
e guardamos as linhas sendo lidas em um objeto do tipo StringBuilder. Nas linhas 13-16
lemos cada linha do arquivo em um loop while e usamos o mtodo append() duas vezes, uma
para adicionar a linha lida na string e outra para adicionar o separador de linha (comummente
\n). Ao fim invocamos close() para fechar o BufferedReader. E colocamos o texto lido
de volta na caixa de texto.
Dica: Voc tambm pode optar por salvar seus arquivos no cache, isto , na pasta cache
da sua aplicao. Esses arquivos so apagados automaticamente quando o sistema necessita
de memria. Para isso use o mtodo getCacheDir() para obter o caminho do diretrio
de cache.

Armazenamento

7.3

85

Armazenamento Externo

Todo dispositivo Android suporta um armazenamento externo que compartilhado e que


pode ser usado para salvar arquivos. Esse armazenamento pode ser tanto um carto SD que esta
conectado ao aparelho como a prpria memria interna dele. Os arquivos salvos no armazenamento externo podem ser lidos por todos outros aplicativos e modificados quando o usurio
conecta ao computador para transferir os arquivos via USB.
Se um dispositivo usa uma partio da memria interna do aparelho como armazenamento
externo e ainda oferecer um carto SD, ento a partio do carto SD no estar disponvel para
armazenamento, sua aplicao no conseguir acessar o carto SD.
Por isso, o armazenamento externo pode ficar indisponvel caso o usurio monte o armazenamento externo no computador ou remova o carto SD. Os arquivos so abertos e podem ser
abertos, modificados ou removidos pelo usurio ou por outras aplicaes.
Para poder usar o armazenamento externo, primeiro voc deve verificar sua disponibilidade
usando o mtodo getExternalStorageState(). A mdia pode estar conectada a um
computador, faltando ou em estado de apenas leitura. Voc pode verificar a disponibilidade da
mdia como mostrado no exemplo abaixo, retirado da documentao oficial2 .
1
2
3

boolean mExternalStorageAvailable = false;


boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();

4
5
6
7
8
9
10
11
12

if (Environment.MEDIA_MOUNTED.equals(state)) {
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
mExternalStorageAvailable = mExternalStorageWriteable = false;
}

Algoritmo 7.7: Verificando se o armazenamento externo est disponvel


O algoritmo 7.7 verifica se o armazenamento externo est disponvel. Temos duas variveis
de controle que guardaro o estado da mdia,mExternalStorageAvailable e mExternalStorageWriteable. A primeira nos diz se o armazenamento externo est disponvel
e a segunda se possvel escrever nele. Na varivel state guardado o estado que obtido
atravs do mtodo getExternalStorageState(). Uma comparao feita com o resultado obtido, se for equivalente Environment.MEDIA_MOUNTED ento sabemos que a
mdia est montada e possvel escrever nela, ento setamos os estados das variveis de controle
para true. Caso o resultado seja equivalente Environment.MEDIA_MOUNTED_READ_ONLY ento sabemos que a mdia est montada porm est somente com permisso de leitura,
nesse caso setamos apenas a varivel de disponibilidade como true e mantemos a outra como
false. Se o resultado no for esses dois estados, mas sim algum outro estado possvel, ento
setamos as duas variveis para false.
Aps verificar a disponibilidade, voc deve usar o mtodo getExternalFilesDir()
para obter uma referncia ao caminho do diretrio onde voc deve salvar seus arquivos. Esse
2

http://developer.android.com/guide/topics/data/data-storage.html#filesExternal

86

Armazenamento

mtodo recebe um parmetro que especifica o tipo de subdiretrio que voc quer usar, tais como
DIRECTORY_MUSIC e DIRECTORY_RINGTONES, ou passe null para obter a raz dos diretrios da sua aplicao. O mtodo ir criar o diretrio apropriado se necessrio. Ao especificar
o tipo, voc se assegura que o Android ir categorizar seus arquivos de forma apropriada. Se o
usurio desinstalar seu aplicativo, todos os dados sero deletados.
Se voc quer salvar arquivos que no so especficos da sua aplicao e que no devem ser
deletados quando desinstalado, salve-os em um dos diretrios pblicos que esto no raz da
mdia de armazenamento externo. So eles os diretrios Music/, Pictures/, etc. Use o
mtodo getExternalStoragePublicDirectory() passando o tipo de diretrio desejado, da mesma forma que anteriormente.
O sistema classifica os arquivos encontrados nessas pastas na forma:

7.4

Music/ - Msicas do usurio;


Podcasts/ - podcasts;
Ringtones/ - Toques;
Alarms/ - Toques do despertador;
Notifications/ - Toques das notificaes;
Pictures/ - Fotos (exceto aquelas tiradas com a cmera);
Movies/ - Filmes (exceto aquelas gravadas com a cmera); e
Download/ - Arquivos baixados.

Banco de dados

O Android fornece suporte ao bancos de dados SQLite. Qualquer banco que voc criar
ser acessvel apenas pela sua aplicao e no fora dela.
A maneira recomendada de criar um banco de dados criando uma subclasse da classe
SQLiteOpenHelper e sobrescrever o mtodo onCreate(). Para ler e escrever no banco,
chame os mtodos getReadableDatabase() e getWritableDatabase(). Ambas
retornam um objeto do tipo SQLiteDatabase que fornece mtodos para operar sobre o
banco.
Usando o mtodo SQLiteDatabase.query() podemos realizar uma consulta, o mtodo aceita vrios parmetros, tais como: tabela, seleo, colunas, agrupamento e etc. Para
consultas mais complexas voc pode usar a classe SQLiteQueryBuilder que contm vrios mtodos para construir consultas.
Toda consulta vai retornar um Cursor que aponta para as tuplas do resultado da consulta.
Voc dever us-lo para navegar atravs dos resultados.
Para exemplificar, vamos fazer um mecanismo simples de busca. O banco ser populado
com fabricantes de carros e alguns de seus modelos. Para o design desse exemplo, optei por
ter dois Spinners (equivalente ao Combo Box) e um boto. Os Spinners sero populados com
os dados do Banco de Dados de forma que o primeiro contenha todos os fabricantes de carros
e o segundo todos os anos de fabricao de seus modelos, esses dados sero obtidos atravs de
consultas ao banco.
Para comear, devemos criar o BD e isso feito com uma classe que iremos criar que ir ser
subclasse de SQLiteOpenHelper 3 . Chamaremos de CarOpenHelper. Essa classe tem
algumas funes bsicas, como criar e popular o BD, configurar a ao a ser tomada quando o
BD atualizado ou desatualizado.
3

http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html

Armazenamento

87

O cdigo 7.8 abaixo demonstra a criao do BD usado no exemplo:


1
2
3
4
5
6
7
8
9
10

public class CarOpenHelper extends SQLiteOpenHelper {


private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "MyCars";
private static final String DATABASE_TABLE_NAME = "Cars";
private static final String DATABASE_TABLE_CREATE =
"CREATE TABLE " + DATABASE_TABLE_NAME + " (" +
"SN INT PRIMARY KEY, " +
"Manufacturer TEXT, " +
"Model TEXT, " +
"Year INT);";

11

public CarOpenHelper(Context context) {


super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

12
13
14
15

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_TABLE_CREATE);

16
17
18
19

db.execSQL( "INSERT INTO " + DATABASE_TABLE_NAME +


" SELECT 1 AS SN, Mazda AS Manufacturer," +
" MX-5 AS Model, 1991 AS Year" +
" UNION SELECT 2, Mazda, RX-8, 2001" +
" UNION SELECT 3, Mazda, Speed3, 2007" +
" UNION SELECT 4, Subaru, Impreza, 2010" +
" UNION SELECT 5, Fiat, 500, 2012" +
" UNION SELECT 6, Ford, Focus, 2008" +
" UNION SELECT 7, Fiat, Punto, 2012" +
" UNION SELECT 8, Ford, Fiesta, 2006" +
" UNION SELECT 9, Honda, Civic, 2013" +
" UNION SELECT 10, Honda, Fit, 2010" +
" UNION SELECT 11, Toyota, Corolla, 2010" +
" UNION SELECT 12, Chevrolet, Celta, 2009" +
" UNION SELECT 13, Chevrolet, Cruze, 2012");

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

36
37

Algoritmo 7.8: Classe CarOpenHelper do SQLite


Primeiro criamos algumas Strings para ajudar na criao do banco. Em seguida, criamos o
construtor da classe, os argumentos passados so: o contexto da activity que instanciou a classe,
o nome do banco, um CursorFactory caso esteja usando (no o caso), e a verso do banco
que comea em 1 e deve incrementar a medida que voc o atualiza.
O mtodo onCreate() do banco responsvel pela criao do banco e de o popular.
Fazemos isso executando consultas SQL diretamente ao banco com o mtodo execSQL().
No cdigo 7.8 voc pode observar a chamada de execSQL() duas vezes. No se confunda

88

Armazenamento

com a segunda consulta, que adiciona as tuplas ao banco, s uma maneira de inserir vrias
tuplas em uma nica consulta.
Agora na nossa activity precisamos realizar as consultas para popular os Spinners. No mtodo onCreate() faa:

1
2
3

@Override
protected void onCreate(Bundle savedInstanceBundle){
...

4
5
6

mDatabase = new CarOpenHelper(this);


String[] mColumns = {"Manufacturer", "Year"};

7
8
9
10

SQLiteDatabase mSQLite = mDatabase.getReadableDatabase();


Cursor cursor =
mSQLite.query("Cars", mColumns, null, null, null, null, null);

11
12
13
14
15
16
17
18
19

Set<String> set1 = new HashSet<String>();


Set<String> set2 = new HashSet<String>();
while(cursor.moveToNext()){
set1.add(cursor.getString(0));
set2.add(cursor.getString(1));
}
List<String> list1 = new ArrayList<String>(set1);
List<String> list2 = new ArrayList<String>(set2);

20
21
22
23
24
25
26
27

ArrayAdapter<String> manuAdapter =
new ArrayAdapter<String>
(this, android.R.layout.simple_spinner_item, list1);
manuAdapter.
setDropDownViewResource
(android.R.layout.simple_spinner_dropdown_item);
mSpManufacturer.setAdapter(manuAdapter);

28
29
30
31
32
33
34
35

ArrayAdapter<String> yearAdapter =
new ArrayAdapter<String>
(this, android.R.layout.simple_spinner_item, list2);
manuAdapter.
setDropDownViewResource
(android.R.layout.simple_spinner_dropdown_item);
mSpYear.setAdapter(yearAdapter);

Algoritmo 7.9: Usando o CarOpenHelper na activity

Estou "pulando"a parte de obter as referncias dos spinners e botes e partindo para o que
interessa no cdigo 7.9. Na linha 5 instanciamos o CarOpenHelper na varivel mDatabase. Na linha 6 criamos um array que contm as colunas que iremos fazer consulta, isto
necessrio para o mtodo query() na linha 10.
Ao chamar mDatabase.getReadableDatabase() obtemos um objeto do tipo SQ-

Armazenamento

89

LiteDatabase 4 mas que s permite a leitura. J o mtodo query() retorna um Cursor


que usado para iterar sobre os resultados. Na linha 10 observe os parmetros que usamos para
fazer a consulta: A tabela que vamos fazer a consulta, um array de strings com as colunas que
queremos obter, uma clusula WHERE (observe que passar null aqui implica em retornar todas
as tuplas da coluna), argumentos da seleo (para aqueles que conhecem prepared statements
onde "?" substituido pelos valores, uma clusula GROUP BY (null significa no agrupar), uma
clusula HAVING, uma clusula ORDER BY e uma clusula LIMIT.
Em seguida criamos dois Set para armazenar os resultados e omitir os repetidos. Usamos
o Cursor para iterar sobre o resultado e adicion-los aos conjuntos. O set1 contm os
fabricantes e o set2 contm os anos de fabricao. Usamos Cursor.getString() para
obter o elemento do resultado que est naquele ndice, nesse caso o ndice 0 a coluna de
fabricantes e o ndice 1 a coluna de anos de fabricao. Com esses dois conjuntos em mo
podemos criar duas ArrayList para serem usadas com os ArrayAdapter que iro popular
os Spinners.
Depois necessrio criar dois listeners para os elementos dos Spinners que guarda numa
varivel o fabricante e o ano de fabricao selecionados (aqui irei omitir cdigo, mas pode ser
obtido no repositrio). E um listener para o boto que ir chamar o mtodo openCarList().

1
2
3
4
5
6
7

public void openCarList(){


Intent intent = new Intent(this, CarDetailActivity.class);
List<String> list = fetchCarList();
String[] carList = list.toArray(new String[list.size()]);
intent.putExtra(CARLIST, carList);
startActivity(intent);
}

Algoritmo 7.10: Mtodo openCarList()

Esse mtodo responsvel por obter a lista de carros da seleo feita nos Spinners e mandar
essa lista para outra activity. O mtodo fetchCarList() que ir fazer a chamada ao BD,
da mesma forma que foi feito anteriormente, mas dessa vez estamos obtendo a coluna Model
do banco com WHERE sendo os parmetros obtidos.

http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html

90
1
2
3
4
5
6
7

Armazenamento

public List<String> fetchCarList(){


SQLiteDatabase mSQLite = mDatabase.getReadableDatabase();
String[] column = {"Model"};
Cursor cursor =
mSQLite.query("Cars", column,
"Year="+selYear+" AND Manufacturer="+selManufacturer + "",
null, null, null, null, null);

8
9
10
11
12

List<String> list1 = new ArrayList<String>();


while(cursor.moveToNext()){
list1.add(cursor.getString(0));
}

13
14
15

return list1;
}

Algoritmo 7.11: Mtodo fetchCarList()


O resultado de fetchCarList() enviado para outra activity que ir mostrar os modelos
em forma de uma lista.
Nesse captulo pudemos aprender apenas o bsico de acesso ao banco de dados SQLite,
um assunto que tem muito mais o que aprender e recomendo olhar a documentao para futuras
consultas.

C APTULO

8
Cmera

O Android fornece um framework para acesso s diferentes cmeras e funcionalidades dessas cmeras disponveis no dispositivo. possvel tirar fotos e gravar vdeos nos aplicativos.
H duas formas de acessar a cmera, a primeira sendo pela API e a segunda pelo Intent da
cmera. Nesse captulo iremos discutir brevemente sobre como usar a API e um exemplo de
chamar o aplicativo padro da cmera atravs de um Intent.

8.1

Usando a API

Para usar a API da cmera, primeiro devemos requisitar a permisso para usar a cmera no
Manifest.
1

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

2
3

<uses-feature android:name="android.hardware.camera" />

Algoritmo 8.1: Requisitando permisso para usar a cmera


Voc pode ainda requisitar outras funcionalidades como flash, foco automtico, usar a cmera da frente ao requisitar outras features usando <uses-feature>. A lista das funcionalidades est presente na documentao1 .
Se voc for guardar as fotos no armazenamento externo, precisa requisitar a permisso para
escrita:
1
2

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

Algoritmo 8.2: Requisitando permisso para gravar no armazenamento externo


1

http://developer.android.com/guide/topics/manifest/uses-feature-element.html#hw-features

91

92

Cmera

Se for gravar udio quando estiver gravando vdeo, precisa requisitar a permisso para gravar
udio:

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

Algoritmo 8.3: Requisitando permisso para gravar udio

Segundo a documentao2 existem alguns passos gerais que so seguidos para criar um
aplicao usando a API da cmera. So eles:

Detectar e acessar a cmera: Cdigo que verifica a existncia de uma cmera e requisita
o acesso.

Criar uma classe de pr-visualizao: Criar uma classe que extende SurfaceView e
implementa SurfaceHolder para mostrar na tela o que a cmera est vendo.

Construir o layout da pr-visualizao: Com a classe pronta, crie uma view que ir
incorporar a classe de pr-visualizao e mostrar os controles da interface.

Configurar os listeners para a captura: Criar listeners para os botes da interface para
gravar a imagem.

Capturar e salvar os arquivos: Criar o cdigo que ir capturar a imagem e salvar no


armazenamento.

Liberar a cmera: Depois de usar a cmera, a aplicao deve liberar o uso para outras
aplicaes.

8.1.1

Acessando a cmera

Primeiro vamos criar uma classe CameraAccess que ir requisitar o acesso cmera.
A classe ficar responsvel apenas por verificar se o dispositivo tem cmera e por instanciar a
cmera para o uso.

http://developer.android.com/guide/topics/media/camera.html

8
1
2
3

Cmera

93

public class CameraAccess {


Context c;
Camera cam;

public CameraAccess(Context c){


this.c = c;
cam = null;
}

5
6
7
8
9

private boolean checkCameraHardware(){


if(c.getPackageManager().
hasSystemFeature(PackageManager.FEATURE_CAMERA))
return true;
return false;
}

10
11
12
13
14
15
16

public Camera getCameraInstance(){


Camera cam = null;
if(checkCameraHardware()){
try{
cam = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
} else {
return null;
}

17
18
19
20
21
22
23
24
25
26
27
28

return cam;

29

30
31

public void releaseCamera(){


cam.release();
}

32
33
34
35

Algoritmo 8.4: Classe CameraAccess

8.1.2

Pr-visualizao

Em seguida iremos criar a classe da visualizao da cmera. Essa classe extende SurfaceView e implementa a interface SurfaceHolder.CallBack, iremos chamar essa classe
de CameraPreview. No cdigo 8.5 abaixo comeamos a implementar essa classe. Precisamos de duas variveis SurfaceHolder e Camera. O construtor recebe o contexto e
a cmera previamente instanciada como parmetro. Obtemos o SurfaceHolder usando o
mtodo getHolder(), adicionamos a classe como callback. Por ltimo, na linha 11 configuramos o tipo do SurfaceHolder como SURFACE_TYPE_PUSH_BUFFERS isso j
depredado mas necessrio para acessar a cmera em verses do Android mais antigas que 3.0.

94
1
2
3

Cmera

public class CameraPreview extends SurfaceView implements Callback {


private SurfaceHolder mHolder;
private Camera mCamera;

public CameraPreview(Context c, Camera cam) {


super(c);
mCamera = cam;

5
6
7
8

mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

9
10
11

12
13

@Override
public void surfaceCreated(SurfaceHolder holder) {
try{
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("CAM", "Error setting camera preview: " + e.getMessage());
}
}

14
15
16
17
18
19
20
21
22
23

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
if(mHolder.getSurface() == null){
return;
}

24
25
26
27
28
29
30

try {
mCamera.stopPreview();
} catch (Exception e) {
// Irrelevante
}

31
32
33
34
35
36

try{
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d("CAM", "Error setting camera preview: " + e.getMessage());
}

37
38
39
40
41
42

43
44

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Nada, tomar conta de liberar a camera na activity
}

45
46
47
48
49

Algoritmo 8.5: Classe CameraPreview

Cmera

95

Continuando a analisar o algoritmo 8.5. Precisamos sobrecarregar os mtodos da interface.


Esses so: surfaceCreated(), surfaceChanged() e surfaceDestroyed(). O
primeiro, surfaceCreated() responsvel por instanciar a pr-visualizao. Para isso
chame o mtodo setPreviewDisplay() passando o holder como parmetro para a cmera saber onde a visualizao ser desenhada. Depois s chamar startPreview() para
iniciar a visualizao.
O segundo, surfaceChanged() serve para parar e recomear a visualizao quando o
usurio rotaciona a tela, por exemplo. Para isso, necessrio fechar a visualizao e depois
iniciar novamente. Na linha 27 testamos para saber se o mHolder no tem nada desenhado, se
for o caso ento apenas retorne do mtodo pois no h nada para ser fechado. Caso contrrio
ento precisamos parar a visualizao com stopPreview() e depois iniciar novamente da
mesma forma como feito no mtodo surfaceCreated().
O terceiro, surfaceDestroyed() irrelevante nesse caso pois estaremos tomando
conta de liberar a cmera na activity e no nessa classe.

8.1.3 Layout e Activity da visualizao


A classe da visualizao precisa ser instanciada por uma activity que vai efetivamente mostrar o contedo. Para o layout da activity iremos criar um FrameLayout que ir conter a
visualizao e um boto pra capturar a imagem.

1
2
3
4
5

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


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

6
7
8
9
10
11

<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />

12
13
14
15
16
17
18
19

<Button
android:id="@+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>

Algoritmo 8.6: Layout da Activity que ir conter a visualizao

Voc deve tambm especificar a orientao da activity como landscape no Manifest. Como
mostrado na linha 5 do cdigo 8.7. Apesar disso no ser obrigatrio, normal que tiremos fotos
no modo paisagem e no retrato, mas isso no regra.

96
1
2
3
4
5
6

Cmera

...
<activity
android:name="com.example.camera1.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" >
...

Algoritmo 8.7: Configurando a orientao da activity no Manifest

8.1.4

Criando a activity

Para comear vamos apenas fazer com que a activity mostre a visualizao da cmera na
tela usando as classes que criamos anteriormente.
1
2
3

public class CameraActivity extends Activity {


private Camera mCam;
private CameraPreview mPreview;

4
5
6
7
8

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

CameraAccess ca = new CameraAccess(this);


mCam = ca.getCameraInstance();

10
11
12

mPreview = new CameraPreview(this, mCam);


FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);

13
14
15
16

Algoritmo 8.8: Primeira parte da classe CameraActivity


Na linha 10 criamos uma instncia da classe CameraAccess e requisitamos o acesso a
cmera usando getCameraInstance(). Depois criamos um CameraPreview e adicionamos o preview ao FrameLayout. Nesse ponto, se o aplicativo for executado ir mostrar a
imagem da cmera na tela, com o boto ao lado mas que no faz nada ainda. Precisamos em
seguida programar para que as imagens ficarem salvas no armazenamento externo.

Cmera

8.1.5

97

Capturando e salvando as imagens

Agora voc est pronto para capturar e salvar as fotos em sua aplicao. Para isso voc deve
definir listeners para o boto da sua interface que ir responder tirando uma foto.

Dica: Certifique-se de que h permisso para escrita no armazenamento externo para


salvar as fotos.

Para capturar uma foto, voc deve usar o mtodo Camera.takePicture(). Para receber os dados no format JPEG voc deve implementar um Camera.PictureCallback que
recebe os dados da imagem e os escrevem em um arquivo. Observe a implementao da interface PictureCallback no cdigo abaixo, que pode ser escrito no mtodo onCreate()
da activity.

PictureCallback mJPEGCallback = new PictureCallback() {

@Override
public void onPictureTaken(byte[] data, Camera camera) {
File picFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if(picFile == null){
Log.d("ERROR",
"Error creating media file, check storage permissions");
return;
}

3
4
5
6
7
8
9
10
11

try {
FileOutputStream fos = new FileOutputStream(picFile);
fos.write(data);
fos.close();
} catch(FileNotFoundException e) {
Log.d("ERROR", "File not found: " + e.getMessage());
} catch (IOException e){
Log.d("ERROR", "Error accessing file: " + e.getMessage());
}

12
13
14
15
16
17
18
19
20

21
22

};

Algoritmo 8.9: Criando um Callback para imagens JPEG

Para simplificar, todo cdigo que efetivamente cria a imagem em um arquivo ficou no mtodo getOutputMediaFile() que pode ser observado abaixo.

98
1
2
3

Cmera

private File getOutputMediaFile(int type){


String folder =
getResources().getString(R.string.app_name) + "_PICS";

4
5
6
7

File mediaStorageDir =
new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), folder);

8
9
10
11
12
13
14
15
16

if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("ERROR", "failed to create directory: " + folder);
return null;
}
}
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());

17
18
19
20
21
22
23
24
25
26
27

File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}

28

return mediaFile;

29
30

Algoritmo 8.10: Mtodo getOutputMediaFile()


Na linha 2 do mtodo getOutputMediaFile() estamos escolhendo o nome da pasta
que armazernar as fotos salvas. Para este exemplo, escolhi o nome do aplicativo concatenado com a string "_PICS". Como neste exemplo o nome do aplicativo "Camera1",
ento a pasta ser "Camera1_PICS". Na linha 5 criamos um objeto File que contm
uma referncia a esta pasta que estamos criando, o caminho dela dado pelo mtodo Environment.getExternalStoragePublicDirectory() e em seguida pela varivel
Environment.DIRECTORY_PICTURES. Essa chamada escolhe a pasta Pictures/ do
armazenamento externo e ao ser concatenada com a nossa varivel folder, temos a pasta que
iremos salvar as fotos. Em seguida feita uma verificao se a pasta j existe, caso no exista
ele tenta cri-la com o mtodo mkdirs(), que retorna false caso no seja possvel criar.
Para o nome do arquivo, criamos um timestamp que pega a data e hora atual para o nome do
arquivo. Isso pode ser observado na linha 16 com a criao de um SimpleDateFormat no
formato yyyyMMdd_HHmmss, ou seja: ano, ms, dia, hora, minuto e segundo, nesta ordem.
Na linha 18 criamos novamente um objeto File mas que dessa vez ir guardar o caminho do
arquivo.
Por fim, mostramos um Toast para dar um feedback ao usurio que a foto foi salva e

Cmera

99

mostrando a pasta para que ele possa ver.


Agora s falta dar a funo do boto de capturar, com o seguinte listener:

1
2

mCapture = (Button) findViewById(R.id.button_capture);


mCapture.setOnClickListener(new OnClickListener() {

3
4
5
6
7
8

@Override
public void onClick(View v) {
mCam.takePicture(null, null, mJPEGCallback);
}
});

Algoritmo 8.11: Mtodo getOutputMediaFile()


Basta chamar o mtodo takePicture()3 , que recebe trs callbacks, so eles: ShutterCallback, e dois PictureCallback, um para imagens do tipo RAW e outra para
imagens do tipo JPEG. Como s criamos um callback, o resto ser null.
Entretanto, as imagens salvas estaro com uma qualidade bem inferior. Para corrigir isso
voc pode adicionar o seguinte cdigo antes da linha 13 do algoritmo 8.8:

1
2

Parameters params = mCam.getParameters();


params.setJpegQuality(100);

3
4
5
6
7
8
9
10
11

List<Size> sizes = params.getSupportedPictureSizes();


Camera.Size size = sizes.get(0);
for(int i=0;i<sizes.size();i++)
{
if(sizes.get(i).width > size.width)
size = sizes.get(i);
}
params.setPictureSize(size.width, size.height);

12
13

mCam.setParameters(params);

Algoritmo 8.12: Melhorando a qualidade das fotos tiradas

Voc consegue configurar os parmetros da cmera, neste caso usaremos para melhorar a
qualidade da foto. Para isso comece chamando getParameters() e guarde essa referncia
num objeto Parameters. Com setJpegQuality() conseguimos configurar a qualidade
da compresso JPEG entre 0 e 100, sendo 100 o melhor. Em seguida iteraremos sob uma lista
de resolues suportadas e escolhemos a maior resoluo. Dessa forma a foto ter a maior resoluo e com a melhor qualidade de compresso. Para finalizar, chame setParameters()
e os parmetros sero gravados.
3

http://developer.android.com/intl/es/reference/android/hardware/Camera.html

100

8.2

Cmera

Gravando vdeos

Agora voc j tem um aplicativo que tira fotos, mas tambm quer gravar vdeos. Para isso,
algumas modificaes sero necessrias, assim como algumas adies. Para capturar videos,
necessrio um controle cauteloso do objeto Camera em coordenao com a classe MediaRecorder4 . Diferente de tirar fotos, gravar vdeos requer chamadas mtodos em uma ordem
particular. Voc deve seguir essa ordem para preparar a aplicao e capturar o video.
Os trs primeiros passos so: 1) Abrir a cmera, 2) Criar uma visualizao e 3) Visualizar a
cmera, j foram feitos na seo anterior. Para comear a gravar o vdeo so necessrios mais
alguns passos:

8.2.1

Preparar a gravao

Preparar a gravao significa configurar a classe MediaRecorder, todos os passos da


configurao devem ser feitos em uma ordem especfica. Para preparar a gravao criaremos
um mtodo prepareForRecording().
1
2
3
4

private boolean prepareForRecording() {


recorder = new MediaRecorder();
mCam.unlock();
recorder.setCamera(mCam);

recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

6
7
8

CamcorderProfile profile =
CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(profile);

9
10
11
12

recorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
recorder.setPreviewDisplay(mPreview.getHolder().getSurface());

13
14
15

try{
recorder.prepare();
} catch (IllegalStateException e){
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
return false;
}

16
17
18
19
20
21
22
23
24
25

return true;

26
27

Algoritmo 8.13: Mtodo prepareForRecording()


Temos uma varivel recorder da classe MediaRecorder e o instanciamos. O Primeiro
passo destravar a cmera com unlock() e atribuir a cmera ao recorder com setCa4

http://developer.android.com/intl/es/reference/android/media/MediaRecorder.html

Cmera

101

mera(). O segundo passo configurar a fonte de udio e vdeo com setAudioSource()


e setVideoSource(), respectivamente, como feito nas linhas 6 e 7. O terceiro passo
configurar um profile com o setProfile(), voc pode obter um profile usando o mtodo
CamcorderProfile.get(). O quarto passo configurar o arquivo de saida com o mtodo setOutputFile(), aqui usaremos o mtodo getOutpuMediaFile() que fizemos
antes. O quinto passo configurar a visualizao do recorder com setPreviewDisplay(), como feito na linha 14 e passamos como parmetro o mtodo getSurface() da nossa
classe CameraPreview. Por ltimo devemos tentar chamar o mtodo prepare(), mas
caso d errado precisamos liberar a cmera de volta, para isso usaremos o mtodo releaseMediaRecorder(), que iremos criar abaixo.

1
2
3
4
5
6
7
8

private void releaseMediaRecorder() {


if(recorder != null){
recorder.reset();
recorder.release();
recorder = null;
mCam.lock();
}
}

Algoritmo 8.14: Mtodo releaseMediaRecorder()

O cdigo de releaseMediaRecorder() simples, so os passos necessrios para


liberar a cmera do recorder e d-la de volta activity. O mtodo reset() limpa as
configuraes do recorder.
importante tambm liberar a cmera para outras aplicaes caso o usurio troque de aplicao, quando isso acontece o mtodo onPause() chamado no ciclo de vida da activity.

1
2
3
4
5
6
7
8
9

@Override
protected void onPause() {
super.onPause();
releaseMediaRecorder();
if(mCam != null){
mCam.release();
mCam = null;
}
}

Algoritmo 8.15: Liberando a cmera no mtodo onPause()

Por ltimo, configurar um listener para o boto de gravar vdeo, como mostrado no algoritmo 8.16 abaixo.

102
1
2

Cmera

mRecord = (Button) findViewById(R.id.button_record);


mRecord.setOnClickListener(new OnClickListener() {

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@Override
public void onClick(View v) {
if(isRecording){
recorder.stop();
releaseMediaRecorder();
((Button) v).setText("Record");
isRecording = false;
} else {
if(prepareForRecording()) {
recorder.start();
((Button) v).setText("Stop");
isRecording = true;
} else {
releaseMediaRecorder();
}
}
}
});

Algoritmo 8.16: Configurando o listener do boto de gravar vdeo


Com uma varivel de controle isRecording guardamos se a cmera est gravando ou
no. Caso esteja gravando e o usurio clique no boto chamamos stop(), liberamos o recorder com releaseMediaRecorder(), mudamos o texto do boto, alm de marcar
isRecording como falso, pois paramos de gravar. Caso contrrio, isto , o usurio quer
comear a gravar ento chamamos prepareForRecording() que criamos anteriormente,
se o resultado for positivo significa que todas as preparaes deram certo, podemos chamar
start() e colocar "Stop" no boto onde antes era "Record", alm de marcar isRecording como verdadeiro. Se a preparao falhar, ento apenas chamamos releaseMediaRecorder() para cancelar qualquer preparao que tenha sido feita.
Dica: Porque a complexidade de configurar a gravao de vdeo alta, recomendvel
baixar o projeto Camera1 do repositrio e estudar o cdigo completo.

8.3

Usando um Intent

A maneira mais fcil de tirar uma simples foto na verdade chamando um Intent que se
encarregar de abrir a activity da cmera e retornar a foto tirada.

8.3.1 Intent de capturar foto


O Intent da cmera pode receber opcionalmente um objeto Uri que especifica o caminho
e o nome do arquivo onde voc gostaria que as fotos fossem salvas. Apesar de ser opcional, esse
parmetro recomendvel. Caso no seja especificado, a aplicao da cmera ir salvar a foto
no local padro com um nome padro que pode ser visto no campo Intent.getData() que
retornado.

Cmera

103

O cdigo abaixo mostra como executar um Intent para capturar fotos.

1
2
3
4

private
private
private
private

static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;


static final int MEDIA_TYPE_IMAGE = 1;
static final int MEDIA_TYPE_VIDEO = 2;
Uri fileUri;

5
6
7
8

@Override
public void onCreate(Bundle savedInstanceState) {
...

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

10
11

fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

12
13
14

startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);

15
16

Algoritmo 8.17: Chamando a activity de cmera com Intent

Primeiro cria-se um novo Intent com parmetro MediaStore.ACTION_IMAGE_CAPTURE que configura uma activity com cmera. Depois usamos o mtodo getOutputMediaFileUri() que apenas faz uma chamada ao mtodo getOutputMediaFile()
(algoritmo 8.10) que usamos na seo anterior porm retornando uma Uri, que o caminho do
arquivo. Colocamos como extra no Intent o caminho retornado em fileUri, nesse caso
MediaStore.EXTRA_OUTPUT apenas uma conveno para ficar claro que o valor passado
no extra para ser usado na activity de cmera para salvar o arquivo. Por fim chamamos startActivityForResult(), que recebe o Intent e um cdigo que indica uma activity que
captura imagens.

1
2
3

private Uri getOutputMediaFileUri(int type){


return Uri.fromFile(getOutputMediaFile(type));
}

Algoritmo 8.18: Mtodo getOutputMediaFileUri()

8.3.2

Recebendo resultado do Intent

Quando voc chama startActivityForResult() significa que voc espera uma resposta da activity que chamou. Essa resposta ser obtida no mtodo onActivityResult()
que voc precisa escrever para manipular esses dados da resposta. O algoritmo 8.19 exemplifica
esse mtodo, que trata a resposta da cmera, isto , se foi possvel capturar a foto ou gravar o
vdeo.

104
1
2

Cmera

private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;


private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;

3
4
5
6

@Override
protected void onActivityResult
(int requestCode, int resultCode, Intent data) {

if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Image saved to:\n" +
fileUri.toString(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// Opcional
} else {
Toast.makeText(this, "Error capturing image...",
Toast.LENGTH_LONG).show();
}
}

8
9
10
11
12
13
14
15
16
17
18
19

if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Video saved to:\n" +
fileUri.toString(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// Opcional
} else {
Toast.makeText(this, "Error recording video...",
Toast.LENGTH_LONG).show();
}
}

20
21
22
23
24
25
26
27
28
29
30
31

Algoritmo 8.19: Mtodo onActivityResult()

A lgica est apenas em verificar o retorno da activity e mostrar uma mensagem ao usurio,
o informando do caminho em que foi salvo a imagem ou vdeo.

Dica: Quando voc passa uma Uri para a activity de cmera, o retorno dela na varivel
data ser sempre null. Voc pode, entretando, no especificar uma Uri e usar o campo
data para criar um Bitmap.

8.3.3 Intent de capturar vdeo


Para requisitar uma activity que grava vdeo, o processo semelhante embora voc possa
passar um extra adicional ao Intent que informa a qualidade do vdeo.

8
1
2
3

Cmera

105

@Override
public void onCreate(Bundle savedInstanceState) {
...

Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

5
6

fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

7
8
9

intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

10
11

startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);

12
13

Algoritmo 8.20: Criando um Intent para vdeo


Isso conclui o captulo de cmera, passamos por todas as fases da criao de um aplicativo
que usa a cmera como principal elemento. Tambm vimos como simples utilizar a cmera
como uma activity secundria cuja funo apenas capturar uma imagem. Sempre que existirem dvidas, faa o download dos projetos do repositrio para ver o cdigo completo em
funcionamento.

106

Cmera

C APTULO

9
udio

9.1

Gravando e tocando udio

Gravar udio uma tarefa muito semelhante a gravar vdeos no Android. Usaremos a mesma
classe: MediaRecorder, para configurar e gravar udio do microfone do aparelho. Para tocar
udio veremos uma nova classe chamada MediaPlayer.
Para este exemplo criaremos um aplicativo simples com 2 botes, um para gravar e um para
tocar a gravao. A fim de simplificar, toda gravao ser feita em apenas um arquivo, logo
sempre que gravar algo novo estar sobreescrevendo a gravao antiga. Entretanto, voc pode
modificar o mtodo getOutputMediaFile() (algoritmo 8.10) que criamos no captulo
anterior para gravar em diferentes arquivos.
Iniciaremos criando uma funo que chamaremos de prepareRecording(), similar
aquela que fizemos para no captulo de vdeo.

1
2
3
4
5
6

private boolean prepareRecording(){


recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile(testFilename);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

try{
recorder.prepare();
} catch (IOException e) {
Log.e("AudioRecorder", "prepareRecording() failed");
return false;
}
return true;

8
9
10
11
12
13
14
15

Algoritmo 9.1: Mtodo prepareRecording() para gravaes de udio


107

108

udio

Assim como fizemos no captulo anterior, a preparao precisa seguir passos determinados.
Primeiro precisamos configurar a fonte com setAudioSource(), nesse caso passamos o
MIC, o microfone do aparelho. O segundo passo configurar o formato da sada com setOutputFormat(), nesse exemplo optei pelo formato 3gp. O terceiro passo determinar
o arquivo de sada com setOutputFile() e para esse mtodo passamos uma string que
chamei de testFilename, o valor de testFilename neste exemplo o caminho do armazenamento externo mais o nome do arquivo. Depois, setAudioEncoder() para selecionar a codificao do udio. Por ltimo chame o mtodo prepare() para consolidar as
configuraes.
Tambm faa uma funo releaseRecorder() para liberar o gravador quando terminar
a gravao.
1
2
3
4

private void releaseRecorder(){


recorder.release();
recorder = null;
}

Algoritmo 9.2: Mtodo releaseRecorder()


Em seguida faa o listener do boto de gravar da mesma forma do boto de gravar vdeo do
captulo anterior.
1

mRecordButton.setOnClickListener(new OnClickListener() {

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

@Override
public void onClick(View v) {
if(isRecording){
recorder.stop();
releaseRecorder();
mRecordButton.setText("Record");
isRecording = false;
} else {
if(prepareRecording()){
recorder.start();
mRecordButton.setText("Stop");
isRecording = true;
} else {
releaseRecorder();
}
}
}
});

Algoritmo 9.3: Configurando o boto de gravar udio


So necessrias apenas verificaes simples para saber se est gravando ou no e se foi
possvel fazer a preparao do gravador ou no. Se est gravando e o usurio pressiona o

udio

109

boto ento chamamos stop() para parar a gravao, trocamos o texto do boto e atribumos
isRecording como falso, pois estamos parando de gravar. Caso contrrio, ou seja, queremos
gravar, ento chamamos start() para iniciar a gravao, ento mudamos o texto e atribumos
verdadeiro para isRecording. Se a preparao falhar chamamos releaseRecorder()
para liberar o microfone.
Para o tocador, iremos usar a classe MediaPlayer, iniciaremos criando um mtodo startPlaying() responsvel por instanciar um MediaPlayer e configur-lo.
1
2

private void startPlaying(){


player = new MediaPlayer();

player.setOnCompletionListener(new OnCompletionListener() {

4
5

@Override
public void onCompletion(MediaPlayer mp) {
mPlayButton.setText("Play");
stopPlaying();
isPlaying = false;
}
});

6
7
8
9
10
11
12
13

try{
player.setDataSource(testFilename);
player.prepare();
player.start();
} catch (IOException e){
Log.e("AudioRecorder", "file not found");
}

14
15
16
17
18
19
20
21

Algoritmo 9.4: Mtodo startPlaying()


Na linha 4 voc pode pode observar o uso de um listener para o MediaPlayer chamado OnCompletionListener, podemos usar essa interface para saber quando o udio
termina de tocar, nesse caso o estamos usando para poder configurar o boto. Dentro do bloco
try-catch usamos setDataSource() para configurar o arquivo de udio a ser tocado.
Depois chamamos prepare() e finalmente start() para comear a tocar.
Para complementar, a funo stopPlaying() tambm til para quando quisermos
parar de tocar o udio.
1
2
3
4
5

private void stopPlaying(){


player.stop();
player.release();
player = null;
}

Algoritmo 9.5: Mtodo stopPlaying()

110

udio

Por fim, basta configurar o listener do boto de tocar udio, como mostrado no algoritmo
abaixo:
1

mPlayButton.setOnClickListener(new OnClickListener() {

2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Override
public void onClick(View v) {
if(isPlaying){
stopPlaying();
isPlaying = false;
mPlayButton.setText("Play");
} else {
startPlaying();
isPlaying = true;
mPlayButton.setText("Stop");
}
}
});

Algoritmo 9.6: Configurando o boto de tocar udio


A lgica desse mtodo praticamente o mesmo do boto de gravar udio.
Para testar, a varivel testFilename dada por:
1
2
3

testFilename =
Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/audiorecordtest.3gp";

Algoritmo 9.7: Varivel testFilename

Dica: No se esquea das permisses no Manifest!


Com isso terminamos nosso aplicativo que no s faz uma simples gravao de udio, mas
que tambm nos permite escut-la usando a classe MediaPlayer.

C APTULO

10
Localizao e Mapas

O Google j tem uma extensa documentao que nos ensina como acessar a API do Google
Maps no seu aplicativo. Porm, muitas vezes ela pode confusa e voc sente que faltam informaes um pouco mais claras. Nesse captulo iremos abordar tanto o acesso a localizao do
dispositivo usando LocationManager e colocar essas informaes no Google Maps.
J existe uma nova API que utiliza o Google Play Services, a documentao oficial pode ser
vista aqui1

10.1

Acessando a localizao

Antes de iniciar, voc precisa definir se quer obter a localizao usando GPS (Global Positioning System) ou usando a Internet. Caso opte por usar o GPS, deve-se atentar ao fato que o
GPS demora muito para inicializar e obter as coordenadas. Enquanto que usando a Internet isso
praticamente instantneo, porm a localizao no to precisa. Ou seja, se quer rapidez use
a Internet como seu Location Provider, se quer preciso use o GPS.
Sabendo disso ento voc deve colocar as permisses no Manifest, mostradas abaixo.

1
2
3
4
5

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


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

Algoritmo 10.1: Permisses para obter localizao

A permisso INTERNET s ser necessria caso no queira obter a localizao usando o


GPS.
O prximo passo criar um LocationListener para acessar a localizao.
1

https://developer.android.com/google/play-services/location.html

111

112
1
2
3

10

Localizao e Mapas

public class MyLocationListener implements LocationListener {


private double c_lat;
private double c_long;

@Override
public void onLocationChanged(Location location) {
c_lat = location.getLatitude();
c_long = location.getLongitude();
}

5
6
7
8
9
10

@Override
public void onProviderDisabled(String provider) {
Toast.makeText(getApplicationContext(),
provider + " provider disabled", Toast.LENGTH_SHORT).show();
}

11
12
13
14
15
16

@Override
public void onProviderEnabled(String provider) {
Toast.makeText(getApplicationContext(),
provider + " provider enabled", Toast.LENGTH_SHORT).show();
}

17
18
19
20
21
22

@Override
public void onStatusChanged
(String provider, int status, Bundle extras) {
}

23
24
25
26
27

...

28
29

Algoritmo 10.2: Criando um LocationListener

O mtodo mais importante deste listener onLocationChanged(), toda vez que as


coordenadas de sua posio forem alteradas, ele ir chamar este mtodo. Para esse exemplo eu
apenas atualizei o valor de duas variveis, que podem ser utilizadas para atualizar a posio em
um mapa, por exemplo. J os mtodos onProviderDisabled() e onProviderEnabled() realizam uma ao quando o provedor de localizao desativado ou ativado, isso ir
depender muito de sua aplicao. Mas para um exemplo simples estou apenas mostrando um
Toast com a mensagem de que ele foi desativado ou ativado. O mtodo onStatusChanged() chamado quando o estado do provedor muda, por exemplo se ele se torna disponvel
depois de um perodo de indisponibilidade.
O prximo passo definir o LocationListener que criamos para um LocationManager. Que deve ser feito no onCreate().

10
1
2
3
4
5

Localizao e Mapas

113

LocationManager mlocManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
MyLocationListener mlocListener = new MyLocationListener();
mlocManager.requestLocationUpdates
(LocationManager.GPS_PROVIDER, 1000, 0, mlocListener);

Algoritmo 10.3: Configurando o LocationManager

A referncia ao LocationManager deve ser obtida com o mtodo getSystemService().Criamos uma nova instncia da classe MyLocationListener que criamos anteriormente. Depois deve-se registrar o listener para receber atualizaes usando requestLocationUpdates(). Os parmetros so: o provedor que ser usado, o intervalo mnimo
entre atualizaes (em milissegundos), a distncia mnima entre atualizaes (em metros) e
o listener. Nesse exemplo, estamos usando o GPS, caso queira usar a Internet, deve-se usar
LocationManager.NETWORK_PROVIDER.

10.2

Google Maps

Configurar o Google Maps para uso trabalhoso. Voc deve registrar sua aplicao para
obter uma chave que d permisso ao seu aplicativo para usar o Maps. Nessa seo faremos o
passo a passo de como obter a chave e colocar um mapa na sua activity.
Antes de iniciar, quero deixar claro que o Google Maps no funciona no emulador Android.
Isso devido a utilizao do Google Play Services que no est disponvel nos emuladores (at
essa data). Existe sim a possibilidade de instalar o Play Services nos emuladores, mas isso no
est previsto pelo Google e pode no funcionar corretamente.

10.2.1

Obtendo a chave de certificao debug

Para poder testar o Maps nos aplicativos que est desenvolvendo voc deve obter uma chave
de certificao debug. Na verdade toda aplicao ao ser lanada na Play Store precisa estar
assinada digitalmente usando uma chave release que pode ser obtida da mesma maneira.2
Caso esteja usando o Eclipse, a chave de certificao debug pode ser obtida no menu Window
> Preferences > Android > Build, sob o campo SHA-1 fingerprint.
Voc tambm pode adquir-la pela linha de comando, usando o software keytool que vem
junto do JDK. Voc ir precisar apontar para o arquivo debug.keystore que est na pasta
.android do seu sistema. Basta executar o keytool com o seguinte comando.
keytool -list -v -keystore ".android/debug.keystore" -alias androiddebugkey -storepass android -keypass android

10.2.2

Obtendo a API Key

Com a chave de certificao debug em mos, voc deve acessar o Google API Console e
criar um novo projeto. Aps criar o projeto, acesse API no menu esquerda e marque Google
Maps Android API v2 com status ON. Como mostrado na figura abaixo:
2

http://developer.android.com/intl/es/tools/publishing/app-signing.html

114

10

Localizao e Mapas

Figura 10.1: Ativando Maps API no Google API Console


Em seguida, necessrio registrar uma nova aplicao. Para isso, acesse Registered apps
no menu esquerda e clique em REGISTER APP. Preencha os campos de acordo e clique em
Register.

Figura 10.2: Registrando um app no Google API Console


Com isso, voc ter acesso a sua API Key que ser colocada no Manifest para habilitar o uso
do Google Maps no dispositivo.

10

Localizao e Mapas

115

Figura 10.3: Passo final para obter a API Key

Agora, com o cdigo em mos, voc deve abrir seu Manifest e adicionar a seguinte linha
dentro da tag <application>. Onde value a key adquirida.

1
2
3

<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="AaaaBbB1cCcCCDDDd22eee-ffFFFgGg3HHhh4i5" />

Algoritmo 10.4: Configurando a API Key no Manifest

10.2.3

Configurando o Google Play Services

Primeiro, voc deve fazer download do Google Play Services no SDK Manager. Aps
o trmino do download necessrio importar a biblioteca no Eclipse e referenci-la em seu
projeto.
A pasta com a biblioteca do Google Play Services se encontrar na pasta sdk/extras/google/google_play_services/libproject/ e se chama google-play-services_lib. necessrio importar essa pasta como projeto existente no Eclipse, no se esquea de
marcar a opo para copiar o projeto para o seu workspace.
Depois de importar, voc deve abrir as propriedades do seu projeto e selecionar Android
no menu esquerda. Na parte inferior onde est escrito Library clique em Add... e selecione
o google-play-services_lib. Se aparecer um smbolo verde de "correto", ento deu
certo.

116

10

Localizao e Mapas

Figura 10.4: Adicionando a Google Play Services como bilbioteca do projeto

10.2.4

Adicionando um mapa em sua activity

O Google Maps no Android usa OpenGL ES verso 2 para renderizar o mapa, logo necessrio adicionar o uso do OpenGL no Manifest.
1
2
3

<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>

Algoritmo 10.5: Adicionando o uso do OpenGL no Manifest


Agora que j obtemos a localizao, podemos us-la para marcar a posio no mapa. Existem diversas maneiras de adicionar um mapa em uma activity. Nesse exemplo utilizaremos um
FrameLayout para colocar o mapa dentro. Uma outra forma simplesmente declarando um
fragmento no XML do layout como mostrado abaixo:
1
2
3
4
5

<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.MapFragment"/>

Algoritmo 10.6: Adicionando o mapa como um fragmento no XML


O layout da activity consiste de dois TextView para mostrar a latitude e a longitude do
dispositivo no momento, alem disso tem um FrameLayout que ir conter o fragmento do
mapa.

10
1
2
3
4
5

Localizao e Mapas

117

public class MainActivity extends FragmentActivity {


private LatLng mLocation;
private TextView latTv;
private TextView longTv;
private FrameLayout mapFrame;

6
7
8
9
10

private
private
private
private

static final String MAP_FRAGMENT_TAG = "map";


GoogleMap mMap;
SupportMapFragment mMapFragment;
Marker marker;

11
12
13
14
15
16

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

17

LocationManager mlocManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
MyLocationListener mlocListener = new MyLocationListener();
mlocManager.requestLocationUpdates
(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);

18
19
20
21
22
23

latTv = (TextView) findViewById(R.id.latitude);


longTv = (TextView) findViewById(R.id.longitude);

24
25
26

mMapFragment = (SupportMapFragment) getSupportFragmentManager()


.findFragmentByTag(MAP_FRAGMENT_TAG);

27
28
29
30

if (mMapFragment == null) {
mMapFragment = SupportMapFragment.newInstance();

31
32
33

FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.add
(R.id.mapFrame, mMapFragment, MAP_FRAGMENT_TAG);
fragmentTransaction.commit();

34
35
36
37
38

39
40

setUpMapIfNeeded();

41
42

Algoritmo 10.7: Activity com Google Maps


O primeiro detalhe a ser notado que a Activity est extendendo FragmentActivity
a fim de poder usar o SupportFragment para compatibilidade com verses anteriores do
Android. As linhas 18 a 22 j so conhecidas, do exemplo anterior.
Na linha 27 estamos usando findFragmentByTag() para podermos obter o fragmento
do mapa, caso ele no esteja declarado no XML, ser criada uma nova instncia na linha 32. Na

118

10

Localizao e Mapas

linha 34 a 38 estamos adicionando o fragmento do mapa ao FrameLayout do layout usando


FragmentTransaction e por fim chamando uma funo setUpMadIfNeeded() para
obtermos a instncia do mapa (e no do seu fragmento).
1
2
3

private void setUpMapIfNeeded() {


if (mMap == null) {
mMap = mMapFragment.getMap();

if (mMap != null)
marker = mMap.addMarker
(new MarkerOptions().position(new LatLng(0,0)));

5
6
7

8
9

Algoritmo 10.8: Mtodo setUpMapIfNeeded()


Apenas obtemos a instncia do mapa de seu fragmento e criando um marcador na posio (0,0). Importante salientar que o principal motivo de se fazer esse mtodo para poder
cham-lo no mtodo onResume() da activity a fim de atualizar o mapa caso o aplicativo tenha
sido parado por algum motivo. Agora iremos modificar o mtodo onLocationChanged()
do MyLocationListener para atualizar a posio no mapa e adicionar um marcador na
posio correta.
1
2
3
4
5

@Override
public void onLocationChanged(Location location) {
c_lat = location.getLatitude();
c_long = location.getLongitude();
mLocation = new LatLng(c_lat, c_long);

latTv.setText("Lat: " + c_lat);


longTv.setText("Long: " + c_long);

7
8
9

marker.remove();
marker = mMap.addMarker
(new MarkerOptions().position(mLocation).title("You are here"));
mMap.moveCamera
(CameraUpdateFactory.newLatLngZoom(mLocation, 14.0f));

10
11
12
13
14
15

Algoritmo 10.9: Mtodo onLocationChanged() modificado


Primeiro removemos o marcador anterior, depois adicionamos um novo marcador na posio obtida pelo listener. Na linha 13 chamamos o mtodo moveCamera() para colocar a
cmera na posio do marcador.
Para mais exemplos de mapas, voc pode importar o projeto maps da pasta samples onde
voc obteve o libproject.

C APTULO

11
Compartilhamento

J aprendemos a enviar dados outras activities usando a classe Intent. Agora veremos como faz para enviar esses dados e dar ao usurio a opo de escolher qual aplicativo j
instalado no dispositivo ele quer usar para receber esses dados. Isso pode ser visto como compartilhamento. Imagine que voc tirou uma foto e quer mostrar ao seus amigos pelo Facebook.
Na verdade est enviando uma foto activity do Facebook que recebe esse tipo de dado e assim
ela tomar o controle.
Quando voc construir um Intent voc deve especificar a ao que voc espera que ele
acione. O Android j define vrios tipos de aes e entre eles est o ACTION_SEND que indica
que o intent est enviando dados de uma activity para outra.
Se voc quer enviar apenas um texto, por exemplo, basta seguir o pequeno exemplo abaixo:

1
2
3
4
5

Intent sendIntent = new Intent();


sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(sendIntent);

Algoritmo 11.1: Enviando um texto simples atravs de um Intent

Contanto que exista um aplicativo instalado no aparelho com um filtro que aceite ACTION_SEND e o tipo MIME "text/plain" o Android ir execut-lo, caso exista mais de um
ento um alerta aparecer perguntando qual aplicativo o usurio deseja escolher para receber o
Intent.

Dica: Se chamar Intent.createChooser() ento o Android sempre ir mostrar


o alerta. Voc pode configurar o texto a ser mostrado no parmetro.

119

120
1

11

Compartilhamento

startActivity(Intent.createChooser(sendIntent, "Share text..."));

Algoritmo 11.2: Chamando createChooser()

Voc pode usar alguns extras padres nos Intent, tais como: EXTRA_EMAIL para o
texto ser o corpo do e-mail, EXTRA_SUBJECT para o texto ser o assunto do e-mail. Porm
o aplicativo que estiver recebendo o Intent deve estar preparado para esse tipo de extra, se
no nada vai acontecer. Existe ainda a opo de criar extras personalizados, mas tambm s
ir funcionar caso o aplicativo esteja projetado para esse tipo de extra. Nesse caso comum
criar um extra personalizado caso voc tenha feito um conjunto de aplicativos que podem trocar
informaes atravs de Intents e que iro usar esse extra.
Alm de texto possvel enviar dados binrios ou at mesmo mais de um dado ao enviar
uma lista. Para enviar uma foto que voc tirou com a cmera, por exemplo, voc deve usar o
extra chamado EXTRA_STREAM e configurar o tipo do Intent para "image/jpeg".
Uma forma mais completa de enviar imagems ou outros tipos de dados usando um ContentProvider1 que ir criar uma interface para a prover os dados a outras aplicaes.
Outra parte importante do compartilhamento fazer com que sua aplicao consiga receber
dados de Intents. O primeiro passo para isso configurar os intent-filter no Manifest. Quando voc define um intent-filter voc est dizendo para o Android que uma
determinada activity capaz de receber um determinado de Intent. Uma activity pode ter
mltiplos filtros, cada um relacionado a um determinado tipo de dado.
Para exemplificar o conceito, iremos fazer um aplicativo que escreve um texto e tira uma
foto e um aplicativo que recebe um texto ou uma imagem e mostra o que foi recebido. O
aplicativo usa conceitos j conhecidos ento iremos nos limitar a explicar somente a parte do
compartilhamento. Entretanto, caso haja dvidas possivel obter ambos aplicativos do repositrio.
No primeiro aplicativo, interessante salientar apenas os botes que adicionei para compartilhar. Um tem a funo de compartilhar o texto e outro a imagem. Voc pode, por exemplo
compartilhar um e depois o outro no aplicativo do Gmail que ambos iro ser adicionados ao
corpo do e-mail.
Observe no cdigo abaixo, como ficaram os botes de compartilhamento.

http://developer.android.com/intl/es/reference/android/content/ContentProvider.html

11
1
2

Compartilhamento

121

final Intent shareIntent = new Intent();


shareIntent.setAction(Intent.ACTION_SEND);

3
4

shareText.setOnClickListener(new OnClickListener() {

5
6
7
8
9
10
11
12
13

@Override
public void onClick(View v) {
String text = textField.getText().toString();
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(shareIntent, "Send to..."));
}
});

14
15

sharePic.setOnClickListener(new OnClickListener() {

16
17
18
19
20
21
22
23

@Override
public void onClick(View v) {
shareIntent.setType("image/png");
shareIntent.putExtra(Intent.EXTRA_STREAM, picUri);
startActivity(Intent.createChooser(shareIntent, "Send to..."));
}
});

Algoritmo 11.3: Botes para compartilhar texto e imagem

A parte mais importante o mtodo setType, ele recebe um MIME Type2 , como o caso
de text/plain e image/png. Outros MIME Types poderiam ser usados como image/*
caso no soubssemos o formato da imagem ou at mesmo */* que representa qualquer tipo
de dado. O problema disso que todos aplicativos iro aparecer na lista e alguns podem no
receber o que voc est tentando enviar! Portanto, seja cauteloso.
J o segundo aplicativo ir receber tanto um texto como uma foto e mostrar ao usurio, apenas para exemplificar o recebimento de Intents. O primeiro passo criar os intent-filter
para receber os MIME Type corretos.

MIME: http://en.wikipedia.org/wiki/MIME

122
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

11

Compartilhamento

<activity
android:name="br.ufscar.dc.mobile.receivecontent.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>

Algoritmo 11.4: Configurando os intent-filter no Manifest


necessrio criar um intent-filter para cada tipo de dado que ser recebido. Como
nesse caso s existe uma activity, ela tanto uma activity Launcher, ou seja, aquela que abre
o apicativo. Mas tambm recebe dois MIME Types, so eles text/plain e image/*. Em
seguida, precisamos receber os dados do Intent e mostr-los para o usurio, como feito no
cdigo abaixo:
1
2
3

Intent intent = getIntent();


String action = intent.getAction();
String type = intent.getType();

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

if(Intent.ACTION_SEND.equals(action) && type != null){


if("text/plain".equals(type)){
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if(sharedText != null)
sharedTextView.setText(sharedText);
} else if (type.startsWith("image/")) {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if(imageUri != null){
picView.setImageBitmap
(decodeSampledBitmapFromResource(
getResources(), imageUri.getPath(), 200, 200));
}
}
}

Algoritmo 11.5: Obtendo os dados do Intent e mostrando ao usurio


Lembre-se que nesse pedao de cdigo, estamos apenas preocupados em obter os dados do

11

Compartilhamento

123

Intent e coloc-los nas suas views e portanto no o cdigo completo da activity. Basicamente o que fazemos comparar as strings recebidas no Intent para saber o que est sendo
recebido. Ao verificar o action, sabemos que o Intent veio atravs de compartilhamento e ao
analisar o type, sabemos o MIME type e podemos construir a activity de acordo.
Dica: Modifique a activity para salvar a imagem ou o texto no caso do usurio voltar
para compartilhar outra coisa. Voc pode salvar os dados no Bundle.
Esse captulo tentou apenas para dar uma introduo ao compartilhamento de dados entre
aplicativos. Outras coisas interessantes podem ser feitas, por exemplo adicionar o boto de
Easy Share como descrito nessa pgina3 da documentao. Alm disso, existe tambm a classe
ContentProvider que fornece uma interface para compartilhamento.

http://developer.android.com/intl/es/training/sharing/shareaction.html

124

11

Compartilhamento

C APTULO

12
Agenda e Contatos

12.1

Usando o Contacts Provider

O Android contm um repositrio central onde esto armazenadas as informaes dos contatos do usurio, incluindo os dados de redes sociais. Esse repositrio se chama Contacts Provider1 .
Seguindo as lies da documentao oficial, iremos criar uma pequena aplicao que obtm
todos seus contatos e popula uma lista com eles. Mais detalhes do contato podero ser obtidos
ao se clicar no contato. O primeiro passo, sempre obrigatrio para se acessar o repositrio de
contatos, adicionar permisso no Manifest. Demonstrado no trecho abaixo:
1

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

Algoritmo 12.1: Permisso para acessar os contatos


Em seguida precisamos de uma activity com uma lista para conter os contatos. Nesse exemplo criamos um XML de layout que s contenha uma ListView e em seguida um outro XML
para definir o layout de um item da lista, nesse caso ser somente uma TextView. Voc poder
obter mais detalhes no cdigo do projeto disponvel no repositrio.
Dica: Voc pode incementar essa lista adicionando outras informaes do contato na
lista, como a foto de cada um.
Aps ter definido o layout precisamos mostrar a lista de contatos, comece definindo a activity que ir conter a lista. A lista dever ser populada utilizando a classe ContactsContract2 que j nos providencia algumas constantes e mtodos para acessar a lista de contatos.
1
2

http://developer.android.com/guide/topics/providers/contacts-provider.html
http://developer.android.com/reference/android/provider/ContactsContract.html

125

126
1
2

12

Agenda e Contatos

public class ContactsActivity extends FragmentActivity implements


LoaderCallbacks<Cursor>, OnItemClickListener {

Algoritmo 12.2: Activity que ir conter a lista de contatos

Como pode ser observado no cdigo 12.2 estamos extendendo a classe FragmentActivity3 que nos permite usar o Loader em Android mais antigos. Alm disso estamos
implementando as interfaces LoaderManager.LoaderCallbacks4 que permite o carregamento de dados de forma assncrona. Estamos implementando tambm a interface OnItemClickListener5 para que possamos carregar mais informaes do contato quando este
clicado na lista.
Precisamos de algumas variveis para auxiliar na seleo dos contatos e na criao do adaptador da lista. A varivel FROM_COLUMNS, abaixo, so os dados que sero mostrados e que
nesse caso apenas o nome do contato. Observe que estamos fazendo uma pequena comparao da verso do SDK pois a partir da verso 3.0 do SDK, a classe Contacts foi alterada. J a
varivel TO_IDS serve para o adaptador saber onde colocar a informao, passamos para ele o
id do TextView que colocamos no XML. Como essa lio se baseia na lio da documentao
oficial, estamos usando um dos ids padres do Android.

1
2
3
4
5

@SuppressLint("InlinedApi")
private final static String[] FROM_COLUMNS =
{Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME };
private final static int[] TO_IDS = {android.R.id.text1};

Algoritmo 12.3: Variveis para o adaptador da lista

As prximas variveis so aquelas da seleo dos contatos. Nesse exemplo vamos obter
todos os contatos e portanto deixar a varivel mSearchString vazia, mas caso queira obter
contatos especficos, basta fazer algo que preencha essa varivel. Temos a varivel SELECTION que funciona como a parte WHERE de uma consulta SQL, ela j est preparada para
a busca por algum nome, caso queira. A varivel PROJECTION uma constante que define
as colunas que voc quer retornar das consultas, nesse caso queremos 3: o id do contato, sua
chave de busca (lookup key) e o seu nome. Por fim, as variveis CONTACT_ID_INDEX e
LOOKUP_KEY_INDEX funcionam como ponteiros para o Cursor que ir se mover nos resultados da pesquisa, basta definir o valor da varivel como sendo a posio da coluna na projeo.
Como temos a coluna id sendo a primeira e a lookup sendo a segunda, colocamos os valores 0 e
1, respectivamente. Isso necessrio para se obter dados de uma coluna individual do Cursor.
3

http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html
http://developer.android.com/reference/android/app/LoaderManager.LoaderCallbacks.html
5
http://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html
4

12
1
2
3
4
5
6
7
8
9

Agenda e Contatos

127

@SuppressLint("InlinedApi")
private final static String[] PROJECTION = {
Contacts._ID,
Contacts.LOOKUP_KEY,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME,
};
private static final int CONTACT_ID_INDEX = 0;
private static final int LOOKUP_KEY_INDEX = 1;

10
11
12
13
14
15

@SuppressLint("InlinedApi")
private static final String SELECTION =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
Contacts.DISPLAY_NAME + " LIKE ?";

16
17
18

private String mSearchString = "";


private String[] mSelectionArgs = { mSearchString };

Algoritmo 12.4: Variveis para o Cursor do conjunto resultante da busca

As ltimas variveis de que precisamos so estas, para armazenar os resultados da consulta


e guardar referncias. Iremos usar a Uri do contato para buscar mais informaes posteriormente.

1
2
3
4
5

private
private
private
private
private

ListView mContactList;
long mContactId;
String mContactKey;
Uri mContactUri;
SimpleCursorAdapter mCursorAdapter;

Algoritmo 12.5: Variveis de controle

Depois, precisamos implementar o mtodo onCreate(). Nesse mtodo, iremos configurar o adaptador da lista que ser um SimpleCursorAdapter6 . Esse adaptador liga os
resultados da busca com a ListView.

http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html

128
1
2
3

12

Agenda e Contatos

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mContactList = (ListView) findViewById(R.id.contactList);


mCursorAdapter = new SimpleCursorAdapter(
this,
R.layout.list_item,
null,
FROM_COLUMNS,
TO_IDS, 0);
mContactList.setAdapter(mCursorAdapter);
mContactList.setOnItemClickListener(this);

5
6
7
8
9
10
11
12
13
14

getSupportLoaderManager().initLoader(0, null, this);

15
16

Algoritmo 12.6: Mtodo onActivityCreated()


Na linha 4 do algoritmo 12.6, estamos criando o SimpleCursorAdapter, como j discutido na lio de listas, necessrio passar para o adaptador o layout interno da lista assim
como os mapas com os dados e os ids das views em que esses dados sero inseridos. So esses
parmetros as variveis FROM_COLUMNS e TO_IDS. Na linha 10 determinamos o adaptador
para a lista e na linha 11 determinamos que o listener da lista ser a prpria classe. Isso
possvel pois estamos implementando a interface OnItemClickListener.
Como estamos usando o CursorLoader7 para buscar os dados, ns precisamos inicializar
a thread que ficar no plano de fundo que ir controlar a busca de forma assncrona.
O prximo passo implementar o mtodo onCreateLoader(), que ir realizar a consulta, ao retornar um CursorLoader que recebeu como parmetros da busca .
1
2
3
4
5
6
7
8
9
10
11

@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
mSelectionArgs[0] = "%" + mSearchString + "%";
return new CursorLoader(
getActivity(),
Contacts.CONTENT_URI,
PROJECTION,
SELECTION,
mSelectionArgs,
null);
}

Algoritmo 12.7: Mtodo onCreateLoader()


Depois, voc deve implementar os mtodos onLoadFinished() e onLoaderReset().
O primeiro chamado quando o Contacts Provider retorna os resultados da consulta, usamos
7

http://developer.android.com/reference/android/content/CursorLoader.html

12

Agenda e Contatos

129

o mtodo swapCursor() para trocar o cursor do adaptador pelo cursor do mtodo onLoadFinished(). J o segundo chamado quando o framework detecta que o cursor contm
dados desatualizados, nesse caso basta apagar a referrencia ao cursor no adaptador.
Por fim, voc deve implementar o mtodo onItemClick() para abrir uma nova activity
ao clicar no contato.

1
2
3
4
5
6

public void onItemClick(AdapterView<?> arg0, View arg1,


int position, long arg3) {
Cursor cursor = mCursorAdapter.getCursor();
cursor.moveToPosition(position);
mContactId = cursor.getLong(CONTACT_ID_INDEX);
mContactKey = cursor.getString(LOOKUP_KEY_INDEX);

mContactUri = Contacts.getLookupUri(mContactId, mContactKey);


Intent intent = new Intent(this, ContactDetailsActivity.class);
intent.putExtra("URI", mContactUri);
startActivity(intent);

8
9
10
11
12

Algoritmo 12.8: Mtodo onItemClick()

Primeiro ns obtemos a posio na lista atravs do parmetro position, em seguida colocamos o cursor nessa posio e obtemos os dados do contato. Usando o mtodo getLookupUri() ns conseguimos a URI do contato para que seja possvel acessar suas informaes
detalhadas.

12.1.1

Detalhes de um contato

Conseguir os detalhes do contato usa o mesmo princpio de conseguir todos os contatos.


Devemos fazer algumas consultas, mas com selees e projees diferentes. Para obter os
telefones de um contato, dever acessar o campo Phone do ContactsContract, para obter
os e-mails: Email, e para acessar os endereos: StructuredPostal.
Nesse exemplo, criaremos uma nova activity que ir conter os dados do contato escolhido
na primeira activity. Para facilitar a leitura do cdigo, iremos criar trs interfaces para serem
usadas, essas interfaces iro conter as projees e selees de cada um dos tipos de dado.

130
1
2
3
4
5
6
7
8
9

12

Agenda e Contatos

public interface ContactDetailsQuery {


final static int QUERY_ID = 1;
@SuppressLint("InlinedApi")
final static String[] PROJECTION = {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME,
};
final static int DISPLAY_NAME = 0;
}

10
11
12
13
14
15
16
17

public interface ContactPhoneQuery {


final static int QUERY_ID = 2;
final static String[] PROJECTION = {
Phone.NUMBER
};
final static String SELECTION =
Phone.CONTACT_ID + " = ?";

18

final static int NUMBER = 0;

19
20

21
22
23
24
25
26
27
28

public interface ContactAddressQuery {


final static int QUERY_ID = 3;
final static String[] PROJECTION = {
StructuredPostal.FORMATTED_ADDRESS
};
final static String SELECTION =
StructuredPostal.CONTACT_ID + " = ?";

29

final static int ADDRESS = 0;

30
31

Algoritmo 12.9: Interfaces das consultas dos contatos


Dessa forma, mostrada no algoritmo 12.9, conseguimos organizar melhor o cdigo para as
consultas que iremos fazer. Essa activity, que decidi chamar ContactDetailsActivity
tambm ir extender FragmentActivity e implementar LoaderCallbacks<Cursor>.
Iremos utilizar essas trs variveis, das quais duas sero obtidas do Intent que abriu essa activity.
1
2
3
4
5

public class ContactDetailsActivity extends FragmentActivity


implements
LoaderCallbacks<Cursor> {
private Uri mContactUri;
private long mContactId;
private ViewGroup container;

Algoritmo 12.10: Classe ContactDetailsActivity


Em seguida, construa o mtodo onCreate(). A diferena dessa activity para a anterior

12

Agenda e Contatos

131

a obteno dos dados do Intent e a referncia ao ViewGroup que iremos utilizar mais
tarde.
1
2
3
4

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

Intent intent = getIntent();


Uri u = intent.getParcelableExtra("URI");
long i = intent.getLongExtra("ID", 0);
setContact(u, i);

6
7
8
9
10

container = (ViewGroup) findViewById(R.id.viewgroup);

11
12

getSupportLoaderManager().initLoader(0, null, this);

13
14

Algoritmo 12.11: Mtodo onCreate() de ContactDetailsActivity


O mtodo setContact() usado para colocar os dados obtidos do Intent nos atributos da classe e chamar as consultas necessrias. Chamamos o mtodo restartLoader()
para forar a criao da thread que ir realizar a consulta. Observe que cada uma responsvel
por uma consulta, passamos o QUERY_ID de cada uma das interfaces para diferenci-las.
1
2
3
4
5
6
7
8

public void setContact(Uri contactUri, long contactId){


if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
mContactUri = contactUri;
} else {
mContactUri = Contacts.lookupContact
(getContentResolver(), contactUri);
}
mContactId = contactId;

getSupportLoaderManager().
restartLoader(ContactDetailsQuery.QUERY_ID, null, this);
getSupportLoaderManager().
restartLoader(ContactAddressQuery.QUERY_ID, null, this);
if(hasPhone())
getSupportLoaderManager().
restartLoader(ContactPhoneQuery.QUERY_ID, null, this);

10
11
12
13
14
15
16
17

Algoritmo 12.12: Mtodo setContact()


O mtodo hasPhone(), chamado na linha 14, verifica se o contato tem algum nmero de
telefone cadastrado. Esse mtodo demonstra uma outra forma de se adquirir os dados de um

132

12

Agenda e Contatos

contato, sem ser em outra thread, mas na mesma thread da UI (User Interface)8 .
Agora iremos implementar o mtodo onCreateLoader(), de forma semelhante ltima
vez, mas agora iremos criar um CursorLoader diferente dependendo do id que foi passado
para o mtodo - no caso o QUERY_ID que foi o parmetro do mtodo restartLoader(),
visto no algoritmo 12.12.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle arg1) {
switch (id) {
case ContactDetailsQuery.QUERY_ID:
return new CursorLoader(this,
mContactUri,
ContactDetailsQuery.PROJECTION,
null, null, null);
case ContactAddressQuery.QUERY_ID:
String addressArgs[] = {String.valueOf(mContactId)};
return new CursorLoader(this,
StructuredPostal.CONTENT_URI,
ContactAddressQuery.PROJECTION,
ContactAddressQuery.SELECTION,
addressArgs, null);
case ContactPhoneQuery.QUERY_ID:
String phoneArgs[] = { String.valueOf(mContactId) };
return new CursorLoader(this,
Phone.CONTENT_URI,
ContactPhoneQuery.PROJECTION,
ContactPhoneQuery.SELECTION,
phoneArgs, null);
default:
break;
}
return null;
}

Algoritmo 12.13: Mtodo onCreateLoader()


Nesse mtodo, ns estamos fazendo um switch para selecionar qual consulta iremos fazer
e ento usamos as projees e selees de cada interface para realizar a consulta. Se atente as
diferenas entre cada um: no primeiro caso, ns usamos mContactUri no segundo parmetro
do construtor do CursorLoader pois ns j temos o "endereo"do nome do contato nesse
atributo, nesse caso no preciso fazer uma seleo.
No segundo e terceiro casos, no segundo parmetro passamos o "endereo"da tabela dos
endereos e telefones dos contatos, mas usamos a seleo para poder selecionar qual contato.
A varivel addressArgs contm o id do contato que ir servir como parmetro da seleo.
8

Tudo que fizemos at agora (exceto o AsyncTask na lio de comunicao) est rodando na chamada UI
thread, isso significa que o Android est executando seu cdigo na mesma thread em que cria a interface do
usurio. Usar o Loader implica em fazer com que as consultas rodem em uma outra thread, liberando a UI
thread do servio e impedindo que o aplicativo parea estar travado enquanto busca informaes. O AsyncTask
uma outra forma de executar background threads de forma asscrona.

12

Agenda e Contatos

133

Note que no Android essas informaes dos contatos esto guardadas em um banco de dados e
o que estamos fazendo na realidade so SELECT comuns, mas usando as classes pr-definidas
para nos auxiliar. O Android tambm j junta vrias tabelas automaticamente para facilitar as
buscas.
Para finalizar, vamos implementar o mtodo onLoadFinished(), nesse mtodo que
adquirimos os dados do cursor e criamos as views para mostrar esses dados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {


if(mContactUri == null){
return;
}
switch(loader.getId()){
case ContactDetailsQuery.QUERY_ID:
if(cursor.moveToFirst()){
String contactName =
cursor.getString(ContactDetailsQuery.DISPLAY_NAME);
TextView nameView = new TextView(this);
nameView.setText(contactName);
nameView.setTextSize(30);
container.addView(nameView);
}
break;
case ContactPhoneQuery.QUERY_ID:
if(cursor.moveToFirst()){
do {
String contactPhone =
cursor.getString(ContactPhoneQuery.NUMBER);
TextView phoneView = new TextView(this);
phoneView.setText(contactPhone);
phoneView.setTextSize(20);
container.addView(phoneView);
} while(cursor.moveToNext());
}
break;
case ContactAddressQuery.QUERY_ID:
if(cursor.moveToFirst()){
do {
String address =
cursor.getString(ContactAddressQuery.ADDRESS);
TextView addrView = new TextView(this);
addrView.setText(address);
addrView.setTextSize(15);
container.addView(addrView);
} while(cursor.moveToNext());
}
break;
}
}

Algoritmo 12.14: Mtodo onLoadFinished()

134

12

Agenda e Contatos

Novamente, o mtodo ir tomar uma ao diferente dependendo de qual consulta foi realizada. Se existe um resultado, ento ns criado uma TextView e colocamos o texto adquirido
nela. Aqui que usamos a varivel container, usamos o mtodo addView() para colocar
essa view dentro dela.
Esse captulo tentou dar uma introduo obteno dos contatos da agenda. uma tema
especialmente difcil por haver muito rigor nos detalhes. altamente recomendvel estudar
mais a fundo a lio presente na documentao oficial e o exemplo oferecido por eles.

C APTULO

13
Acelermetro

Os dispositivos Android contam com um sensor chamado acelermetro que consegue captar
a acelerao do aparelho nos trs eixos. til para fazer jogos, saber a orientao do aparelho,
entre outras utilidades. Para isso o Android contm um framework para os sensores, uma das
classes a SensorManager que contm vrios mtodos para acessar, registrar listeners e
constantes para calibrar os sensores, reportar a preciso do sensor e configurar a taxa de aquisio dos dados. Para saber mais sobre o framework, acesse a documentao oficial1 .
Agora, para exemplificar vamos construir uma aplicao bem simples que apenas ir mostrar
na tela os valores lidos do acelermetro. Criaremos apenas uma activity com trs TextView
para mostrar os valores lidos do acelermetro.
1
2
3
4
5

public class AccelActivity extends Activity implements


SensorEventListener {
private TextView xAxis;
private TextView yAxis;
private TextView zAxis;

6
7
8

private SensorManager mSensorMan;


private Sensor mSensor;

9
10
11

private double gravity[] = {1, 1, 1};


private double acceleration[] = {1, 1, 1};

12

Algoritmo 13.1: Classe AccelActivity


Usaremos os dois arrays, gravity e acceleration para guardar os valores lidos do
sensor. Em seguida, no mtodo onCreate(), voc dever adquirir uma referncia do SensorManager usando getSystemService().
1

http://developer.android.com/guide/topics/sensors/sensors_overview.html

135

136
1
2
3

13

Acelermetro

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_accel);

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


yAxis = (TextView) findViewById(R.id.yAxis);
zAxis = (TextView) findViewById(R.id.zAxis);

5
6
7
8

mSensorMan =
(SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

9
10
11
12

Algoritmo 13.2: Mtodo onCreate() de AccelActivity


necessrio agora criar o listener que ir escutar o sensor e escrever os dados na tela. O
mtodo onSensorChanged() chamado de acordo com o intervalo definido em registerListener() na linha 4 do algoritmo 13.4.
1
2
3
4

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;

final double alpha = 0.8;

6
7

gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];


gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

8
9
10
11

acceleration[0] = event.values[0] - gravity[0];


acceleration[1] = event.values[1] - gravity[1];
acceleration[2] = event.values[2] - gravity[2];

12
13
14
15

DecimalFormat df
xAxis.setText("X
yAxis.setText("Y
zAxis.setText("Z

16
17
18
19
20

= new
Axis:
Axis:
Axis:

DecimalFormat("##.####");
" + df.format(acceleration[0]));
" + df.format(acceleration[1]));
" + df.format(acceleration[2]));

Algoritmo 13.3: Mtodo onSensorChanged()


Primeiro verificamos se o sensor que chamou o mtodo o acelermetro, s prosseguimos
caso seja. Os dados lidos esto armazenados no array event. Cada ndice indica um eixo: 0
o eixo X, 1 o eixo Y e 2 o eixo Z. Para calcular corretamente a acelerao, precisamos retirar
o componente gravitacional da conta. Para isso fazemos um filtro passa-baixo, como indicado
na documentao2 .
2

http://developer.android.com/guide/topics/sensors/sensors_motion.html

13

Acelermetro

137

Por ltimo, deve-se registrar o listener quando a activity est em primeiro plano e desregistrar quando a activity for pausada. Isso necessrio para no ficar consumindo a bateria
de forma desnecessria. Logo, ser necessrio fazer isso nos mtodos onResume() e onPause().
1
2
3
4
5

protected void onResume(){


super.onResume();
mSensorMan.registerListener
(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}

6
7
8
9
10

protected void onPause(){


super.onPause();
mSensorMan.unregisterListener(this);
}

Algoritmo 13.4: Mtodos onResume() e onPause()


Quando registrar o listener, importante indicar o intervalo de atualizao (terceiro parmetro), estamos usando SENSOR_DELAY_NORMAL pois o mais lento.
Esse captulo buscou apenas dar uma introduo bem simples ao uso de sensores, mas mais
especificamente ao uso do acelermetro. Todos os sensores podem ser programados de forma
similar.

138

13

Acelermetro

C APTULO

14
Bluetooth

O Android providencia uma API para conectar o aparelho em outros dispositivos Bluetooth,
sejam outros celulares, tablets, fones de ouvido e at mesmo HDP (Health Device Profile) que
um tipo de conexo Bluetooth para aparelhos mdicos. Com a API para Bluetooth voc pode:
sondar outros dispositivos Bluetooth, buscar por dispositivos j pareados, estabelecer conexes,
transferir dados e gerenciar mltiplas conexes.
Para exemplificar iremos construir uma pequena aplicao que envia uma mensagem para
um dispositivo pareado. Esse exemplo ir introduzir diversos novos conceitos do sistema Android e do prprio Java, entre eles: BroadcastReceiver, Thread, Handler, Runnable, alm claro, do Bluetooth.
Primeiro nossa aplicao deve recuperar todos os dispositivos bluetooth j conhecidos, ou
seja, aqueles que j foram pareados com o nosso aparelho. Colocaremos numa ListView
tanto os dispositivos pareados como aqueles novos que iremos procurar.
Iniciaremos com uma classe que chamei de DeviceListActivity, essa activity ir
mostrar a lista dos dispositivos encontrados, ao se clicar em um desses dispositivos, uma mensagem ser enviada atravs de um socket. Somente se o outro dispositivo estiver rodando a
aplicao no mesmo momento que a mensagem poder ser visualizada.
1
2
3

public class DeviceListActivity extends Activity


implements OnItemClickListener {
private ListView deviceList;

4
5
6

private BluetoothAdapter mBluetoothAdapter;


private ArrayAdapter<String> mArrayAdapter;

Algoritmo 14.1: Classe DeviceListActivity


Usaremos um ArrayAdapter1 para mostrar na lista o nome e o endereo MAC dos dispositivos. Alm disso, a varivel BluetoothAdapter2 usada para iniciar a varredura por
1
2

http://developer.android.com/reference/android/widget/ArrayAdapter.html
http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html

139

140

14

Bluetooth

novos dispositivos, obter a lista de dispositivos pareados instanciar um BluetoothDevice3


e criar um BluetoothServerSocket4 que ir aceitar novas conexes.
Agora j iremos para o mtodo onCreate(), nesse mtodo quatro importantes passos
devero ser seguidos. Os trs primeiros esto sendo mostrados no cdigo abaixo.
1
2
3
4

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

/* 1 */
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null) {
// Bluetooth nao suportado
}

6
7
8
9
10
11

/* 2 */
if(!mBluetoothAdapter.isEnabled()){
Intent enableBluetooth = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, REQUEST_ENABLE_BT);
}

12
13
14
15
16
17
18

mArrayAdapter = new ArrayAdapter<String>(


this, android.R.layout.simple_list_item_1);

19
20
21

/* 3 */
Set<BluetoothDevice> pairedDevices =
mBluetoothAdapter.getBondedDevices();
if(pairedDevices.size() > 0){
for(BluetoothDevice device : pairedDevices){
mArrayAdapter.add(device.getName() + "\n"
+ device.getAddress());
}
}
... //[1]
deviceList.setAdapter(mArrayAdapter);
... //[2]

22
23
24
25
26
27
28
29
30
31
32
33
34

Algoritmo 14.2: Primeira parte do mtodo onCreate()


O primeiro passo, marcado na linha 6, obter a referncia do adaptador bluetooth com a
chamada ao mtodo BluetoothAdapter.getDefaultAdapter(). Caso esse mtodo
retorne null ento o aparelho Android no tem hardware Bluetooth e, portanto, a aplicao
no ir funcionar.
3
4

http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html
http://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html

14

Bluetooth

141

O segundo passo verificar se o Bluetooth est ativado, essa verificao feita com uma
chamada ao mtodo isEnabled() como feito na linha 13. Se o retorno for falso ento ns
iremos perguntar ao usurio se ele gostaria de ativar o adaptador Bluetooth. Para isso necessrio lanar um Intent com action igual BluetoothAdapter.ACTION_REQUEST_ENABLE. Ao chamar o mtodo startActivityForResult() um alerta ir perguntar ao
usurio se ele quer ativar o Bluetooth. A varivel REQUEST_ENABLE_BT pode ser qualquer
valor maior que 0.
O terceiro passo obter todos os dispositivos j pareados, a classe BluetoothAdapter
mantm uma lista desses dispositivos que pode ser adquirida com a chamada ao mtodo getBondedDevices(). Depois, adicionaremos o nome e endereo MAC desses dispositivos
pareados ListView usando o ArrayAdapter.
O mtodo onActivityResult() no precisa de nada especial. Mostrar para o usurio
um feedback j suficiente.

1
2
3
4
5
6
7
8
9
10
11
12

@Override
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == RESULT_OK){
Toast.makeText(this, "Bluetooth is on", Toast.LENGTH_LONG)
.show();
} else if(requestCode == RESULT_CANCELED){
Toast.makeText(this, "Bluetooth is off", Toast.LENGTH_LONG)
.show();
}
}

Algoritmo 14.3: Mtodo onActivityResult()

Voltando ao mtodo onCreate() Observe o comentrio // [1] na linha 31 do algoritmo 14.2, nesse pedao do cdigo iremos adicionar um rodap na ListView com um boto
que iniciar a varredura por novos dispositivos.
Apesar de no ter apresentado este conceito antes, rodaps e cabealhos em ListViews
so bem simples, construa um layout XML e use o LayoutInflater para obter a View,
depois adicione essa View lista usando addFooterView(). Rodaps e cabealhos precisam ser adicionados ListView antes de definir o adaptador. Tambm adicionaremos um
listener a um boto para que ele inicie a varredura. O mtodo startDiscovery() usado
para fazer a varredura.

142
1

14

Bluetooth

deviceList = (ListView) findViewById(R.id.deviceList);

2
3
4

View v = getLayoutInflater().inflate(R.layout.device_list_footer, null);


Button scan = (Button) v.findViewById(R.id.button_scan);

5
6

scan.setOnClickListener(new OnClickListener() {

7
8
9
10
11
12

@Override
public void onClick(View v) {
mBluetoothAdapter.startDiscovery();
}
});

13
14

deviceList.addFooterView(v);

Algoritmo 14.4: Segunda parte do mtodo onCreate()


Porm, chamar startDiscovery() no suficiente, necessrio construir um BroadcastReceiver para capturar os Intents que tenham as informaes dos dispositivos
Bluetooth que foram descobertos.
1

private IntentFilter filter;

2
3
4
5
6
7
8
9
10
11
12
13
14

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {


@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mArrayAdapter.add(device.getName() + "\n"
+ device.getAddress());
}
}
};

Algoritmo 14.5: BroadcastReceiver que captura dispositivos Bluetooth


O que esse BroadcastReceiver est fazendo capturar os Intents e verificar em
quais deles o campo action equivale a BluetoothDevice.ACTION_FOUND. Sendo positivo, obtido um BluetoothDevice que veio no campo extra do Intent. Ento repetimos
o procedimento de adicionar esse dispositivo no adaptador da lista. necessrio criar um IntentFilter5 para ser usado com o BroadcastReceiver.
Porm, para o BroadcastReceiver comear a funcionar necessrio registr-lo com
o mtodo registerReceiver(). importante tambm remove-lo com o mtodo unregisterReceiver() no mtodo onDestroy().
5

http://developer.android.com/reference/android/content/IntentFilter.html

14
1
2
3
4
5

Bluetooth

143

@Override
protected void onDestroy(){
super.onDestroy();
unregisterReceiver(mReceiver);
}

6
7
8
9
10
11
12

@Override
protected void onResume(){
super.onResume();
filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
}

Algoritmo 14.6: Registrando e removendo o BroadcastRegister

Agora, iremos criar trs threads. A primeira, chamada de AcceptThread ser responsvel por instanciar um BluetoothSocketServer e aceitar uma nova conexo vinda de
um outro dispositivo. A segunda, ConnectThread ser usada quando a aplicao funcionar como cliente, essa thread ir tentar estabeceler uma conexo com outro dispositivo. J a
terceira, ConnectedThread ser usada em ambos os casos, para gerenciar a conexo e a entrada e sada de dados. Todas essas threads esto escritas como classes inline (dentro da classe
DeviceListActivity).
A primeira thread, AcceptThread, ir instanciar um BluetoothServerSocket6
usando o mtodo listenUsingRfcommWithServiceRecord(), esse mtodo cria um
socket seguro em um canal Bluetooth, portanto toda informao ser criptografada. Os parmetros so name e uuid, name o nome do servio que nesse caso pode ser qualquer coisa. J
o uuid um identificador universal nico para ser usado com a aplicao, pode-se obter um
nmero usando geradores aleatrios de UUID disponveis na web. A thread ir ficar tentando
escutar uma conexo com o mtodo accept(). Note que este mtodo bloqueante e ir retornar um socket se a conexo foi aceita ou ir gerar uma exceo. Como no queremos aceitar
mais nenhuma conexo, chamamos close() que ir fechar o BluetoothServerSocket
mas no ir fechar o BluetoothSocket criado, mantendo a conexo.
Se o socket conseguir ser instanciado corretamente, iremos chamar o mtodo manageConnection(), responsvel por instanciar a thread que gerencia a conexo.

http://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html

144
1
2

14

Bluetooth

public static String MY_UUID = "d17deaaa-94e9-45ae-8ab0-0f73ef29e417";


public static String NAME = "myBluetoothChat";

3
4
5

private class AcceptThread extends Thread {


private final BluetoothServerSocket mServerSocket;

public AcceptThread(){
BluetoothServerSocket tmp = null;
try {
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
NAME, UUID.fromString(MY_UUID));
} catch(IOException e) { }
mServerSocket = tmp;
}

7
8
9
10
11
12
13
14
15

public void run() {


BluetoothSocket socket = null;
while(true){
try {
socket = mServerSocket.accept();
} catch(IOException e) { break; }

16
17
18
19
20
21
22

if (socket != null) {
manageConnection(socket);
try {
mServerSocket.close();
break;
} catch (IOException e) { }
}

23
24
25
26
27
28
29

30

31
32

Algoritmo 14.7: Classe AcceptThread

O algoritmo 14.8 abaixo mostra a segunda thread, essa thread responsvel por estabelecer
uma conexo com outro dispositivo que esteja rodando a mesma aplicao, por isso usado o
mesmo UUID. O construtor ir receber um BluetoothDevice, que foi selecionado da lista,
e tentar criar a conexo com o mtodo createRfcommSocketToServiceRecord().
A thread ir cancelar a varredura e ir tentar conectar ao socket usando connect(), que tambm uma chamada bloqueante. Se a conexo falhar ou ocorrer um timeout, ele ir gerar uma
exceo. Por segurana, chamamos close() dentro do bloco catch para tentar fechar o
socket e liberar os recursos do aparelho. Se tudo der certo, chamamos o mtodo manageConnection() que veremos logo. Por enquanto deixaremos a explicao da linha 29 em espera,
voltaremos logo nela.

14
1
2
3

Bluetooth

145

private class ConnectThread extends Thread {


private final BluetoothSocket mSocket;
private final BluetoothDevice mDevice;

public ConnectThread(BluetoothDevice device){


BluetoothSocket tmp = null;
mDevice = device;

5
6
7
8

try {
tmp = device.createRfcommSocketToServiceRecord(
UUID.fromString(mUUID));
} catch (IOException e) {}
mSocket = tmp;

9
10
11
12
13

14
15

public void run() {


mBluetoothAdapter.cancelDiscovery();

16
17
18

try {
mSocket.connect();
} catch (IOException openExc) {
try {
mSocket.close();
} catch (IOException closeExc) { }
return;
}

19
20
21
22
23
24
25
26
27

manageConnection(mSocket);
mHandler.post(mShowDialog);

28
29

30
31

Algoritmo 14.8: Classe ConnectThread

O mtodo manageConnection() apenas ir instanciar a thread ConnectedThread.

1
2
3
4

public void manageConnection(BluetoothSocket socket){


tConnected = new ConnectedThread(socket);
tConnected.start();
}

Algoritmo 14.9: Mtodo manageConnection()

Agora veremos a ltima thread, essa resposvel pela entrada e sada de dados atravs do
socket.

146
1
2
3
4

14

Bluetooth

private class ConnectedThread extends Thread {


private final BluetoothSocket mmSocket;
private final InputStream mmInput;
private final OutputStream mmOutput;

public ConnectedThread(BluetoothSocket socket){


mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;

6
7
8
9
10

try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }

11
12
13
14
15

mmInput = tmpIn;
mmOutput = tmpOut;

16
17

18
19

public void run() {


byte[] buffer = new byte[1024];
int bytes;

20
21
22
23

while(true) {
try {
bytes = mmInput.read(buffer);
mMessage = new String(buffer);
mHandler.post(mShowMessage);
} catch(IOException e) { break; }
}

24
25
26
27
28
29
30

31
32

public void write(byte[] bytes) {


try {
mmOutput.write(bytes);
} catch(IOException e) { }
}

33
34
35
36
37
38

public void cancel() {


try{
mmSocket.close();
} catch (IOException e) { }
}

39
40
41
42
43
44

Algoritmo 14.10: Classe ConnectedThread


Primeiro obtemos o InputStream e OutputStream do socket usando os mtodos getInputStream() e getOutputStream(), respectivamente. Voc pode ler e escrever o
fluxo de dados usando read() e write(), necessrio fazer isso numa thread pois essas

14

Bluetooth

147

chamadas so bloqueantes. O mtodo read() ir bloquear enquanto no houver nada para ler
e o mtodo write() pode bloquear caso o outro lado esteja lendo os buffers muito lentamente.
A thread ir repetir enquanto no houver exceo e ficar tentando ler do stream usando
read(). Aqui novamente colocaremos a explicao da linha 28 em espera e voltaremos nela
posteriormente. Faremos tambm um mtodo write() para essa classe, para embrulhar o
mtodo do socket, que poder ser chamado da activity.
Agora temos dois mtodos, um para receber e mostrar a mensagem e outro para enviar a
mensagem. O mtodo writeMessage(), abaixo, abre um alerta com um EditText em
que o usurio pode escrever a mensagem para ser enviada.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public void writeMessage() {


final EditText input = new EditText(this);
AlertDialog.Builder dialog = new AlertDialog.Builder(this)
.setTitle("Send a message")
.setView(input)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String msg = input.getText().toString();
try{
tConnected.write(msg.getBytes());
} catch (NullPointerException e) {}
}
}
});
dialog
.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
}).show();
}

Algoritmo 14.11: Mtodo writeMessage()


O mtodo showMessage() mostra a mensagem recebida em um Toast.
1
2
3

public void showMessage(String msg){


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

Algoritmo 14.12: Mtodo showMessage()


Com isso, podemos voltar e explicar a linha 29 do algoritmo 14.7 e a linha 28 do algoritmo
14.10. Essas so chamadas para o Handler da UI thread. Dessa forma, podemos chamar
mtodos que precisam estar na UI thread, mas fora dela. A chamada ir esperar at que a UI
thread entre para executar.

148
1
2
3
4
5
6
7
8
9
10
11
12
13

14

Bluetooth

private String mMessage;


private final Handler mHandler = new Handler();
private final Runnable mShowMessage = new Runnable() {
public void run() {
showMessage(mMessage);
}
};
private final Runnable mShowDialog = new Runnable() {
public void run() {
writeMessage();
tConnected.cancel();
}
};

Algoritmo 14.13: Handler e Runnable


Os Runnable esto encarregados de executar os dois mtodos, para mostrar e enviar a
mensagem. O Handler ir chamar o Runnable quando estiver na UI thread.
Agora necessrio implementar o mtodo onItemClick() para abrir a conexo com o
dispositivo selecionado.
1
2
3
4

@Override
public void onItemClick(AdapterView<?> arg0, View v, int pos, long id) {
String address = mArrayAdapter.getItem(pos).split("\n")[1];
BluetoothDevice mDevice = mBluetoothAdapter.getRemoteDevice(address);

ConnectThread tConnect = new ConnectThread(mDevice);


tConnect.start();

6
7
8

Algoritmo 14.14: Implementao do mtodo onItemClick()


Obtemos a string do adaptador e usamos split para separar somente o endereo MAC.
Criamos uma ConnectThread para conectar ao dispositivo Bluetooth.
Por ltimo iremos completar o mtodo onCreate() (algoritmo 14.2), onde antes tinha
// [2] complete com uma chamada AcceptThread.
1

deviceList.setOnItemClickListener(this);

2
3
4

AcceptThread tAccept = new AcceptThread();


tAccept.start();

Algoritmo 14.15: Terceira parte do mtodo onCreate()


Com isso, finalizamos a aplicao. recomendvel verificar o projeto completo no repositrio caso ainda existam dvidas. Qualquer tipo de dado em forma de um array de bytes

14

Bluetooth

149

pode ser enviado atravs do canal de comunicao Bluetooth. Nesse exemplo estamos enviando
apenas uma mensagem mas poderia ser qualquer tipo de arquivo.
Esse captulo tentou mostrar uma simples aplicao que utiliza o Bluetooth no envio de
dados. Hoje o Bluetooth est presente em diversos dispositivos: smartphones, fones de ouvido,
caixas de som, aparelhos mdicos, entre outros. O Android, por exemplo, utiliza o NFC para
abrir uma conexo Bluetooth entre dois aparelhos e enviar dados. Isso chamado Android
Beam.

150

14

Bluetooth

C APTULO

15
Internacionalizao

possvel fazer com que sua aplicao d suporte a diversos idiomas, o Android fornece
uma maneira fcil de internacionalizar suas strings e bitmaps.
Antes, ns usamos o arquivo de recursos strings.xml para armazenar qualquer tipo
de texto dos nossos exemplos. Dessa forma voc consegue manter esse tipo de informao
separada do cdigo. Isso permite separar os textos por idioma, quando isso acontece o Android
muda automaticamente o idioma do aplicativo de acordo com o idioma do aparelho.
Para adicionar suporte a mais idiomas, crie diretrios values/ adicionais dentro do diretrio res/ que incluam um hfen e o cdigo ISO do pas no final. Por exemplo, values-es/
para espanhol ou values-pt/ para portugus. Dentro de cada uma dessas pastas voc
deve ter um arquivo string.xml com os textos traduzidos. Por exemplo, em values/string.xml voc deve manter o idioma padro da aplicao, preferencialmente ingls:
1
2
3
4
5

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


<resources>
<string name="title">My Application</string>
<string name="hello_world">Hello World!</string>
</resources>

Algoritmo 15.1: strings.xml padro


Em values-pt/strings.xml voc pode escrever os textos em portugus:
1
2
3
4
5

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


<resources>
<string name="title">Meu aplicativo</string>
<string name="hello_world">Oi Mundo!</string>
</resources>

Algoritmo 15.2: strings.xml em portugus


151

152

15

Internacionalizao

Outros recursos tambm podem aproveitar da localizao, como o caso dos drawables.
Voc pode ter imagens traduzidas dentro dessas pastas.
Os cdigos do idioma seguem o padro ISO-639-11 .
Se voc quiser separar por pas, por exemplo entre portugus brasileiro e portugus de portugal, voc deve adicionar o cdigo do pas ao nome da pasta, precedido por um -r. Portugus
do brasil seria values-pt-rBR/ e de portugal seria values-pt-rPT/.

http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes

Potrebbero piacerti anche