Download pdf - ANDROID Uma Visao Geral

Transcript
Page 1: ANDROID Uma Visao Geral

A

nd

erso

n D

uar

te d

e A

mo

rim

20

11

AN

DR

OID

, um

a v

isã

o g

era

l

Page 2: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 2

Conteúdo Introdução ....................................................................................................................... 10

Atividades ....................................................................................................................... 11

Criar uma atividade .................................................................................................... 11

Implementando uma interface de usuário ................................................................... 12

Declarando a atividade no manifesto.......................................................................... 13

O uso de filtros intenção ............................................................................................. 13

Iniciar uma atividade .................................................................................................. 14

Iniciar uma atividade para um resultado..................................................................... 15

Encerrar uma atividade ............................................................................................... 16

Gerenciando o ciclo de atividade................................................................................ 16

Aplicar o ciclo de vida callbacks ................................................................................ 17

Salvando estado de atividade ...................................................................................... 23

Manipulação de alterações na configuração ............................................................... 25

Coordenar as atividades .............................................................................................. 26

Fragmentos ..................................................................................................................... 27

Filosofia de design ...................................................................................................... 27

Criando um fragmento ................................................................................................ 29

DialogFragment ...................................................................................................... 30

ListFragment ........................................................................................................... 31

PreferenceFragment ................................................................................................ 31

Adicionando uma interface de usuário ................................................................... 31

Criando um layout .................................................................................................. 32

Adicionando um fragmento de uma atividade ........................................................ 32

Adicionando um fragmento sem uma interface de usuário (UI) ............................ 34

Gerenciando fragmentos ......................................................................................... 35

Executando transações com fragmento .................................................................. 35

Page 3: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 3

Comunicando-se com a atividade ........................................................................... 37

Criar callbacks evento para a atividade .................................................................. 38

Adicionando itens à barra de ação .......................................................................... 39

Manuseio do ciclo de vida do fragmento................................................................ 40

Coordenação com o ciclo de vida de atividade ...................................................... 41

Loaders ........................................................................................................................... 43

Resumo API Loader ................................................................................................... 43

Usando carregadores em um aplicativo ...................................................................... 44

Iniciando um Loader ............................................................................................... 45

Reiniciando o Loader.............................................................................................. 46

Usando callbacks do LoaderManager..................................................................... 47

Exemplo ...................................................................................................................... 50

Tarefas e pilha de execução ............................................................................................ 53

Salvando estado de atividade ...................................................................................... 56

Gerenciando tarefas .................................................................................................... 57

Definição de modos de lançamento ............................................................................ 58

Usando o arquivo de manifesto .................................................................................. 59

Usando as opções de intenções ................................................................................... 61

Manipulação de afinidades ......................................................................................... 62

Limpando a pilha de volta .......................................................................................... 64

Iniciando uma tarefa ................................................................................................... 65

Serviços .......................................................................................................................... 66

O básico ...................................................................................................................... 67

Você deve utilizar um serviço ou um thread? ........................................................ 67

Declarando um serviço no manifesto ..................................................................... 69

Criando um serviço iniciado ....................................................................................... 70

Segmentação Android 1.6 ou inferior..................................................................... 70

Page 4: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 4

Estendendo a classe IntentService .......................................................................... 71

Estendendo a classe de serviço ............................................................................... 73

Iniciando um serviço .............................................................................................. 77

Parando um serviço ................................................................................................ 77

Criando um serviço vinculado .................................................................................... 78

Enviando notificações para o usuário ......................................................................... 79

Executando um serviço em primeiro plano ................................................................ 79

Gerenciando ciclo de vida de um serviço ................................................................... 80

Aplicando o ciclo de vida dos callbacks ................................................................. 81

Serviços vinculados ........................................................................................................ 84

O básico ...................................................................................................................... 84

Vinculação a um serviço iniciado ........................................................................... 84

Criando um serviço ligado .......................................................................................... 85

Estendendo a classe Binder .................................................................................... 87

Usando um Messenger ........................................................................................... 90

Vinculação a um serviço............................................................................................. 93

Notas adicionais ...................................................................................................... 95

Gerenciando o ciclo de vida de um serviço de associação ......................................... 96

Processos e threads ......................................................................................................... 98

Processos .................................................................................................................... 98

Ciclo de vida do processo ....................................................................................... 99

Thread ....................................................................................................................... 102

Threads funcionais ................................................................................................ 103

Usando AsyncTask ............................................................................................... 104

Métodos de Thread-safe ....................................................................................... 106

Comunicação entre processos ................................................................................... 106

Interface de usuário ...................................................................................................... 108

Page 5: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 5

Hierarquia de view.................................................................................................... 108

Como o Android desenha views ........................................................................... 109

Layout ....................................................................................................................... 111

Widgets ..................................................................................................................... 112

Eventos UI ................................................................................................................ 114

Menus ....................................................................................................................... 114

Tópicos Avançados .................................................................................................. 115

Adaptadores .......................................................................................................... 115

Estilos e Temas ..................................................................................................... 116

Declarando Layout ....................................................................................................... 117

Escreve o XML ......................................................................................................... 118

Carregar os recursos XML ....................................................................................... 119

Atributos ................................................................................................................... 119

ID .......................................................................................................................... 119

Parâmetros de layout ............................................................................................ 120

Posição de Layout ..................................................................................................... 122

Tamanho, padding e margin ..................................................................................... 122

Criando Menus ............................................................................................................. 124

Menu de Opções ................................................................................................... 124

Menu de Contexto ................................................................................................ 124

Submenu ............................................................................................................... 124

Criando um recurso de menu .................................................................................... 124

Inflar um recurso de menu ........................................................................................ 126

Criando um Menu de Opções ................................................................................... 126

Respondendo à ação do usuário............................................................................ 127

Alterando os itens de menu em tempo de execução ............................................. 129

Criando um Menu de Contexto ................................................................................ 129

Page 6: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 6

Registre uma ListView ......................................................................................... 130

Criando Submenus .................................................................................................... 132

Outras funções do menu ........................................................................................... 132

Grupos de Menu ................................................................................................... 132

Itens de menu verificados ..................................................................................... 133

As teclas de atalho ................................................................................................ 135

Adicionar intenções em menu dinamicamente ..................................................... 136

Permitindo a sua atividade a ser adicionada para outros menus........................... 137

Usando a barra de ação ................................................................................................. 139

Adicionando a barra de ação .................................................................................... 139

Removendo a barra de ação .................................................................................. 140

Adicionando itens de ação ........................................................................................ 141

Usando o ícone do aplicativo como um item de ação .......................................... 142

Usando o ícone do aplicativo para navegar "para cima" ...................................... 143

Adicionando uma exibição de ação .......................................................................... 144

Adicionando abas ..................................................................................................... 146

Adicionando de navegação drop-down .................................................................... 149

Exemplo de SpinnerAdapter e OnNavigationListener ......................................... 150

Estilizando a barra de ação ....................................................................................... 152

Criando caixas de diálogo............................................................................................. 156

Mostrando uma caixa de diálogo .............................................................................. 156

Dispensar um diálogo ............................................................................................... 158

Usando demissão de receptores ............................................................................ 158

Criando um AlertDialog ........................................................................................... 159

Adicionando botões .............................................................................................. 160

Adicionando uma lista .......................................................................................... 161

Adicionando caixas de seleção e botões de rádio ................................................. 161

Page 7: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 7

Criar um ProgressDialog .......................................................................................... 162

Mostrando uma barra de progresso ...................................................................... 163

Criando uma caixa de diálogo personalizada ........................................................... 166

Manipulando eventos de UI .......................................................................................... 169

Os ouvintes de eventos ............................................................................................. 169

Manipuladores de eventos ........................................................................................ 172

Modo de toque .......................................................................................................... 173

Manipulação do foco ................................................................................................ 174

Notificar o usuário ........................................................................................................ 176

Notificação brinde .................................................................................................... 176

Criando notificações brinde .................................................................................. 177

Notificação na barra de status ................................................................................... 179

Criação de notificações da barra de status ............................................................ 180

Notificação de diálogo .............................................................................................. 189

Aplicando estilos e temas ............................................................................................. 190

Definição de estilos .................................................................................................. 190

Herança ................................................................................................................. 191

Propriedades do estilo ........................................................................................... 192

Aplicando estilos e temas para a interface do usuário .............................................. 194

Aplicar um estilo a uma view ............................................................................... 194

Aplicar um tema a uma atividade ou aplicação .................................................... 195

Selecione um tema baseado na versão de plataforma........................................... 196

Usando estilos e temas da plataforma ...................................................................... 196

Recursos de aplicação ................................................................................................... 198

Armazenamento de dados ............................................................................................. 201

Utilizando preferências compartilhadas ................................................................... 201

Preferências do usuário ......................................................................................... 202

Page 8: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 8

Usando o armazenamento interno ............................................................................ 203

Salvando os arquivos de cache ............................................................................. 204

Usando o armazenamento externo ............................................................................ 205

Verificar a disponibilidade dos meios .................................................................. 205

Acessando arquivos em armazenamento externo ................................................. 206

Escondendo seus arquivos a partir da Media Scanner .......................................... 206

Como salvar arquivos que devem ser compartilhados ......................................... 206

Salvando os arquivos de cache ............................................................................. 207

Utilizando bancos de dados ...................................................................................... 208

Banco de dados de depuração ............................................................................... 209

Usando uma conexão de rede ................................................................................... 209

Artigos .......................................................................................................................... 210

Acessibilidade ........................................................................................................... 210

Linguagens e recursos .......................................................................................... 210

Linguagens e localidade ....................................................................................... 211

Fazendo o dispositivo „falar‟ ................................................................................ 211

Interface .................................................................................................................... 213

Toque .................................................................................................................... 213

Gestos ....................................................................................................................... 216

Método de entrada de dados ................................................................................. 216

Criando um método de entrada de dados ............................................................. 223

Ações de desenhos .................................................................................................... 226

Truques de layout: criando layouts eficientes .......................................................... 229

Truques de layout: usando ViewStub ....................................................................... 234

Truques de layout: mesclando layouts...................................................................... 237

ListView, uma otimização ........................................................................................ 244

Live folders ............................................................................................................... 247

Page 9: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 9

Live Wallpapers ........................................................................................................ 252

Usando webViews .................................................................................................... 254

Funcionalidades ........................................................................................................ 255

Caixa de pesquisa ................................................................................................. 255

Sistema ..................................................................................................................... 258

Alocação de memória ........................................................................................... 258

Zipaling oferece uma otimização fácil ................................................................. 260

Nota .............................................................................................................................. 263

Fontes ........................................................................................................................... 263

Fontes das imagens ................................................................................................... 266

Fontes dos artigos ..................................................................................................... 266

Page 10: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 10

Introdução

O Android é um software open-source criado para os celulares e outros dispositivos. O

Android Open Source Project (AOSP), liderado pelo Google, está encarregado da

manutenção e desenvolvimento do Android. Muitos fabricantes de dispositivos

trouxeram ao mercado de dispositivos rodando o Android, e eles são disponíveis ao

redor do mundo.

O objetivo principal é construir uma plataforma de software excelente para usuários de

todos os dias. Uma série de empresas empenhou muitos engenheiros para atingir esse

objetivo, e o resultado é uma produção total de produtos de consumo de qualidade, cuja

fonte é aberta para customização e portabilidade.

Você pode encontrar mais informações sobre o Android a partir destas páginas abaixo:

Filosofia de projeto e objetivos

Interagindo com o projeto

Compatibilidade com Android

Informações sobre licenciamento

Page 11: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 11

Atividades

Uma Activity é um componente do aplicativo que fornece uma tela com a qual os

usuários podem interagir, a fim de fazer algo, como discar o telefone, tirar uma foto,

enviar um e-mail, ou ver um mapa. Para cada atividade é dada uma janela na qual se

desenha sua interface de usuário. A janela normalmente preenche a tela, mas pode ser

menor do que a tela e flutuar em cima de outras janelas.

Um aplicativo normalmente consiste de múltiplas atividades que são frouxamente

ligadas uns aos outros. Normalmente uma atividade em um aplicativo é especificada

como a atividade “principal", que é apresentada ao usuário ao iniciar o aplicativo pela

primeira vez. Cada atividade pode começar outra atividade, a fim de executar ações

diferentes. Cada vez que começa uma nova atividade, a atividade anterior é

interrompida, mas o sistema preserva a atividade em uma pilha (a "pilha de volta").

Quando uma nova atividade começa, é empurrada para a pilha de volta e leva o foco do

usuário. A pilha de volta usa "last in, first out" como mecanismo de fila, então, quando

o usuário está em uma atividade e pressione a tecla BACK, a atividade é removida da

pilha (e destruída) e retoma a atividade anterior.

Quando uma atividade é parada por causa de uma nova atividade, há uma notificação da

alteração no estado através de métodos de retorno da atividade do ciclo de vida.

Existem vários métodos de retorno que uma atividade possa receber, devido a uma

mudança em seu estado. Por exemplo, quando parado, sua atividade deve liberar todos

os objetos grandes, como conexões de rede ou banco de dados. Quando a atividade

recomeça, você pode readquirir os recursos necessários e retomar as ações que foram

interrompidas. Estas transições de estado são todos parte do ciclo de atividade.

Criar uma atividade

Para criar uma atividade, você deve criar uma subclasse da Activity (ou uma subclasse

existente do mesmo). Em sua subclasse, você precisa implementar métodos de callback

que chama o sistema quando ocorrem as transições de atividade entre diversos estados

do seu ciclo de vida, como quando a atividade está sendo criada, parou, recomeçou, ou

foi destruída. Os dois métodos de retorno mais importantes são:

onCreate(): Você deve implementar este método. O sistema chama isso ao criar a sua

atividade. Dentro de sua aplicação, você deve inicializar os componentes essenciais de

Page 12: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 12

sua atividade. Mais importante, este é o lugar onde você deve chamar setContentView()

para definir o layout para a atividade do usuário a interface.

onPause(): O sistema chama este método como o primeiro indício de que o usuário está

saindo de sua atividade (embora nem sempre signifique que a atividade está sendo

destruída). Isso geralmente é onde você deve cometer quaisquer alterações que devem

ser mantidas para além da sessão atual do usuário (porque o usuário pode não voltar).

Existem vários métodos de retorno do ciclo de vida de outros que você deve usar a fim

de proporcionar uma experiência de usuário mais fluida entre as atividades e manipular

interrupções inesperadas que causem a sua atividade a ser interrompida e até mesmo

destruída.

Implementando uma interface de usuário

A interface de usuário para uma determinada atividade é assegurada por uma hierarquia

de pontos de vista - objetos derivam da classe View. Cada exibição controla um

determinado espaço retangular dentro da janela da atividade e pode responder a

interação do usuário. Por exemplo, uma visão pode ser um botão que inicia uma ação

quando o usuário tocá-la.

Android fornece um número de pontos de vista prontos que você pode usar para criar e

organizar seu layout. "Widgets" são vistas que proporcionam um visual (interativo) de

elementos para a tela, como um botão, um campo texto, checkbox, ou apenas uma

imagem. "Esquemas" são pontos de vista derivados de ViewGroup que fornecem um

modelo de layout exclusivo para a estrutura derivada, como um layout linear, um layout

de grade, ou a disposição relativa. Você pode criar uma subclasse da View e

ViewGroup (ou subclasses existentes) para criar seus próprios widgets e layouts e

aplicá-las ao seu layout atividade.

A maneira mais comum para definir um layout usando pontos de vista é com um

arquivo XML salvo disposição em recursos do seu aplicativo. Dessa forma, você pode

manter o design da sua interface de usuário separadamente do código fonte que define o

comportamento da atividade. Você pode definir o layout da interface do usuário para a

sua atividade com setContentView(), passando a identificação do recurso para o layout.

No entanto, você também pode criar novas Views no seu código de atividade e construir

Page 13: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 13

uma hierarquia de vista através da inserção de novos Views em um ViewGroup, em

seguida, usar esse esquema, passando a raiz ViewGroup para setContentView().

Declarando a atividade no manifesto

Você deve declarar a sua atividade no arquivo de manifesto para que ele seja acessível

para o sistema. Para declarar sua atividade, abra o arquivo e adicione um <activity>

como um filho do <application>. Por exemplo:

<manifest ... >

<application ... >

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

...

</application ... >

...

</manifest >

Existem vários outros atributos que podem ser incluídos nesse elemento, para definir

propriedades, como o rótulo para a atividade, um ícone para a atividade, ou um tema ao

estilo de interface do usuário da atividade.

O uso de filtros intenção

Um <activity> também pode especificar filtros diferentes, usando o <intent-filter>, a

fim de declarar como outros componentes de aplicação podem ativá-lo.

Quando você cria um novo aplicativo usando as ferramentas do Android SDK, a

atividade de topo que é criada para você automaticamente inclui a intenção de filtro que

declara a atividade e responde à ação "principal" e deve ser colocado no "lançador" da

categoria. A intenção parece filtro como este:

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

O elemento <action> especifica que este é o "principal ponto de entrada" para o

aplicativo. O elemento <category> especifica que esta atividade deve ser listada no

sistema lançador de aplicação.

Page 14: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 14

Se você pretende que o seu aplicativo seja auto-suficiente e não permita que outras

aplicações ativem as suas atividades, então você não precisa de nenhum outro filtro de

intenção. Apenas uma atividade deve ter a ação "principal" e "lançador" da categoria,

como no exemplo anterior. Atividades que você não deseja disponibilizar para outros

aplicativos não devem ter a intenção de filtros e você pode iniciá-los usando as

intenções explícitas.

No entanto, se você quiser a sua atividade para responder às intenções implícitas que

são entregues a partir de outras aplicações (e suas próprias), então você deve definir

filtros de intenção adicional para a sua atividade. Para cada tipo de intenção para o qual

pretende responder, você deve incluir um <intent-filter> que inclui um elemento

<action> e, opcionalmente, um elemento <category> e/ou um <data>. Estes elementos

especificam o tipo de intenções para que sua atividade possa responder.

Iniciar uma atividade

Você pode iniciar outra atividade, chamando startActivity(), passando-lhe uma Intent

que descreve a atividade que deseja iniciar. A intenção especifica qualquer atividade

exatamente o que deseja iniciar ou descreve o tipo de ação que deseja executar (e o

sistema seleciona a atividade adequada para você, que pode mesmo ser de um aplicativo

diferente). A intenção também pode transportar pequenas quantidades de dados a serem

utilizados pela atividade que é iniciada.

Quando se trabalha dentro de sua própria aplicação, muitas vezes você precisa

simplesmente lançar uma atividade conhecida. Você pode fazer isso criando uma

intenção que define explicitamente a atividade que deseja iniciar, usando o nome da

classe. Por exemplo, aqui está como uma atividade inicia outra atividade denominada

SignInActivity:

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

startActivity(intent);

No entanto, sua aplicação pode também querer executar alguma ação, como enviar um

e-mail, mensagem de texto, ou atualização de status, usando dados de sua atividade.

Neste caso, sua aplicação não pode ter as suas próprias atividades para realizar tais

ações, para que você possa aproveitar ao invés das atividades previstas por outras

aplicações no dispositivo, que pode executar as ações para você. Este é o lugar onde as

Page 15: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 15

intenções são realmente valiosas, você pode criar uma intenção que descreve uma ação

que deseja executar e o sistema inicia a atividade adequada a partir de outro aplicativo.

Se houver múltiplas atividades que podem manipular a intenção, então o usuário pode

selecionar qual usar. Por exemplo, se você quer permitir que o usuário envie uma

mensagem de e-mail, você pode criar a seguinte intenção:

Intent intent = new Intent(Intent.ACTION_SEND);

intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);

startActivity(intent);

O EXTRA_EMAIL extra adicionado é uma matriz de seqüência de endereços de e-mail

para onde o e-mail deve ser enviado. Quando um aplicativo de e-mail responde a esta

intenção, ele lê a matriz de cadeia prevista na „extra‟ e as coloca no campo "Para" do

formulário de composição de e-mail.

Iniciar uma atividade para um resultado

Às vezes, você pode querer receber um resultado da atividade que você começar. Nesse

caso, iniciar a atividade, chamando startActivityForResult() (em vez de startActivity() ).

Para então receber o resultado da atividade subseqüente, aplicar o onActivityResult(),

método de retorno. Quando a atividade subseqüente é feita, ele retorna um resultado de

uma Intent para o seu método onActivityResult().

Por exemplo, talvez você queira que o usuário escolha um de seus contatos, para que

sua atividade pode fazer algo com as informações desse contato. Veja como você pode

criar uma intenção e manipular o resultado:

private void pickContact() {

// Create an intent to "pick" a contact, as defined by the content provider URI

Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);

startActivityForResult(intent, PICK_CONTACT_REQUEST);

}

@Override

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

// If the request went well (OK) and the request was PICK_CONTACT_REQUEST

if (resultCode == Activity.RESULT_OK && requestCode ==

PICK_CONTACT_REQUEST) {

// Perform a query to the contact's content provider for the contact's name

Cursor cursor = getContentResolver().query(data.getData(),

new String[] {Contacts.DISPLAY_NAME}, null, null, null);

if (cursor.moveToFirst()) { // True if the cursor is not empty

int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);

String name = cursor.getString(columnIndex);

Page 16: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 16

// Do something with the selected contact's name...

}

}

}

Este exemplo mostra a lógica básica que você deve usar seu método onActivityResult()

para lidar com um resultado de atividade. A primeira condição verifica se a solicitação

foi bem-sucedida, se for, então o resultCode será RESULT_OK e se a solicitação para

que este resultado está respondendo é conhecido, neste caso, o requestCode coincide

com o segundo parâmetro enviada com startActivityForResult(). De lá, o código

manipula o resultado de atividade, consultando os dados retornados de uma Intent.

O que acontece é, um ContentResolver executa uma consulta contra um provedor de

conteúdo, que retorna um Cursor que permite que os dados consultados possam serem

lidos.

Encerrar uma atividade

Você pode encerrar uma atividade chamando seu método finish(). Você também pode

encerrar uma atividade separada que já começou chamando finishActivity() .

Nota: Na maioria dos casos, você não deve terminar explicitamente uma atividade com

estes métodos. Como discutido na seção seguinte sobre o ciclo de vida de atividade, o

sistema Android gerencia a vida de uma atividade para você, então você não precisa

terminar a sua própria atividade. Chamar esses métodos pode afetar negativamente a

experiência do usuário e só deve ser usado quando você realmente não quer que o

usuário retorne a esta instância da atividade.

Gerenciando o ciclo de atividade

Gerenciar o ciclo de vida de suas atividades através da implementação de métodos de

retorno é essencial para desenvolver uma aplicação forte e flexível. O ciclo de vida de

uma atividade está diretamente afetada pela sua associação com outras atividades, a sua

missão e voltar à pilha.

Uma atividade pode existir em três estados, essencialmente:

Retomado: A atividade está em primeiro plano da tela e tem o foco do usuário. (Esse

estado é também por vezes referido como "run".)

Page 17: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 17

Em pausa: Outra atividade está em primeiro plano e tem foco, mas este é ainda visível.

Ou seja, outra atividade é visível na parte superior de um presente e que a atividade é

parcialmente transparente ou não cobre a tela inteira. Uma atividade em pausa está

completamente viva (o objeto Activity é mantido na memória, ele mantém todas as

informações do estado e membro, e permanece preso ao gerenciador de janelas), mas

pode ser morta pelo sistema em situações de pouca memória.

Parado: A atividade é totalmente obscurecida por outra atividade (a atividade está

agora em "background"). A atividade parada também está ainda viva (o objeto Activity

é mantido na memória, ele mantém todas as informações do estado e membro, mas não

está ligado ao gerenciador de janelas). No entanto, já não é visível para o usuário e pode

ser morto pelo sistema quando a memória é necessária em outro lugar.

Se uma atividade está em pausa ou parada, o sistema pode retirá-la da memória, quer

por pedir para terminar (chamando seu finish()), ou simplesmente matar o processo.

Quando a atividade é aberta novamente (depois de ter sido concluído ou morto), ela

deve ser criada por toda parte.

Aplicar o ciclo de vida callbacks

Quando uma atividade transita entrando e saindo dos diferentes estados descritos acima,

ele é notificado através de vários métodos de retorno. Todos os métodos de callback são

ganchos que você pode substituir para fazer um trabalho adequado quando o estado da

sua atividade muda. A atividade seguinte inclui cada um dos métodos de ciclo de vida

fundamentais:

public class ExampleActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// The activity is being created.

}

@Override

protected void onStart() {

super.onStart();

// The activity is about to become visible.

}

@Override

protected void onResume() {

super.onResume();

// The activity has become visible (it is now "resumed").

}

Page 18: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 18

@Override

protected void onPause() {

super.onPause();

// Another activity is taking focus (this activity is about to be "paused").

}

@Override

protected void onStop() {

super.onStop();

// The activity is no longer visible (it is now "stopped")

}

@Override

protected void onDestroy() {

super.onDestroy();

// The activity is about to be destroyed.

}

}

Nota: A implementação destes métodos do ciclo de vida deve sempre chamar a

implementação da superclasse antes de fazer qualquer trabalho, conforme mostrado

nos exemplos acima.

Juntos, esses métodos definem o ciclo de vida de uma atividade. Ao implementar esses

métodos, você pode monitorar três loops aninhados no ciclo de vida de atividade:

A vida inteira de uma atividade acontece entre a chamada para onCreate() e a

chamada para onDestroy(). Sua atividade deve executar a instalação do "Estado"

global (tal como a definição de layout) em onCreate(), e liberar todos os recursos

remanescentes em onDestroy(). Por exemplo, se a sua atividade tem um

segmento em execução em segundo plano para transferir os dados da rede, ele

pode criar esse tópico em onCreate() e depois parar o segmento em onDestroy().

O tempo de vida visível de uma atividade acontece entre a chamada para

onStart() e a chamada para onStop(). Durante este tempo, o usuário pode ver a

atividade na tela e interagir com ele. Por exemplo, onStop() é chamado quando

inicia uma nova atividade e esta não é mais visível. Entre estes dois métodos,

você pode manter os recursos que são necessários para mostrar a atividade para

o usuário. Por exemplo, você pode registrar um BroadcastReceiver em onStart()

para monitorar as mudanças que impactam sua interface do usuário, e cancelar o

registro em onStop() quando o usuário não pode mais ver o que você está sendo

exibido. O sistema pode chamar onStart() e onStop() várias vezes durante toda a

vida útil da atividade, como a atividade se alterna entre visível e oculta para o

usuário.

Page 19: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 19

O tempo de vida do primeiro plano de uma atividade acontece entre a chamada

para onResume() e a chamada para onPause(). Durante este tempo, a atividade

está na frente de todas as outras atividades na tela e tem foco de entrada do

usuário. Uma atividade pode freqüentemente transitar para dentro e fora do

plano, por exemplo, onPause() é chamado quando o dispositivo vai dormir ou

quando uma caixa de diálogo aparece. O código desses dois métodos deve ser

bastante leve, para evitar transições lentas que fazem o usuário esperar.

A figura 1 ilustra esses laços e os caminhos de uma atividade que podem levar a esses

estados.

Figura 1. O ciclo de vida de atividade.

Page 20: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 20

Tabela 1. Um resumo do ciclo de vida do callback métodos atividade.

Método Descrição Killable depois?

Seguinte

onCreate()

Chamado quando a atividade

é criada pela primeira vez.

Isto é onde você deve fazer

tudo do seu conjunto estático

normal - criar pontos de vista,

vincular dados em listas, e

assim por diante. Sempre

seguido por onStart() .

Não onStart()

onRestart()

Chamado depois que a

atividade foi interrompida,

pouco antes de ele ser iniciado

novamente. Sempre seguido

por onStart()

Não onStart()

onStart()

Chamado imediatamente antes

da atividade tornar-se visível

para o usuário. Seguido por

onResume() se a atividade

vem para o primeiro plano, ou

onStop() se torna oculto.

Não onResume()

onResume()

Chamado imediatamente antes

da atividade passar a interagir

com o usuário. Neste ponto, a

atividade está no topo da pilha

de atividade. Sempre seguido

por onPause() .

Não onPause()

Page 21: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 21

onPause()

Chamado quando o sistema

está prestes a começar a

retomar a outra atividade. Este

método é geralmente usado

para confirmar as alterações

não salvas, dados persistentes,

animações stop e outras coisas

que podem estar consumindo

CPU, e assim por diante. Ele

deve fazer tudo o que ele faz

muito rapidamente, porque a

próxima atividade não será

retomada até que ele retorne.

Seguidas por onResume() se a

atividade retorna para a frente,

ou por onStop() se torna

invisível para o usuário.

Sim onResume() ou

onStop()

onStop()

Chamado quando a atividade

já não é visível para o usuário.

Isso pode acontecer porque

ele está sendo destruído, ou

porque outra atividade (seja

um existente ou uma nova) foi

retomado e está cobrindo-o.

Seguidas por onRestart() se a

atividade está voltando para

interagir com o usuário, ou

por onDestroy() se essa

atividade está indo embora.

Sim onRestart() ou

onDestroy()

Page 22: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 22

onDestroy()

Chamado antes que a

atividade é destruída. Esta é a

chamada final que a atividade

irá receber.Poderia ser

chamada, quer porque a

atividade está acabando

(alguém chamado finish()

nela), ou porque o sistema

está destruindo essa instância

da atividade para economizar

espaço. Você pode distinguir

entre estes dois cenários com

o isFinishing().

Sim nada

A coluna chamada "killable depois?" indica se o sistema pode matar o processo que

acolhe a atividade a qualquer momento após o método retornar, sem executar outra

linha. Três métodos são marcados como "sim": (onPause() , onStop() , e onDestroy() ).

onPause() é o primeiro dos três, uma vez que a atividade é criada, onPause() é o último

método que é garantido para ser chamado antes que o processo pode ser morto, se o

sistema deve recuperar a memória em caso de emergência, então onStop() e onDestroy()

não podem ser chamados. Portanto, você deve usar onPause() para escrever dados

persistentes para armazenamento. No entanto, você deve ser seletivo sobre quais

informações devem ser mantidas durante onPause(), porque os procedimentos de

bloqueio neste método bloqueiam a passagem para a próxima atividade e retardam a

experiência do usuário.

Métodos que são marcados como "Não" na coluna killable protegem o processo da

atividade de ser morto desde o momento em que são chamados. Assim, uma atividade é

killable a partir do momento onPause() e retorna quando onResume() é chamado. Não

será novamente killable até onPause() seja novamente chamado e retornado.

Nota: uma atividade que não é tecnicamente "killable" por esta definição na tabela 1

ainda pode ser morta pelo sistema, mas isso vai acontecer apenas em circunstâncias

extremas, quando não há outro recurso.

Page 23: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 23

Salvando estado de atividade

A introdução à Gestão do Ciclo de Atividade menciona brevemente que, quando uma

atividade está em pausa ou parada, o estado da atividade é mantido. Isto é verdade

porque a Activity ainda está retida na memória quando está em pausa ou parada, todas

as informações sobre seus membros e estado atuais ainda estão vivos. Assim, qualquer

alteração que o usuário fez no âmbito da atividade é retida na memória, de modo que

quando a atividade retorna para o primeiro plano (quando ele "retoma"), essas

mudanças ainda estão lá.

Figura 2. As duas formas em que para a atividade um usuário retorna ao foco com seu estado intacto,

quer a atividade é interrompida, e retomada em seguida, o estado de atividade permanece intacta (à

esquerda), ou a atividade é destruído, então recriada e a atividade deve restaurar o estado da atividade

anterior (direita).

No entanto, quando o sistema destrói uma atividade, a fim de recuperar a memória, a

Activity é destruída, então o sistema não pode simplesmente continuar com o seu estado

intacto. Em vez disso, o sistema deve recriar a Activity se o usuário navega de volta

para ele. No entanto, o usuário não sabe que o sistema destrói e recria a atividade e,

assim, provavelmente espera que a atividade seja exatamente como era. Nessa situação,

Page 24: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 24

você pode garantir que informações importantes sobre o estado de atividade são

preservadas através da implementação de um método de retorno adicional que permite

que você salve as informações sobre o estado de sua atividade e, em seguida, restaura

quando o sistema recria a atividade.

O método de callback em que você pode salvar informações sobre o estado atual da sua

atividade é onSaveInstanceState(). O sistema chama este método antes de fazer a

atividade vulnerável a ser destruída e passa-lhe um objeto Bundle. O Bundle é o lugar

onde você pode armazenar informações de estado sobre a atividade como pares valor-

nome, utilizando métodos como putString(). Então, se o sistema mata a atividade e o

usuário navega de volta para sua atividade, o sistema passa o Bundle para onCreate()

para que você possa restaurar o estado de atividade que tenha sido guardado durante

onSaveInstanceState(). Se não há informações do estado para restaurar, em seguida, o

Bundle que passou a onCreate() se torna nulo.

Nota: Não há nenhuma garantia de que onSaveInstanceState() será chamado antes de

sua atividade ser destruída, porque há casos em que não será necessário salvar o

estado (como quando o usuário deixa a sua atividade com a chave de volta, porque a

usuário explicitamente encerra as atividades). Se o método for chamado, ele sempre é

chamado antes de onStop() e, possivelmente, antes de onPause().

No entanto, mesmo se você não faz nada e não implementar onSaveInstanceState(),

alguns estados de atividade são restaurados pela Activity de implementação padrão da

classe de onSaveInstanceState(). Especificamente, a implementação padrão chama

onSaveInstanceState() para cada View no layout, que permite fornecer informações

sobre si que devem ser salvos. Quase todos os widgets no âmbito Android implementam

este método, de modo que qualquer mudança visível para o interface do usuário são

automaticamente salvas e restauradas quando sua atividade é recriada. Por exemplo, o

EditText salva qualquer texto digitado pelo usuário e o CheckBox widget salva se é

marcado ou não. O único trabalho exigido por você é fornecer uma identificação única

(com o android:id) para cada elemento gráfico que deseja salvar seu estado. Se um

elemento não tem um ID, então ele não pode salvar seu estado.

Você também pode parar explicitamente de salvar em seu layout seu estado, definindo o

android:saveEnabled para "false" ou chamando o setSaveEnabled(). Normalmente, você

Page 25: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 25

não deve desativar isso, mas você pode caso queira restaurar o estado da atividade de

interface diferente.

Embora a implementação padrão de onSaveInstanceState() salva as informações úteis

sobre a atividade da sua interface, você ainda pode precisar substituí-lo para guardar

informações adicionais. Por exemplo, você talvez precise salvar valores de um membro

que mudou na vida da atividade (que poderiam se correlacionar com os valores

restaurados na interface do usuário, mas os membros que detêm esses valores UI não

são restaurados, por padrão).

Como a implementação padrão de onSaveInstanceState() ajuda a salvar o estado da

interface do usuário, se você substituir o método para salvar informações de estado

adicionais, você deve sempre chamar a implementação da superclasse de

onSaveInstanceState() antes de fazer qualquer trabalho.

Nota: Devido ao onSaveInstanceState() não ser garantido de ser chamado, você deve

usá-lo apenas para registrar o estado transiente da atividade (o estado da interface do

usuário), você nunca deve usá-lo para armazenar dados persistentes. Em vez disso,

você deve usar onPause() para armazenar dados persistentes (como os dados que

devem ser salvos em um banco de dados) quando o usuário deixa a atividade.

Uma boa maneira de testar a capacidade do seu aplicativo para restaurar seu estado é

simplesmente girar o dispositivo para fazer alterações na orientação da tela. Quando da

mudança de orientação da tela, o sistema destrói e recria a atividade a fim de aplicar

recursos alternativos que possam estar disponíveis para a nova orientação. Por esta

razão, é muito importante para sua atividade restaurar completamente o seu estado

quando ele é recriado, pois os usuários regularmente giram a tela ao usar aplicações.

Manipulação de alterações na configuração

Algumas configurações de dispositivo podem mudar durante a execução (tais como a

orientação da tela, a disponibilidade de teclado e idioma). Quando essa mudança ocorre,

o Android reinicia a atividade em execução (onDestroy() é chamado, seguido

imediatamente por onCreate()). O comportamento reiniciar é projetado para ajudar a sua

candidatura a se adaptar às novas configurações automaticamente recarregando a sua

aplicação com recursos alternativos que você forneceu. Se você projeta sua atividade

Page 26: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 26

para lidar adequadamente com este evento, vai ser mais resistentes a eventos

inesperados no ciclo de atividade.

A melhor maneira de lidar com uma mudança de configuração, tais como uma mudança

na orientação da tela, é simplesmente preservar o estado do seu aplicativo usando

onSaveInstanceState() e onRestoreInstanceState() (ou onCreate() ), como discutido na

seção anterior.

Coordenar as atividades

Quando uma atividade começa outra, ambas experimentam as transições do ciclo de

vida. A primeira atividade faz uma pausa e para (embora, não vai parar se ele ainda está

visível ao fundo), enquanto a outra atividade é criada. Caso esses dados compartilham

atividades salvas em disco ou em outro lugar, é importante entender que a primeira

atividade não está completamente parada antes de a segunda ser criada. Pelo contrário, o

processo de iniciar o segundo se sobrepõe ao processo de parar o primeiro.

A ordem dos retornos do ciclo de vida é bem definida, especialmente quando as duas

atividades estão no mesmo processo e está começando um do outro. Aqui está a ordem

das operações que ocorrem quando a atividade A começa atividade B:

1. O método onPause() da atividade A é executado.

2. Os métodos onCreate(), onStart(), e onResume() de B são executados em

seqüência. (Atividade B agora tem o foco do usuário.)

3. Então, se uma atividade não é mais visível na tela, a sua onStop() é executada.

Esta seqüência previsível de callbacks do ciclo de vida permite-lhe gerir a transição de

informações de uma atividade para outra. Por exemplo, se você deve escrever em um

banco de dados quando a primeira atividade pára para que esta atividade pode lê-lo,

então você deve escrever para o banco de dados durante onPause() em vez de durante

onStop().

Page 27: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 27

Fragmentos

Um Fragment representa um comportamento ou uma parte da interface de usuário em

uma Activity. Você pode combinar vários fragmentos em uma única atividade para

construir uma interface multi-painel e reutilização de um fragmento de atividades

múltiplas. Você pode pensar em um fragmento como uma seção modular de uma

atividade, que tem seu próprio ciclo de vida, recebe os seus próprios eventos de entrada,

e que você pode adicionar ou remover, enquanto a atividade está em execução.

Um fragmento deve sempre ser incorporado em uma atividade e o ciclo de vida do

fragmento é diretamente afetado pelo ciclo de vida da atividade de acolhimento. Por

exemplo, quando a atividade é interrompida, assim são todos os fragmentos nele, e

quando a atividade é destruída, assim são todos os fragmentos. No entanto, enquanto

uma atividade está em execução (que é na retomada do ciclo de vida do estado), você

pode manipular cada fragmento de forma independente, como adicionar ou remover.

Quando você executa uma operação deste tipo de fragmento, você também pode

adicioná-la a uma pilha de volta que é gerenciado pela atividade de cada pilha de volta

na entrada da atividade que é um registro da transação de fragmento que ocorreu. A

volta da pilha permite que o usuário possa reverter uma transação (navegar para trás),

pressionando a tecla BACK.

Quando você adiciona um fragmento como uma parte do seu layout, ele vive em um

ViewGroup dentro da view de hierarquia e define o seu próprio layout de pontos de

vista. Você pode inserir um fragmento em seu layout declarando o fragmento na

atividade de distribuição de arquivos, como <fragment>, ou a partir de seu código de

aplicativo, adicionando-o a um já existente ViewGroup. No entanto, um fragmento não

é obrigado a fazer parte do esquema de atividade, você também pode utilizar um

fragmento como um trabalhador invisível para a atividade.

Filosofia de design

Android apresenta fragmentos no Android 3.0 (API Level "Honeycomb"),

principalmente para apoiar projetos mais dinâmicos e flexíveis de interface do usuário

em telas grandes, como os Tablets. Como uma tela de tablet é muito maior do que a de

um telefone, há mais espaço para combinar e trocar os componentes de interface do

usuário. Fragmentos permitem tais projetos sem a necessidade de gerenciar mudanças

Page 28: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 28

complexas à hierarquia vista. Ao dividir o layout de uma atividade em fragmentos, você

se torna capaz de modificar a aparência da atividade em tempo de execução e preservar

essas mudanças em uma pilha de volta que é gerenciada pela atividade.

Por exemplo, um aplicativo de notícias pode usar um fragmento para mostrar uma lista

de artigos à esquerda e outro fragmento para mostrar um artigo à direita, então os

fragmentos aparecem em uma atividade, lado a lado, e cada fragmento tem seu próprio

conjunto do ciclo de vida, métodos callback e lidam com seus próprios eventos de

entrada do usuário. Assim, em vez de usar uma atividade para selecionar um artigo e

outra atividade para ler o artigo, o usuário pode selecionar um artigo e ler tudo dentro da

mesma atividade, conforme ilustrado na figura 1.

Figura 1. Um exemplo de como dois módulos de interface do usuário que normalmente são separados em

duas atividades podem ser combinados em uma atividade, utilizando fragmentos.

Um fragmento deve ser um componente modular e reutilizável em sua aplicação. Ou

seja, porque o fragmento define o seu próprio layout e seu próprio comportamento,

usando seu próprio ciclo de vida callbacks, você pode incluir um fragmento em

múltiplas atividades. Isto é especialmente importante porque permite adaptar a sua

experiência de usuário para diferentes tamanhos de tela. Por exemplo, você pode incluir

vários fragmentos de uma atividade apenas quando o tamanho da tela é suficientemente

grande, e, quando não é, lançar atividades distintas que utilizam diferentes fragmentos.

Por exemplo, para continuar com o aplicativo de notícia, a aplicação pode inserir dois

fragmentos da atividade, quando rodando em uma grande tela extra (um tablet, por

exemplo). No entanto, em um tamanho de tela normal (um telefone, por exemplo), não

há lugar suficiente para os dois fragmentos, de modo a Atividade A inclui somente o

fragmento para a lista de artigos, e quando o usuário seleciona um artigo, ele começa a

Page 29: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 29

Atividade B, que inclui o fragmento para ler o artigo. Assim, a aplicação suporta os

padrões de projeto sugerido na figura 1.

Criando um fragmento

Figura 2. O ciclo de vida de um fragmento (enquanto a sua atividade está em execução).

Page 30: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 30

Para criar um fragmento, você deve criar uma subclasse de Fragment (ou uma subclasse

existente do mesmo). O código da classe Fragment se parece muito com uma Activity.

Ele contém métodos de retorno semelhante a uma atividade, como onCreate(), onStart(),

onPause(), e onStop(). Na verdade, se você está convertendo uma aplicação Android

existentes para usar fragmentos, você pode simplesmente mover o código de métodos

de retorno de sua atividade sobre os métodos de retorno de seus respectivos fragmentos.

Normalmente, você deve implementar pelo menos os métodos do ciclo de vida a seguir:

onCreate(): O sistema chama isso ao criar o fragmento. Dentro de sua aplicação, você

deve inicializar os componentes essenciais do fragmento que pretende manter quando o

fragmento é pausado ou parado, então retomado.

onCreateView(): O sistema chama isso quando está na hora de extrair o fragmento de

sua interface de usuário pela primeira vez. Para desenhar uma interface para o seu

fragmento, você deve retornar um View a partir deste método que é a raiz do fragmento

do seu layout. Você pode retornar nulo se o fragmento não fornece uma interface do

usuário.

onPause(): O sistema chama este método como o primeiro indício de que o usuário está

saindo do fragmento (embora nem sempre significa que o fragmento está sendo

destruído). Isso geralmente é onde você deve cometer quaisquer alterações que devem

ser mantidas para além da sessão atual do usuário (porque o usuário pode não voltar).

A maioria dos aplicativos devem implementar pelo menos estes três métodos para cada

fragmento, mas existem vários métodos de retorno que você também deve usar para

lidar com diferentes fases do ciclo de vida do fragmento. Todos os métodos de retorno

do ciclo de vida são discutidos mais adiante, na seção sobre o manuseio do Ciclo de

Vida do fragmento.

Existem também algumas subclasses que você pode querer estender:

DialogFragment

Mostra uma janela flutuante. Usar essa classe para criar uma caixa de diálogo é uma boa

alternativa para usar os métodos auxiliares de diálogo na Activity, porque você pode

incorporar um fragmento de diálogo para a volta da pilha de fragmentos gerido pela

atividade, permitindo que o usuário retorne a um fragmento rejeitado.

Page 31: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 31

ListFragment

Exibe uma lista de itens que são gerenciados por um adaptador (como um

SimpleCursorAdapter), semelhante ao ListActivity. Ele fornece diversos métodos para

gerenciar uma lista, como o onListItemClick() de callback para manipular eventos de

clique.

PreferenceFragment

Exibe uma hierarquia de objetos Preference como uma lista, semelhante à

PreferenceActivity. Isso é útil quando se cria um "settings" para sua aplicação.

Adicionando uma interface de usuário

Um fragmento é normalmente usado como parte de uma atividade de interface de

usuário e contribui com a sua própria disposição para a atividade.

Para fornecer um layout de um fragmento, você deve implementar o onCreateView(),

que o sistema Android chama quando é hora do fragmento ser desenhado no layout. A

implementação deste método deve retornar um View que é a raiz do fragmento do seu

layout.

Nota: Se o fragmento é uma subclasse de ListFragment, a implementação padrão

retorna um ListView de onCreateView(), então você não precisa implementá-lo.

Para devolver um layout de onCreateView(), você pode retirá-lo a partir de um layout

de recursos definidos em XML e o desenvolve. Para ajudá-lo a fazê-lo, onCreateView()

fornece um LayoutInflater objeto.

Por exemplo, aqui está uma subclasse de Fragment que carrega um layout a partir da

example_fragment.xml:

public static class ExampleFragment extends Fragment {

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// Inflate the layout for this fragment

return inflater.inflate(R.layout.example_fragment, container, false);

}

}

Page 32: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 32

Criando um layout

No exemplo acima, R.layout.example_fragment é uma referência a um recurso chamado

layout example_fragment.xml salvo na aplicação dos recursos.

O parâmetro passado para onCreateView() é o pai ViewGroup (da atividade do layout),

em que o layout do fragmento será inserido. O parâmetro savedInstanceState é um

Bundle que fornece dados sobre a instância anterior do fragmento, se o fragmento está

sendo retomado.

O método inflate() utiliza três argumentos:

A identificação de recurso do layout que você deseja inserir.

O ViewGroup ser o pai do layout já em utilização. Passando o container é

importante para que o sistema possa aplicar os parâmetros de layout para o

modo de exibição raiz do layout inflado, especificado pela posição do pai em

que ele está indo.

Um booleano que indica se o layout desenvolvido deverá ser anexado ao

ViewGroup (segundo parâmetro) durante a chamada do procedimento inflate().

(Neste caso, isso é falso, porque o sistema já está inserindo o layout inflado no

container de passagem verdade seria criar um grupo de vista redundantes no

layout final.)

Adicionando um fragmento de uma atividade

Normalmente, um fragmento contribui com uma parcela de UI para a atividade de

acolhimento, que é incorporado como parte da hierarquia da visão da atividade de

conjunto. Há duas maneiras com as quais você pode adicionar um fragmento para o

layout de atividade:

Declare o fragmento dentro atividade de layout do arquivo.

Neste caso, você pode especificar propriedades de layout para o fragmento como se

fosse uma exibição. Por exemplo, aqui está o arquivo de layout para uma atividade

com dois fragmentos:

Page 33: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 33

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

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

android:orientation="horizontal"

android:layout_width="match_parent"

android:layout_height="match_parent">

<fragment android:name="com.example.news.ArticleListFragment"

android:id="@+id/list"

android:layout_weight="1"

android:layout_width="0dp"

android:layout_height="match_parent" />

<fragment android:name="com.example.news.ArticleReaderFragment"

android:id="@+id/viewer"

android:layout_weight="2"

android:layout_width="0dp"

android:layout_height="match_parent" />

</LinearLayout>

O atributo android:name na <fragment> especifica o Fragment para instanciar no

layout.

Quando o sistema cria esse layout, ele instancia cada fragmento especificado no

layout e chama o onCreateView() para cada um, para recuperar o layout de cada

fragmento. O sistema insere a View retornada pelo fragmento diretamente no

local do elemento <fragment>.

Nota: Cada fragmento requer um identificador único que o sistema pode usar

para restaurar o fragmento se a atividade for reiniciada (e que você pode usar

para capturar o fragmento para realizar transações, como removê-lo). Existem

três formas para fornecer uma identificação de um fragmento:

o Forneça o android:id com um ID único.

o Forneça o android:tag com uma string única.

o Se você não fornecer nenhum dos dois anteriores, o sistema

utiliza a identificação de exibição de recipiente.

Ou então, programaticamente adicionar o fragmento de um já existente

ViewGroup .

A qualquer momento, enquanto sua atividade está sendo executada, você pode

adicionar fragmentos ao seu layout. Você só precisa especificar um ViewGroup para

colocar o fragmento.

Page 34: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 34

Para fazer transações em sua atividade (como adicionar, remover ou substituir um

fragmento), você deve usar as APIs do FragmentTransaction. Você pode obter uma

instância de FragmentTransaction de sua Activity como esta:

FragmentManager fragmentManager = getFragmentManager()

FragmentTransaction fragmentTransaction =

fragmentManager.beginTransaction();

Você pode então adicionar um fragmento ao usar o método add(), especificando o

fragmento a adicionar e a visão para inseri-lo. Por exemplo:

ExampleFragment fragment = new ExampleFragment();

fragmentTransaction.add(R.id.fragment_container, fragment);

fragmentTransaction.commit();

O primeiro argumento passado para add() é o ViewGroup em que o fragmento deve

ser colocado, especificado por identificação do recurso, e o segundo parâmetro é o

fragmento a acrescentar.

Depois que você fizer as alterações com FragmentTransaction , você deve chamar

commit() para que as alterações tenham efeito.

Adicionando um fragmento sem uma interface de usuário (UI)

Os exemplos acima mostram como adicionar um fragmento de sua atividade, a fim de

fornecer uma interface do usuário. No entanto, você também pode usar um fragmento

para fornecer um comportamento de fundo para a atividade sem a apresentação da

interface do usuário.

Para adicionar um fragmento sem uma interface de usuário, adicione o fragmento da

atividade usando add(Fragment, String) (fornecimento de uma única seqüência de "tag"

para o fragmento, ao invés de um ID). Isso adiciona o fragmento, mas, porque não está

associada a um ponto de vista do layout atividade, ele não recebe uma chamada para

onCreateView(). Assim você não precisa implementar esse método.

Fornecendo uma tag string para o fragmento não é estritamente para os fragmentos não-

UI. Você também pode fornecer etiquetas de seqüência de fragmentos que possuem

uma interface de usuário, mas se o fragmento não possui uma interface de usuário, a tag

string é o único caminho para identificá-lo. Se você deseja obter o fragmento da

atividade posterior, você precisa usar findFragmentByTag().

Page 35: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 35

Gerenciando fragmentos

Para gerenciar os fragmentos em sua atividade, você precisará usar FragmentManager.

Para obtê-lo, chame getFragmentManager() em sua atividade.

Algumas coisas que você pode fazer com FragmentManager incluem:

Obter fragmentos que existem na atividade, com findFragmentById() (para os

fragmentos que fornecem uma interface de usuário no layout de atividade) ou

findFragmentByTag() (para os fragmentos que fazem ou não uma interface do

usuário).

Retirar fragmentos da pilha, com popBackStack() (simulando um comando

BACK pelo usuário).

Registre-se um ouvinte de alteração de parte de trás da pilha, com

addOnBackStackChangedListener().

Conforme demonstrado na seção anterior, você também pode usar FragmentManager

para abrir uma FragmentTransaction, que lhe permite realizar transações, tais como

adicionar e remover fragmentos.

Executando transações com fragmento

Uma das grandes novidades sobre o uso de fragmentos em sua atividade é a capacidade

de adicionar, remover, substituir e realizar outras ações com eles, em resposta à

interação do usuário. Cada conjunto de alterações que comprometem a atividade é

chamado de transação e você pode executar um usando APIs em FragmentTransaction.

Você também pode salvar cada transação na pilha gerenciada pela atividade, permitindo

ao usuário navegar para trás através das mudanças no fragmento (semelhante ao navegar

para trás por meio de atividades).

Você pode adquirir uma instância de FragmentTransaction do FragmentManager como

este:

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

Cada transação é um conjunto de mudanças que se deseja realizar, ao mesmo tempo.

Você pode configurar todas as alterações que pretendem efetuar uma operação

Page 36: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 36

determinada utilizando métodos como add(), remove(), e replace(). Em seguida, para

aplicar a operação para a atividade, você deve chamar commit().

Antes de chamar commit(), no entanto, você pode querer chamar addToBackStack(), a

fim de acrescentar a operação a uma volta da pilha de transações. Esta volta na pilha é

gerida pela atividade e permite que ao usuário retornar ao estado de fragmento anterior,

pressionando a tecla BACK.

Por exemplo, aqui está como você pode substituir um fragmento a outro e preservar o

estado anterior da pilha de volta:

// Create new fragment and transaction

Fragment newFragment = new ExampleFragment();

FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,

// and add the transaction to the back stack

transaction.replace(R.id.fragment_container, newFragment);

transaction.addToBackStack(null);

// Commit the transaction

transaction.commit();

Neste exemplo, newFragment substitui qualquer fragmento (se houver) atualmente no

contêiner de layout identificado pelo R.id.fragment_container ID. Ao chamar

addToBackStack(), é salvado na pilha de volta a operação para que o usuário possa

anular a operação e trazer de volta o fragmento anterior pressionando a tecla BACK.

Se você adicionar várias alterações à operação (como um outro add() ou remove()) e

chamar addToBackStack(), então todas as mudanças aplicadas antes de chamar

commit() são adicionados à volta da pilha como uma única operação e a tecla BACK irá

inverter-los todos juntos.

A ordem na qual você adiciona as alterações em um FragmentTransaction não importa,

exceto:

Você deve chamar commit() por último.

Se você está adicionando vários fragmentos para o mesmo recipiente, então a

ordem em que você adicioná-los determina a ordem em que aparecem na

hierarquia.

Page 37: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 37

Se você não chamar addToBackStack() quando você executar uma operação que

remove um fragmento, em seguida, esse fragmento é destruído quando a transação for

confirmada e o usuário não pode navegar de volta para ela. Considerando que, se você

chamar addToBackStack() quando da remoção de um fragmento, o fragmento é

interrompido e será retomado se o usuário navega de volta.

Dica: Para cada transação, você pode aplicar uma animação de transição, chamando

setTransition() antes do commit().

Chamar commit() não executa a operação imediatamente. Em vez disso, ele agenda a

execução no segmento da atividade de interface do usuário (a thread "main"), logo que

o segmento for capaz de fazê-lo. Se necessário, no entanto, você pode chamar

executePendingTransactions() no seu segmento de interface do usuário para executar

imediatamente as operações apresentadas por commit(). Fazer isso geralmente não é

necessário a menos que a transação é uma dependência para o emprego em outros

segmentos.

Cuidado: você pode cometer uma transação usando commit() apenas antes da

atividade salvar seu estado (quando o usuário deixa a atividade). Se a tentativa for

cometer depois desse ponto, uma exceção será lançada. Isso ocorre porque o estado,

após a confirmação, pode ser perdido se a atividade precisa ser restaurada. Para as

situações em que não tem problema você perder o commit, use

commitAllowingStateLoss().

Comunicando-se com a atividade

Ainda que um Fragment seja implementado como um objeto que é independente de uma

Activity e pode ser usado dentro de múltiplas atividades, uma determinada instância de

um fragmento está diretamente ligada à atividade que o contém.

Especificamente, o fragmento pode acessar a instância Activity com getActivity() e

facilmente realizar tarefas como encontrar um ponto de vista do esquema de atuação:

View listView = getActivity().findViewById(R.id.list);

Da mesma forma, sua atividade pode chamar métodos no fragmento através da

aquisição de uma referência para o Fragment de FragmentManager, usando

findFragmentById() ou findFragmentByTag(). Por exemplo:

Page 38: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 38

ExampleFragment fragment = (ExampleFragment)

getFragmentManager().findFragmentById(R.id.example_fragment);

Criar callbacks evento para a atividade

Em alguns casos, você pode precisar de um fragmento de compartilhar eventos com a

atividade. Uma boa maneira de fazer isso é definir uma interface de retorno no interior

do fragmento e exigem que a atividade de acolhimento implemente-a. Quando a

atividade recebe uma chamada através da interface, ela pode compartilhar a informação

com outros fragmentos no layout conforme necessário.

Por exemplo, se um aplicativo de notícias tem dois fragmentos de uma atividade e um

mostra uma lista de artigos (fragmento A) e outro mostra um artigo (fragmento B),

então um fragmento deve informar a atividade quando um item da lista é escolhido de

modo que pode dizer ao fragmento B para exibir o artigo. Neste caso, a interface

OnArticleSelectedListener é declarada dentro de um fragmento:

public static class FragmentA extends ListFragment {

...

// Container Activity must implement this interface

public interface OnArticleSelectedListener {

public void onArticleSelected(Uri articleUri);

}

...

}

Em seguida, a atividade que hospeda o fragmento implementa a

OnArticleSelectedListener e substitui onArticleSelected() para notificar o fragmento B

do evento a partir do fragmento A. Para garantir que a atividade de acolhimento

implemente essa interface, um fragmento do método onAttach() de retorno (que chama

o sistema quando adicionando o fragmento para a atividade) instancia uma instância de

OnArticleSelectedListener pelo casting da Activity que é passado para onAttach():

public static class FragmentA extends ListFragment {

OnArticleSelectedListener mListener;

...

@Override

public void onAttach(Activity activity) {

super.onAttach(activity);

try {

mListener = (OnArticleSelectedListener) activity;

} catch (ClassCastException e) {

throw new ClassCastException(activity.toString() + " must implement

OnArticleSelectedListener");

}

Page 39: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 39

}

...

}

Se a atividade não tenha aplicado a interface, então o fragmento lança um

ClassCastException. Em caso de sucesso, o membro mListener mantém uma referência

para a implementação da atividade de OnArticleSelectedListener, de modo que um

fragmento pode compartilhar eventos com a atividade, chamando os métodos definidos

pela interface OnArticleSelectedListener. Por exemplo, se um fragmento é uma

extensão do ListFragment, cada vez que o usuário clica em um item da lista, o sistema

chama onListItemClick() no fragmento, o que chama onArticleSelected() para

compartilhar o evento com a atividade:

public static class FragmentA extends ListFragment {

OnArticleSelectedListener mListener;

...

@Override

public void onListItemClick(ListView l, View v, int position, long id) {

// Append the clicked item's row ID with the content provider Uri

Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);

// Send the event and Uri to the host activity

mListener.onArticleSelected(noteUri);

}

...

}

O parâmetro id passado para onListItemClick() é o ID da linha do item clicado, que a

atividade (ou outro fragmento) utiliza para buscar o artigo a partir do aplicativo

ContentProvider.

Adicionando itens à barra de ação

Seus fragmentos podem contribuir itens de menu para a atividade do menu de opções (e,

conseqüentemente, a Barra de ação) pela execução onCreateOptionsMenu(). Para que

esse método receba chamadas, no entanto, você deve chamar setHasOptionsMenu()

durante onCreate(), para indicar que o fragmento gostaria de adicionar itens ao menu de

opções (caso contrário, o fragmento não irá receber uma chamada para

onCreateOptionsMenu()).

Os itens que você adicionar ao menu de opções do fragmento são acrescentados aos

itens de menu existente. O fragmento também recebe callbacks para

onOptionsItemSelected() quando um item de menu é selecionado.

Page 40: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 40

Você também pode registrar uma exibição em seu layout para fornecer um menu de

contexto, chamando registerForContextMenu(). Quando o usuário abre o menu de

contexto, o fragmento recebe uma chamada para onCreateContextMenu(). Quando o

usuário seleciona um item, o fragmento recebe uma chamada para

onContextItemSelected().

Nota: Embora o fragmento receba um on-item-selected na chamada de retorno para

cada item de menu que acrescenta, a atividade é a primeira a receber o respectivo

retorno quando o usuário seleciona um item de menu. Se a execução da atividade da

chamada de retorno no item selecionado não lidar com o item selecionado, o evento é

transmitido para retorno do fragmento. Isso é verdadeiro para o menu de opções e

menus de contexto.

Manuseio do ciclo de vida do fragmento

Figura 3. O ciclo de vida afeta a atividade do ciclo de vida do fragmento.

Page 41: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 41

Gerenciar o ciclo de vida de um fragmento é um pouco como gerir o ciclo de vida de

uma atividade. Como uma atividade, um fragmento pode existir em três estados:

Retomado: O fragmento é visível na atividade de execução.

Em pausa: Outra atividade está em primeiro plano e tem o foco, mas a atividade em que

vive esse fragmento é ainda visível (a atividade do primeiro plano é parcialmente

transparente ou não cobre a tela inteira).

Parado: O fragmento não é visível. A atividade host foi parada ou o fragmento foi

retirado da atividade, mas adicionado à volta da pilha. Um fragmento que parou ainda

está vivo (todas as informações do estado e membro são mantidas pelo sistema). No

entanto, já não é visível para o usuário e serão mortos se a atividade é morta.

Também como uma atividade, você pode manter o estado de um fragmento com um

Bundle, no caso de a atividade do processo ser morta e você precisar restaurar o estado

do fragmento, quando a atividade é recriada. Você pode salvar o estado durante o

fragmento onSaveInstanceState() de callback e restaurá-lo durante onCreate(),

onCreateView() ou onActivityCreated().

A diferença mais significativa no ciclo de vida entre uma atividade e um fragmento é

como são armazenados em suas respectivas pilhas. Uma atividade é colocada em uma

pilha de volta das atividades que é gerido pelo sistema quando ela está parada, por

padrão (para que o usuário possa navegar de volta a ele com a chave de volta, como

discutido em Tarefas e pilha de volta). No entanto, um fragmento é colocado em uma

pilha de volta gerido pela atividade de acolhimento somente quando você solicitar

explicitamente que a instância deve ser salva chamando addToBackStack() durante uma

operação que remove o fragmento.

Gerenciar o ciclo de vida do fragmento é muito semelhante à gestão do ciclo de vida da

atividade. Assim, as mesmas práticas de gestão do ciclo de vida de atividade também se

aplicam aos fragmentos. O que você também precisa entender, porém, é como a vida da

atividade afeta a vida do fragmento.

Coordenação com o ciclo de vida de atividade

O ciclo de vida da atividade em que o fragmento de vida afeta diretamente o ciclo de

vida do fragmento, da mesma forma que cada ciclo de retorno para a atividade resulta

Page 42: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 42

em um retorno semelhante para cada fragmento. Por exemplo, quando a atividade

recebe onPause(), cada fragmento na atividade recebe onPause().

Fragmentos têm alguns retornos do ciclo de vida extra, no entanto, para lidar com uma

interação única com a atividade, a fim de realizar ações como construir e destruir UI do

fragmento. Estes métodos de callback adicionais são:

onAttach(): Chamado quando o fragmento foi associado com a atividade.

onCreateView(): Chamado para criar a hierarquia de visão associada com o fragmento.

onActivityCreated(): Chamado quando onCreate() da atividade foi retornado.

onDestroyView(): Chamado quando a hierarquia de visão associada com o fragmento

está sendo removida.

onDetach(): Chamado quando o fragmento está sendo dissociado da atividade.

O fluxo do ciclo de vida de um fragmento, como ele é afetado por sua atividade de

acolhimento, é ilustrado pela figura 3. Nesta figura, você pode ver o que cada estado

sucessivo da atividade que determina os métodos de retorno de um fragmento pode

receber. Por exemplo, quando a atividade tenha recebido uma onCreate() de callback,

um fragmento da atividade não recebe mais do que o onActivityCreated() de callback.

Uma vez que a atividade atinge o estado retomado, você pode adicionar livremente e

remover fragmentos na a atividade. Assim, somente quando a atividade está em estado

de retomada, o ciclo de vida de um fragmento pode mudar de forma independente.

No entanto, quando a atividade deixa o estado de retomada, o fragmento é novamente

inserido através do ciclo de vida da atividade.

Page 43: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 43

Loaders

Loaders tornam fácil carregar os dados de forma assíncrona, em uma atividade ou um

fragmento. Loaders têm estas características:

Eles estão disponíveis para cada Activity e Fragment.

Eles fornecem carga assíncrona de dados.

Eles monitoram a fonte de seus dados e entregam novos resultados quando muda

o conteúdo.

Eles reconectam automaticamente o cursor do gestor passado, quando está sendo

recriado após uma mudança de configuração. Assim, eles não precisam de voltar

a consultar os seus dados.

Resumo API Loader

Existem várias classes e interfaces que podem ser envolvidos na utilização de

carregadores em um aplicativo. Os resultados estão resumidos nesta tabela:

Classe/Interface Descrição

LoaderManager Uma classe abstrata associada a uma Activity ou

Fragment para gerenciar uma ou mais instâncias

Loader. Isso ajuda a gerenciar um pedido de

execução de operações já em conjunto com o ciclo

de vida de Activity ou Fragment, o uso mais

comum deste é com um CursorLoader, no entanto

as aplicações são livres para escrever seus próprios

loaders para carregar outros tipos de dados.

Há apenas um LoaderManager por atividade ou

fragmento. Mas um LoaderManager pode ter

vários carregadores.

LoaderManager.LoaderCallbacks Uma interface de retorno de um cliente para

interagir com o LoaderManager. Por exemplo,

você usar o onCreateLoader() para criar um

carregador novo.

Page 44: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 44

Loader Uma classe abstrata que executa o carregamento

assíncrono de dados. Esta é a classe base para um

gestor. Você usaria normalmente CursorLoader,

mas você pode implementar sua própria subclasse.

Enquanto os carregadores estão ativos, eles devem

acompanhar a fonte de seus dados e apresentar

resultados novos quando alterar o conteúdo.

AsyncTaskLoader Carregador abstrato que provê AsyncTansk para o

trabalho.

CursorLoader Uma subclasse de AsyncTaskLoader que consulta

o ContentResolver e retorna um Cursor. Essa

classe implementa o protocolo Loader de uma

forma padrão para consultar cursores, com base

em AsyncTaskLoader para realizar a consulta de

cursor em uma discussão de fundo para que ele

não bloqueie os aplicativo de interface do usuário.

Utilizar este carregador é a melhor maneira de

carregar os dados de forma assíncrona a partir de

um ContentProvider, ao invés de realizar uma

consulta gerida através do fragmento ou de APIs.

As classes e interfaces na tabela acima são os componentes essenciais que você vai usar

para implementar um carregador em sua aplicação. Você não vai precisar de todos eles

para cada gestor, mas você sempre precisa de uma referência ao LoaderManager para

inicializar um carregador e uma implementação de um Loader, como CursorLoader. As

seções a seguir mostram como usar essas classes e interfaces em uma aplicação.

Usando carregadores em um aplicativo

Um aplicativo que usa carregadores normalmente inclui o seguinte:

Uma Activity ou Fragment.

Uma instância da LoaderManager.

Page 45: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 45

Um CursorLoader para carregar dados apoiado por uma ContentProvider.

Alternativamente, você pode implementar sua própria subclasse de Loader ou

AsyncTaskLoader para carregar dados de alguma outra fonte.

Uma implementação para LoaderManager.LoaderCallbacks. Isto é onde você

cria novos loaders e gerencia suas referências aos carregadores existentes.

Uma maneira de mostrar o carregador de dados, como um

SimpleCursorAdapter.

Uma fonte de dados, como um ContentProvider, ao usar um CursorLoader .

Iniciando um Loader

O LoaderManager gerencia um ou mais instâncias Loader dentro de uma Activity ou

Fragment. Há apenas um LoaderManager por atividade ou fragmento.

Normalmente, você inicializa um Loader com o método onCreate() dentro da atividade

ou método onActivityCreated() dentro do fragmento. Você pode fazer isso da seguinte

forma:

// Prepare the loader. Either re-connect with an existing one,

// or start a new one.

getLoaderManager().initLoader(0, null, this);

O método initLoader() utiliza os seguintes parâmetros:

Um ID exclusivo que identifica o carregador. Neste exemplo, a identificação é 0.

Os argumentos opcionais para fornecer ao loader a construção (null neste

exemplo).

A execução de LoaderManager.LoaderCallbacks, em que a LoaderManager é

chamada para relatar eventos carregador. Neste exemplo, a classe local

implementa a interface LoaderManager.LoaderCallbacks, assim que passa uma

referência para si, this.

Page 46: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 46

A chamada ao initLoader() assegura que um carregador é inicializado e ativo. Ele tem

dois resultados possíveis:

Se o carregador especificado pelo ID já existe, o último carregador criado é

reutilizado.

Se o carregador especificado pela ID não existir, initLoader() aciona o método

LoaderManager.LoaderCallbacks em onCreateLoader(). Isto é onde você

implementa o código para instanciar e retornar um carregador novo.

Em ambos os casos, a aplicação determinada LoaderManager.LoaderCallbacks está

associada com o carregador, e será chamada quando o estado muda carregador. Se no

momento da chamada, o chamador está em seu estado inicial e o carregador solicitado

já existe e tem gerado os seus dados, o sistema solicita onLoadFinished() (durante

initLoader()), então você deve estar preparado para isso acontecer.

Observe que o método initLoader() retorna o Loader que é criado, mas você não precisa

capturar uma referência a ele. O LoaderManager gerencia a vida do carregador

automaticamente. O LoaderManager inicia e pára de carregar quando necessário, e

mantém o estado do carregador e do seu conteúdo associado. Isso implica que você

raramente interage com carregadores diretamente. É mais comumente usar o

LoaderManager.LoaderCallbacks para intervir no processo de carregamento quando

ocorrem eventos específicos.

Reiniciando o Loader

Quando você usa initLoader(), como mostrado acima, ele usa um carregador existente

com a identificação especificada, se houver. Se não houver, ele cria um. Mas às vezes

você deseja descartar os dados antigos e começar de novo.

Para descartar os dados antigos, use restartLoader(). Por exemplo, essa implementação

de SearchView.OnQueryTextListener reinicia o carregador quando o usuário muda de

consulta. O loader precisa ser reiniciado para que ele possa usar a pesquisa de revisão de

filtro para fazer uma nova consulta:

Page 47: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 47

public boolean onQueryTextChanged(String newText) {

// Called when the action bar search text has changed. Update

// the search filter, and restart the loader to do a new query

// with this filter.

mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;

getLoaderManager().restartLoader(0, null, this);

return true;

}

Usando callbacks do LoaderManager

LoaderManager.LoaderCallbacks é uma interface de callback que permite que um

cliente interaja com o LoaderManager .

Loaders, em especial o CursorLoader, são esperados para reter seus dados depois de ser

interrompido. Isso permite aos aplicativos que mantenham seus dados através dos

métodos onStop() e onStart() da atividade ou fragmento, de modo que quando os

usuários retornam a um pedido, eles não tem que aguardar os dados para recarregarem.

Você usa o método LoaderManager.LoaderCallbacks quando quer saber quando criar

um carregador novo, e para dizer a aplicação quando é hora de parar de usar um

gerenciador de dados.

LoaderManager.LoaderCallbacks inclui os seguintes métodos:

onCreateLoader() - instancia e retorna um novo Loader para o ID dado.

onLoadFinished() - Chamado quando um loader criado anteriormente terminou

sua carga.

onLoaderReset() - Chamado quando um loader criado anteriormente está sendo

redefinido, tornando os dados disponíveis.

Esses métodos são descritos em detalhes nas seções seguintes.

onCreateLoader

Quando você tenta acessar um loader (por exemplo, através initLoader()), ele verifica se

o carregador especificado pelo ID existe. Se isso não ocorrer, ele aciona o método

onCreateLoader() do LoaderManager.LoaderCallbacks. Isto é onde você irá criar um

carregador novo. Normalmente, esse será um CursorLoader, mas você pode

implementar sua própria subclasse Loader.

Page 48: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 48

Neste exemplo, o onCreateLoader() cria um método de retorno CursorLoader. Você

deve construir o CursorLoader usando o método construtor, que exige um conjunto

completo de informações necessárias para realizar uma consulta para o

ContentProvider. Especificamente, é necessário:

URI - A URI para o conteúdo para recuperar.

projeção - uma lista de quais colunas retornar. Passando null irá retornar todas

as colunas, que é ineficiente.

seleção - Um filtro que declara que as linhas de retorno, formatado como uma

cláusula WHERE SQL (excluindo o próprio WHERE). Passando null retornará

todas as linhas para o URI especificado.

selectionArgs - Você pode incluir ?s na seleção, que serão substituídas pelos

valores da selectionArgs, na ordem em que aparecem na seleção. Os valores

serão vinculados como Strings.

SortOrder - Como adquirir as linhas, formatado como uma cláusula ORDER BY

de SQL (excluindo-se o ORDER BY). Passando null usará a ordem de

classificação padrão, que pode ser desordenada.

// If non-null, this is the current filter the user has provided.

String mCurFilter;

...

public Loader<Cursor> onCreateLoader(int id, Bundle args) {

// This is called when a new Loader needs to be created. This

// sample only has one Loader, so we don't care about the ID.

// First, pick the base URI to use depending on whether we are

// currently filtering.

Uri baseUri;

if (mCurFilter != null) {

baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,

Uri.encode(mCurFilter));

} else {

baseUri = Contacts.CONTENT_URI;

}

// Now create and return a CursorLoader that will take care of

// creating a Cursor for the data being displayed.

String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("

+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("

+ Contacts.DISPLAY_NAME + " != '' ))";

return new CursorLoader(getActivity(), baseUri,

CONTACTS_SUMMARY_PROJECTION, select, null,

Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }

Page 49: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 49

onLoadFinished

Este método é chamado quando um loader criado anteriormente terminou sua carga.

Este método é garantido para ser chamado antes do lançamento do último dado que foi

fornecido para este carregador. Neste ponto, você deve remover todo uso dos dados

antigos (desde que será lançado em breve), mas não deve fazer a seu próprio

lançamento dos dados desde o seu carregador é o dono e vai cuidar disso.

O carregador vai lançar os dados, uma vez que conhece que o aplicativo não está mais

usando. Por exemplo, se os dados são um cursor de um CursorLoader, você não deve

chamar close() sobre ele mesmo. Se o cursor está sendo colocado em um

CursorAdapter, você deve usar o método swapCursor() para que o antigo Cursor não

seja fechado. Por exemplo:

// This is the Adapter being used to display the list's data.

SimpleCursorAdapter mAdapter;

...

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

// Swap the new cursor in. (The framework will take care of closing the

// old cursor once we return.)

mAdapter.swapCursor(data);

}

onLoaderReset

Este método é chamado quando um loader criado anteriormente está sendo redefinido,

tornando os seus dados indisponíveis. Este retorno permite saber quando o dado está

prestes a ser liberado assim você pode remover a referência a ele.

Esta aplicação chama swapCursor() com um valor null:

// This is the Adapter being used to display the list's data.

SimpleCursorAdapter mAdapter;

...

public void onLoaderReset(Loader<Cursor> loader) {

// This is called when the last Cursor provided to onLoadFinished()

// above is about to be closed. We need to make sure we are no

// longer using it.

mAdapter.swapCursor(null);

}

Page 50: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 50

Exemplo

Como exemplo, aqui é a implementação completa de um Fragment que apresenta um

ListView com os resultados de uma consulta contra o provedor de conteúdo contatos.

Ele usa um CursorLoader para gerenciar a consulta do fornecedor.

Para uma aplicação para acessar os contatos de um usuário, como mostrado neste

exemplo, o manifesto deve incluir a permissão READ_CONTACTS .

public static class CursorLoaderListFragment extends ListFragment

implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

// This is the Adapter being used to display the list's data.

SimpleCursorAdapter mAdapter;

// If non-null, this is the current filter the user has provided.

String mCurFilter;

@Override public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

// Give some text to display if there is no data. In a real

// application this would come from a resource.

setEmptyText("No phone numbers");

// We have a menu item to show in action bar.

setHasOptionsMenu(true);

// Create an empty adapter we will use to display the loaded data.

mAdapter = new SimpleCursorAdapter(getActivity(),

android.R.layout.simple_list_item_2, null,

new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },

new int[] { android.R.id.text1, android.R.id.text2 }, 0);

setListAdapter(mAdapter);

// Prepare the loader. Either re-connect with an existing one,

// or start a new one.

getLoaderManager().initLoader(0, null, this);

}

@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

// Place an action bar item for searching.

MenuItem item = menu.add("Search");

item.setIcon(android.R.drawable.ic_menu_search);

item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);

SearchView sv = new SearchView(getActivity());

sv.setOnQueryTextListener(this);

item.setActionView(sv);

}

public boolean onQueryTextChange(String newText) {

Page 51: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 51

// Called when the action bar search text has changed. Update

// the search filter, and restart the loader to do a new query

// with this filter.

mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;

getLoaderManager().restartLoader(0, null, this);

return true;

}

@Override public boolean onQueryTextSubmit(String query) {

// Don't care about this.

return true;

}

@Override public void onListItemClick(ListView l, View v, int position, long id) {

// Insert desired behavior here.

Log.i("FragmentComplexList", "Item clicked: " + id);

}

// These are the Contacts rows that we will retrieve.

static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {

Contacts._ID,

Contacts.DISPLAY_NAME,

Contacts.CONTACT_STATUS,

Contacts.CONTACT_PRESENCE,

Contacts.PHOTO_ID,

Contacts.LOOKUP_KEY,

};

public Loader<Cursor> onCreateLoader(int id, Bundle args) {

// This is called when a new Loader needs to be created. This

// sample only has one Loader, so we don't care about the ID.

// First, pick the base URI to use depending on whether we are

// currently filtering.

Uri baseUri;

if (mCurFilter != null) {

baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,

Uri.encode(mCurFilter));

} else {

baseUri = Contacts.CONTENT_URI;

}

// Now create and return a CursorLoader that will take care of

// creating a Cursor for the data being displayed.

String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("

+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("

+ Contacts.DISPLAY_NAME + " != '' ))";

return new CursorLoader(getActivity(), baseUri,

CONTACTS_SUMMARY_PROJECTION, select, null,

Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");

}

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

// Swap the new cursor in. (The framework will take care of closing the

// old cursor once we return.)

mAdapter.swapCursor(data);

}

Page 52: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 52

public void onLoaderReset(Loader<Cursor> loader) {

// This is called when the last Cursor provided to onLoadFinished()

// above is about to be closed. We need to make sure we are no

// longer using it.

mAdapter.swapCursor(null);

}

}

Page 53: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 53

Tarefas e pilha de execução

Um aplicativo normalmente contém várias atividades. Cada atividade deve ser

concebida em torno de um tipo específico de ação que o usuário pode realizar e pode

iniciar outras atividades. Por exemplo, um aplicativo de e-mail pode ter uma atividade

para mostrar uma lista de e-mail. Quando o usuário seleciona um e-mail, uma nova

atividade é aberta para ver o e-mail.

Uma atividade pode até iniciar atividades que existem em outras aplicações no

dispositivo. Por exemplo, se sua aplicação quer enviar um e-mail, você pode definir a

intenção de realizar um "send" e incluir alguns dados, tais como um endereço de e-mail

e uma mensagem. Uma atividade de outra aplicação que se declara para lidar com este

tipo de intenção, em seguida, é aberta. Neste caso, a intenção é enviar um e-mail, assim

a atividade de "compor" e-mail começa (se múltiplas atividades apóiam a mesma

intenção, então o sistema permite ao usuário selecionar qual usar). Quando o email é

enviado, sua atividade é retomada e parece como se a atividade de e-mail é parte do seu

aplicativo. Mesmo que as atividades podem ser de diferentes aplicações, o Android

mantém essa experiência do usuário uniforme, mantendo as duas atividades na mesma

tarefa.

Uma tarefa é um conjunto de atividades que os usuários interagem ao realizar um

determinado trabalho. As atividades são organizadas em uma pilha (a "pilha de volta"),

na ordem em que cada atividade é aberta.

A tela inicial é o ponto de partida para a maioria das tarefas. Quando o usuário toca num

ícone na tela do aplicativo (ou um atalho na tela inicial), essa tarefa vem para o primeiro

plano. Se existe uma tarefa para a aplicação (o pedido não tenha sido usado

recentemente), então uma nova tarefa é criada e a atividade "principal" abre como a

atividade da raiz na pilha.

Quando a atividade atual começa outra, a nova atividade é empurrada na parte superior

da pilha e ganha foco. A atividade anterior permanece na pilha, mas está parada.

Quando uma atividade termina, o sistema mantém o estado atual de sua interface de

usuário. Quando o usuário pressiona a tecla BACK, a atividade atual é retirada da parte

superior da pilha (a atividade é destruída) e a atividade anterior recomeça (o estado

anterior de sua interface é restaurado). Atividades na pilha nunca são reorganizadas, só

Page 54: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 54

incluídas e excluídas da pilha - inseridas na pilha quando iniciado pela atividade atual e

retiradas quando o usuário deixa-as usando a tecla BACK. Como tal, a parte de trás da

pilha funciona como uma estrutura de objetos "last in, first out". A Figura 1 mostra esse

comportamento com uma linha do tempo mostrando o progresso entre as atividades

junto com a atual pilha de volta em cada momento.

Figura 1. Uma representação de como cada nova atividade em uma tarefa adiciona um item na parte de

trás da pilha. Quando o usuário pressiona a tecla BACK, a atividade atual é destruída e volta à atividade

anterior.

Se o usuário continuar a pressionar BACK, então cada atividade da pilha é retirada para

revelar a anterior, até que o usuário retorna à tela inicial (ou de qualquer atividade que

estava sendo executada quando a tarefa começou). Quando todas as atividades são

removidas da pilha, a tarefa não existe mais.

Figura 2. Duas tarefas tarefa estão no fundo, esperando para ser retomado, enquanto a Tarefa B recebe

interação do usuário em primeiro plano.

Page 55: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 55

Figura 3. A única atividade é instanciada várias vezes.

Uma tarefa é uma unidade coesa, que pode passar para o "background" quando os

usuários começam uma nova tarefa ou vão para a tela inicial, através da tecla HOME.

Enquanto no fundo, todas as atividades na tarefa estão paradas, mas a pilha de volta

para a tarefa permanece intacta, a tarefa simplesmente perdeu o foco enquanto outra

tarefa se realiza como mostrado na figura 2. Uma tarefa pode, em seguida, voltar ao

"primeiro plano" para que os usuários possam continuar de onde pararam. Suponha, por

exemplo, que a tarefa atual (Tarefa A) tenha três atividades em sua pilha e dois no

âmbito da atividade corrente. O usuário pressiona a tecla HOME, e em seguida, inicia

uma nova aplicação a partir do lançador de aplicação. Quando a tela inicial aparece,

uma tarefa vai para o fundo. Quando inicia o novo aplicativo, o sistema inicia uma

tarefa para a aplicação (Tarefa B) com sua própria pilha de atividades. Após a interação

com esse aplicativo, o usuário volta para HOME novamente e seleciona o aplicativo que

originalmente começou Tarefa A. Agora, a tarefa A vem para o primeiro plano - todas

as três atividades em sua pilha estão intactas e as atividades no topo da pilha são

retomadas. Neste ponto, o usuário também pode voltar à Tarefa B indo para a HOME e

selecionando o ícone do aplicativo que iniciou essa tarefa (ou tocando e segurando a

tecla HOME para revelar as tarefas recentes e selecionando uma). Este é um exemplo

de multitarefa no Android.

Nota: Várias tarefas podem ser realizadas no fundo de uma vez. No entanto, se o

usuário estiver executando muitas tarefas em segundo plano ao mesmo tempo, o

sistema pode começar a destruir as atividades do fundo, a fim de recuperar a memória,

fazendo com que os estados de atividade possam ser perdidos.

Como as atividades na parte de trás da pilha nunca são reorganizadas, se seu aplicativo

permite que usuários iniciem uma atividade específica de mais de uma atividade, uma

Page 56: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 56

nova instância daquela atividade é criada e colocada na pilha (ao invés de trazer

qualquer instância anterior da atividade para o topo). Como tal, uma atividade em seu

aplicativo pode ser instanciada várias vezes (mesmo de diferentes tarefas), como

mostrado na figura 3. Como tal, se o usuário navega para trás usando a tecla BACK,

cada instância da atividade é revelada na ordem em que foi aberta (cada um com seu

estado próprio de UI). No entanto, você pode modificar esse comportamento se você

não quer uma atividade a ser instanciada mais de uma vez.

Para resumir o comportamento padrão de atividades e tarefas:

Quando a atividade A começa atividade B, uma atividade é interrompida, mas o

sistema mantém o seu estado (como a posição de rolagem e texto inseridos em

formulários). Se o usuário pressiona a tecla de volta, enquanto na Atividade B, a

atividade A continua com o seu estado restaurado.

Quando o usuário deixa uma tarefa, pressionando a tecla HOME, a atividade em

curso é interrompida e sua tarefa vai para o fundo. O sistema mantém o estado

de cada atividade na tarefa. Se o usuário depois recomeça a tarefa de selecionar

o ícone do lançador, que começou a tarefa, ela vem para o primeiro plano e

retoma a atividade no topo da pilha.

Se o usuário pressionar a tecla BACK, a atividade atual é removida da pilha e

destruída. A atividade anterior na pilha é retomada. Quando uma atividade é

destruída, o sistema não mantém atividade do Estado.

As atividades podem ser instanciadas várias vezes, até mesmo de outras tarefas.

Salvando estado de atividade

Como discutido acima, o comportamento padrão do sistema preserva o estado de uma

atividade quando está parada. Desta forma, quando os usuários navegam de volta para

uma atividade anterior, sua interface parece do jeito que deixou. No entanto, você pode,

e deve, de forma proativa manter o estado de suas atividades através de métodos de

retorno, caso a atividade seja destruída e deve ser recriada.

Quando o sistema pára uma de suas atividades (como quando inicia uma nova atividade

ou movimenta as tarefas para o fundo), o sistema poderia destruir completamente essa

atividade se ele precisa recuperar a memória do sistema. Quando isso acontece, as

Page 57: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 57

informações sobre o estado da atividade são perdidas. Se isso acontecer, o sistema ainda

sabe que a atividade tem um lugar na parte de trás da pilha, mas quando a atividade é

trazida para o topo da pilha, o sistema deve recriá-la (em vez de retomá-la). A fim de

evitar a perda do trabalho do usuário, você deve mantê-la de forma proativa através da

aplicação do método onSaveInstanceState() de retorno de sua atividade.

Gerenciando tarefas

A forma como o Android gerencia as tarefas e a pilha de volta, como descrito acima -

colocando todas as atividades que começaram sucessivamente na mesma tarefa e em

uma pilha "last in, first out" - funciona muito bem para a maioria das aplicações e você

não deve se preocupar em como suas atividades estão associadas a tarefas ou como eles

existem na parte de trás da pilha. No entanto, você pode decidir que você deseja

interromper o comportamento normal. Talvez você queira uma atividade em seu

aplicativo para iniciar uma nova tarefa quando é iniciada (em vez de ser colocada dentro

da tarefa atual), ou, quando você começa uma atividade, que pretende apresentar uma

instância existente da mesma (em vez de criar uma nova instância no topo da pilha de

volta), ou, você quer a sua pilha para ser limpas de todas as activitiesstart, com exceção

para a atividade de raiz quando o usuário deixa a tarefa.

Você pode fazer essas coisas e mais, com atributos no manifesto da <activity> e com

bandeiras de intenção que você passa para startActivity().

Neste sentido, os principais atributos de <activity> que você pode usar são:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

E as principais bandeiras de intenção você pode usar são:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_SINGLE_TOP

Page 58: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 58

Atenção: A maioria dos aplicativos não deve interromper o comportamento padrão de

atividades e tarefas. Se você determinar que seja necessário para a sua atividade

modificar o comportamento padrão, tenha cuidado e não se esqueça de testar a

usabilidade da atividade durante o lançamento e quando se navega de volta a ele de

outras atividades e tarefas com a tecla BACK. Certifique-se de teste para os

comportamentos de navegação que possam ser incompatíveis com o comportamento

esperado do usuário.

Definição de modos de lançamento

Modos de Lançamento permitem que a definir como uma nova instância de uma

atividade está associada à tarefa atual. Você pode definir diferentes modos de

lançamento de duas maneiras:

Usando o arquivo de manifesto

o Quando você declarar uma atividade em seu arquivo de manifesto, você

pode especificar como a atividade deve se associar com as tarefas

quando é iniciada.

Usando as opções de Intenções

o Quando você chamar startActivity(), você pode incluir uma bandeira na

Intent que declara como (ou se) a nova atividade deve associar com a

tarefa atual.

Como tal, se a atividade A inicia a atividade B, a atividade B pode definir no seu

manifesto de como ele deve se associar com a tarefa atual (caso exista) e uma atividade

também pode pedir o quanto a atividade B deve associar com a tarefa atual. Se ambas as

atividades definem como a atividade B deve associar com uma tarefa, então a

solicitação da atividade A (como definido na intenção) é honrada sobre o pedido da

atividade B (como definido no seu manifesto).

Nota: Alguns dos modos disponíveis no lançamento do manifesto não estão disponíveis

como sinalizadores para uma intenção e, também, alguns modos de lançamento

disponível como sinalizadores para a intenção pode não ser definida no manifesto.

Page 59: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 59

Usando o arquivo de manifesto

Quando declarar uma atividade em seu arquivo de manifesto, você pode especificar

como a atividade deve associar com uma tarefa usando o atributo launchMode do

elemento <activity>.

O atributo launchMode especifica uma instrução sobre como a atividade deve ser

lançada em uma tarefa. Há quatro modos diferentes de lançamento você pode atribuir ao

atributo launchMode:

"standard" (o modo padrão)

Padrão. O sistema cria uma nova instância da atividade na tarefa de que foi

iniciado e mapeia a intenção a ele. A atividade pode ser instanciada várias

vezes, cada instância pode pertencer a diferentes tarefas e uma tarefa pode ter

múltiplas instâncias.

"singleTop"

Se uma instância da atividade já existe no topo da tarefa atual, o sistema mapeia

da estância através de uma chamada para o seu método onNewIntent(), ao invés

de criar uma nova instância da atividade. A atividade pode ser instanciada várias

vezes, cada instância pode pertencer a diferentes tarefas e uma tarefa pode ter

múltiplas instâncias (mas só se a atividade na parte superior da pilha de retorno

não é uma instância existente da atividade).

Por exemplo, suponha que uma tarefa que está na pilha consiste de uma

atividade de raiz A com as atividades B, C e D no topo (a pilha é ABCD, D está

no topo). A intenção chega para uma atividade do tipo D. Se D tem o modo de

lançamento padrão "standard", uma nova instância da classe é lançada e se torna

a pilha ABCDD. No entanto, se D está no modo de lançamento "singleTop", a

instância existente do D é entregue à intenção por meio onNewIntent(), porque

está no topo da pilha, a pilha continua ABCD. No entanto, se a intenção chega

para uma atividade do tipo B, então, uma nova instância de B é adicionada à

pilha, mesmo se o seu modo de lançamento é "singleTop".

Nota: Quando uma nova instância de uma atividade é criada, o usuário pode

pressionar a tecla Back para retornar à atividade anterior. Mas quando uma

Page 60: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 60

instância existente de uma atividade lida com uma nova intenção, o usuário não

poderá pressionar a tecla Voltar para retornar ao estado da atividade antes da

nova intenção chegaram a onNewIntent().

"singleTask"

O sistema cria uma nova tarefa e instancia a atividade na raiz da nova tarefa. No

entanto, se uma instância da atividade já existe em uma tarefa separada, o

sistema mapeia a intenção da instância existente através de um convite à sua

onNewIntent(), ao invés de criar uma nova instância. Apenas uma instância da

atividade pode existir ao mesmo tempo.

Nota: Embora a atividade começe em uma nova tarefa, a tecla BACK ainda

retorna o usuário para a atividade anterior.

"singleInstance" .

O mesmo que "singleTask", exceto que o sistema não inicia qualquer outra

atividade para a tarefa, segurando a instância. A atividade é sempre e único

membro dessa tarefa; quaisquer atividades iniciadas por este abrem um em uma

tarefa separada.

Como outro exemplo, o navegador Android declara que a atividade do navegador web

deve sempre aberta em sua própria tarefa, especificando o modo de lançamento

singleTask no elemento <activity>. Isto significa que, se o aplicativo emite a intenção

de abrir o navegador do Android, a sua atividade não é colocada na mesma tarefa que a

sua aplicação. Em vez disso, ou uma nova tarefa para o navegador é iniciada ou, se o

navegador já tem uma tarefa em execução em segundo plano, essa tarefa é antecipada

para lidar com a nova intenção.

Independentemente de uma atividade ser iniciada em uma nova tarefa ou na mesma

tarefa como a atividade que começou, a tecla BACK sempre leva o usuário para a

atividade anterior. Entretanto, se você iniciar uma atividade de sua tarefa (Tarefa A),

que especifica o modo de lançamento sendo singleTask, então a atividade pode ter uma

instancia em background que pertence a uma tarefa com a sua própria pilha de volta

(Tarefa B). Neste caso, quando a tarefa B é antecipada para lidar com uma nova

Page 61: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 61

intenção, a tecla BACK navega de volta através das atividades na tarefa B antes de

retornar à atividade do topo da tarefa A. Figura 4 visualiza este tipo de cenário.

Figura 4. A representação de como uma atividade com o modo de lançar "singleTask" é adicionada à

pilha de volta. Se a atividade já faz parte de uma tarefa em segundo plano com a sua própria pilha de

volta (Tarefa B), então toda a volta da pilha também vem para a frente, em cima da tarefa atual (Tarefa

A).

Nota: O comportamento que você especificar para a sua atividade com a launchMode

pode ser anulado por bandeiras incluídas com a intenção de iniciar a sua atividade,

como discutido na próxima seção.

Usando as opções de intenções

Ao iniciar uma atividade, você pode modificar o padrão de associação de uma atividade,

incluindo na intenção que você entrega a startActivity(). As bandeiras que podem ser

usadas para modificar o comportamento padrão são:

FLAG_ACTIVITY_NEW_TASK

Iniciar a atividade em uma nova tarefa. Se a tarefa já está em execução para a

atividade que você está começando agora, essa tarefa é levada para o primeiro

plano com o seu último estado restaurado e a atividade recebe a nova intenção

em onNewIntent().

Page 62: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 62

Isso produz o mesmo comportamento que a "singleTask", discutido na seção

anterior.

FLAG_ACTIVITY_SINGLE_TOP

Se a atividade que está sendo iniciado é a atividade atual (no topo da pilha de

volta), então a instância existente recebe uma chamada para onNewIntent(), em

vez de criar uma nova instância da atividade.

Isso produz o mesmo comportamento que a "singleTop", discutido na seção

anterior.

FLAG_ACTIVITY_CLEAR_TOP

Se a atividade a ser iniciada já está em execução na tarefa atual, então ao invés

de lançar uma nova instância da atividade, todas as outras atividades em cima

dela são destruídas e essa intenção é entregue à instância retomada da atividade

(agora no topo, através onNewIntent()).

Não há nenhum valor para o launchMode que produz esse comportamento.

FLAG_ACTIVITY_CLEAR_TOP é mais freqüentemente utilizado em conjunto

com FLAG_ACTIVITY_NEW_TASK. Quando usados em conjunto, essas

bandeiras são uma maneira de localizar uma atividade existente em outra tarefa e

colocá-la em uma posição onde ela pode responder à intenção.

Nota: Se o modo de lançamento da atividade designada é "standard", ela

também é removida da pilha e uma nova instância é lançada em seu lugar para

lidar com o intuito de entrada. Isso porque uma nova instância é sempre criada

para uma nova intenção, quando a modalidade de lançamento é "standard”.

Manipulação de afinidades

A afinidade indica a qual tarefa uma atividade prefere pertencer. Por padrão, todas as

atividades da mesma aplicação têm afinidade entre si. Então, por padrão, todas as

atividades no mesmo aplicativo preferem estar na mesma tarefa. No entanto, você pode

modificar o padrão de afinidade para uma atividade. Atividades definidas em diferentes

aplicações podem compartilhar uma afinidade, ou atividades definidas no mesmo

aplicativo podem ter diferentes afinidades de tarefas.

Page 63: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 63

Você pode modificar a afinidade de uma atividade específica com o atributo

taskAffinity do <activity> elemento.

O atributo taskAffinity tem um valor de seqüência, que deve ser exclusivo do nome do

pacote padrão declarada no elemento <manifest>, porque o sistema usa esse nome para

identificar a afinidade de tarefas padrão para o aplicativo.

A afinidade entra em jogo em duas circunstâncias:

Quando a intenção que inicia uma atividade contém

FLAG_ACTIVITY_NEW_TASK.

o Uma nova atividade é, por padrão, lançada na tarefa da atividade que

chamou startActivity(). É empurrada para o topo da pilha do chamador. No

entanto, se a intenção passada para startActivity() contém o

FLAG_ACTIVITY_NEW_TASK, o sistema procura por uma tarefa

diferente para abrigar a nova atividade. Muitas vezes, é uma nova tarefa. No

entanto, ele não tem que ser. Se já existe uma tarefa, com a mesma afinidade

que a nova atividade, a atividade é lançada nessa tarefa. Se não, ele começa

uma nova tarefa.

o Se este sinalizador faz uma atividade para iniciar uma nova tarefa e que o

usuário pressiona a tecla HOME para deixá-lo, deve haver alguma maneira

para o usuário navegar de volta para a tarefa. Algumas entidades (como o

gerente de comunicação) sempre iniciam as atividades em uma tarefa

externa, nunca como parte de si própria, assim eles sempre colocam

FLAG_ACTIVITY_NEW_TASK nas intenções para passar a startActivity().

Se você tiver uma atividade que pode ser invocada por uma entidade externa

que possa usar esta bandeira, tome cuidado para que o usuário tenha uma

maneira independente para voltar para a tarefa que começou como um ícone

do lançador (a atividade radicular da tarefa tem uma intenção de filtro

CATEGORY_LAUNCHER).

Quando uma atividade tem seu atributo allowTaskReparenting definido como "true".

Neste caso, a atividade pode se mover da tarefa que começou para a tarefa que tem

afinidade, quando essa tarefa vem em primeiro plano.

Page 64: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 64

Por exemplo, suponha que uma atividade que relata as condições meteorológicas em

cidades selecionadas é definida como parte de um aplicativo de viagens. Ele tem a

mesma afinidade como outras atividades na mesma aplicação (a afinidade aplicativo

padrão) e permite que re-parentalidade com esse atributo. Quando uma de suas

atividades inicia a atividade de reportar o tempo, inicialmente pertencente à mesma

tarefa que a sua atividade. No entanto, quando a tarefa da aplicação de viagens volta

ao primeiro plano, a atividade de reportar oo tempo é designada para essa tarefa e

exibida dentro dela.

Dica: Se um arquivo .apk contiver mais de uma "aplicação" do usuário, você

provavelmente vai querer usar o atributo taskAffinity para atribuir diferentes

afinidades para as atividades associadas a cada "pedido".

Limpando a pilha de volta

Se o usuário deixar uma tarefa por um longo tempo, o sistema cancela a tarefa de todas

as atividades exceto a atividade de raiz. Quando o usuário retorna para a tarefa

novamente, somente a atividade da raiz é restaurada. O sistema se comporta desta

maneira, porque, depois de um longo período de tempo, os usuários provavelmente

abandonaram o que faziam antes e estão retornando para a tarefa para começar algo

novo.

Há alguns atributos de atividade que você pode usar para modificar esse

comportamento:

alwaysRetainTaskState

Se este atributo é definido como "true" na atividade de raiz de uma tarefa, o

comportamento padrão que acabamos de descrever não acontece. A tarefa retém todas

as atividades na sua pilha, mesmo após um longo período.

clearTaskOnLaunch

Se este atributo é definido como "true" na atividade de raiz de uma tarefa, a pilha é

limpa até a atividade de raiz sempre que o usuário deixa a tarefa e retorna a ela. Em

outras palavras, é o oposto do alwaysRetainTaskState. O usuário sempre retorna para a

tarefa em seu estado inicial, mesmo após estar deixando a tarefa por apenas um

momento.

Page 65: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 65

finishOnTaskLaunch

Esse atributo é como clearTaskOnLaunch, mas opera em uma única atividade, não em

uma tarefa inteira. Ela também pode fazer alguma atividade ir embora, incluindo a

atividade de raiz. Quando é definido como "true", a atividade continua a ser parte da

tarefa apenas para a sessão atual. Se o usuário sai e depois volta para a tarefa, ela não

está mais presente.

Iniciando uma tarefa

Você pode configurar uma atividade como o ponto de entrada para uma tarefa, dando-

lhe um filtro com a intenção "android.intent.action.MAIN" como a ação especificada e

"android.intent.category.LAUNCHER" como a categoria especificada. Por exemplo:

<activity ... >

<intent-filter ... >

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

...

</activity>

A intenção do filtro deste tipo faz um ícone e uma legenda para a atividade a ser exibida

na tela do menu, dando aos usuários uma maneira de iniciar a atividade e para retornar

para a tarefa que ele cria em qualquer momento depois de ter sido lançado.

Esta segunda habilidade é importante: o usuário deve ser capaz de deixar uma tarefa e,

em seguida, voltar a ela mais tarde com o lançador de atividade. Por esta razão, os dois

modos de iniciar as atividades que marcam o início, como sempre, uma tarefa,

"singleTask" e " "singleInstance" , devem ser usados somente quando a atividade tem

um filtro ACTION_MAIN e CATEGORY_LAUNCHER. Imagine, por exemplo, o que

poderia acontecer se o filtro estiver faltando: Uma intenção lança uma atividade

"singleTask", iniciando uma nova tarefa, e o usuário passa algum tempo a trabalhar

nessa tarefa. O usuário pressiona o HOME. A tarefa agora é enviada para o fundo e fica

invisível. Porque ele não é representado na tela do aplicativo, o usuário não tem como

voltar para a tarefa.

Para os casos onde você não deseja que o usuário seja capaz de retornar a uma

atividade, defina o elemento de <activity>, finishOnTaskLaunch, para "true".

Page 66: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 66

Serviços

Um Service é um componente da aplicação que pode executar operações de longa

duração em segundo plano e não oferece uma interface de usuário. Outro componente

do aplicativo pode iniciar um serviço e vai continuar a rodar em segundo plano, mesmo

se o usuário muda para outro aplicativo. Além disso, um componente pode se ligar a um

serviço para interagir com ele e até mesmo realizar a comunicação entre processos

(IPC). Por exemplo, um serviço pode lidar com as transações de rede, tocar música,

executar I/O, ou interagir com um provedor de conteúdo, todos no fundo.

Um serviço pode essencialmente de duas formas:

Iniciado

Um serviço é "iniciado" quando um componente da aplicação (como uma

atividade) inicia-o chamando startService(). Uma vez iniciado, o serviço pode

ser executado em segundo plano por tempo indeterminado, mesmo se o

componente que o começou é destruído. Normalmente, um serviço iniciado

executa uma única operação e não retorna um resultado para o chamador. Por

exemplo, pode fazer o download ou upload de um arquivo pela rede. Quando a

operação é feita, o serviço deve parar.

Ligado

Um serviço é "ligado" quando um componente da aplicação liga-se a ele

chamando bindService(). Um serviço vinculado oferece uma interface cliente-

servidor que permite que os componentes interajam com o serviço, enviar

pedidos, obter resultados, e até mesmo fazê-lo através de processos de

comunicação entre processos (IPC). Um serviço vinculado só é executado

enquanto outro componente de aplicação está vinculado a ele. Vários

componentes podem ligar para o serviço de uma só vez, mas quando todos eles

se desvinculam, o serviço é destruído.

Embora essa documentação geralmente aborda esses dois tipos de serviços

separadamente, o serviço pode funcionar nos dois sentidos, ele pode ser iniciado (para

rodar indefinidamente) e também permitir a ligação. É simplesmente uma questão de

Page 67: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 67

saber se você implementa a dupla de métodos: onStartCommand() para permitir ao os

componentes iniciá-lo e onBind() para permitir a ligação.

Independentemente de sua aplicação ser “iniciada”, “ligada”, ou ambos, qualquer

componente de aplicativo pode usar o serviço (mesmo a partir de um aplicativo

separado), da mesma forma que qualquer componente pode usar uma atividade –

iniciando-a com uma Intent. No entanto, você pode declarar o serviço como privado, no

arquivo de manifesto, e bloquear o acesso de outros aplicativos.

Atenção: O serviço é executado no segmento principal de sua hospedagem, o serviço

de processo não cria seu próprio segmento e não é executado em um processo

separado (a menos que você especifique o contrário). Isso significa que, se o serviço

vai fazer todo o trabalho intensivo da CPU ou o bloqueio de operações (como a

reprodução de MP3 ou de rede), você deve criar um novo segmento dentro do serviço

para fazer esse trabalho. Ao utilizar uma thread separada, você vai reduzir o risco de

erros como aplicação não responde (ANR) e o thread principal do aplicativo pode

permanecer dedicado à interação do usuário com suas atividades.

O básico

Você deve utilizar um serviço ou um thread?

O serviço é simplesmente um componente que pode ser

executado em segundo plano, mesmo quando o usuário não está

interagindo com o aplicativo. Assim, você deve criar um serviço

só para o que você precisa.

Se você precisa realizar o trabalho fora de sua linha principal,

mas apenas enquanto o usuário está interagindo com o aplicativo,

então você deve, provavelmente, criar uma nova thread e não um

serviço. Por exemplo, se você quiser tocar algumas músicas, mas

apenas quando sua atividade está em execução, você pode criar

uma lista de discussão em onCreate(), começar a utilizar em

onStart() , e depois parar em onStop(). Também considere usar

AsyncTask ou HandlerThread, em vez da tradicional classe

Thread.

Lembre-se que se você usar um serviço, ele ainda é executado no

Page 68: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 68

Para criar um serviço,

você deve criar uma

subclasse de Service (ou

uma de suas subclasses existentes). Em sua execução, é necessário substituir alguns

métodos de callback para lidar com aspectos essenciais do ciclo de vida do serviço e

fornecer um mecanismo de componentes para ligar para o serviço, se for o caso. Os

métodos mais importantes de retorno são:

onStartCommand()

O sistema chama este método quando outro componente, como uma atividade,

solicita que o serviço seja iniciado, chamando startService(). Uma vez que este

método é executado, o serviço é iniciado e pode rodar em segundo plano por

tempo indeterminado. Se você implementar essa, é sua a responsabilidade parar

o serviço quando seu trabalho é feito, chamando stopSelf() ou stopService(). (Se

você apenas quiser fornecer ligação, você não precisa aplicar esse método.)

onBind()

O sistema chama este método quando um outro componente quer se vincular

com o serviço (por exemplo, executar RPC), chamando bindService(). Na

implementação deste método, você deve fornecer uma interface que os clientes

usam para se comunicar com o serviço, retornando um IBinder. Você sempre

deve implementar este método, mas se você não quer permitir a ligação, então

você deve retornar null.

onCreate()

O sistema chama este método quando o serviço é criado, para executar os

procedimentos de configuração (antes de chamar qualquer onStartCommand()

ou onBind()). Se o serviço já está em execução, este método não é chamado.

onDestroy()

O sistema chama este método quando o serviço não é mais usado e está sendo

destruído. Seu serviço deve implementar isso para limpar quaisquer recursos,

tais como threads, ouvintes registrados, receptores, etc. Esta é a última chamada

que o serviço recebe.

thread principal do aplicativo por padrão, então você deve ainda

criar um novo segmento dentro do serviço se executa operações

intensivas ou bloqueio.

Page 69: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 69

Se um componente inicia o serviço chamando startService() (o que resulta em uma

chamada para onStartCommand()), o serviço continua funcionando até que ele pare com

stopSelf() ou outro componente deixa-o chamando stopService().

Se um componente chama bindService() para criar o serviço (e onStartCommand() não

é chamado), o serviço funciona somente enquanto o componente está ligado a ele.

Depois que o serviço é desvinculado de todos os clientes, o sistema se destrói.

O sistema Android força a parada de um serviço somente quando estiver com pouca

memória e deve recuperar os recursos do sistema para a atividade que tem o foco do

usuário. Se o serviço está vinculado a uma atividade que tem o foco do usuário, então é

menos provável de ser morto, e se o serviço é declarado a ser executado no primeiro

plano (discutido mais tarde), então ele quase nunca vai ser morto. Caso contrário, se o

serviço foi iniciado e está rodando há muito tempo, então o sistema irá baixar a sua

posição na lista de tarefas em segundo plano ao longo do tempo e o serviço se tornará

altamente suscetível a matar-se - se o serviço é iniciado, então você deve projetá-lo

elegantemente para lançar reinício pelo sistema. Se o sistema mata o seu serviço, ele

reinicia logo que os recursos se tornam novamente disponíveis (embora isso também

dependa do valor que você retornar do onStartCommand(), como será discutido mais

tarde).

Declarando um serviço no manifesto

Como atividades (e outros componentes), você deve declarar todos os serviços do

arquivo de manifesto do aplicativo.

Para declarar seu serviço, adicione um elemento <service> como um filho do elemento

<application>. Por exemplo:

<manifest ... >

...

<application ... >

<service android:name=".ExampleService" />

...

</application>

</manifest>

Existem outros atributos que você pode incluir no <service> para definir propriedades,

como as permissões necessárias para iniciar o serviço e o processo em que o serviço

deve ser executado.

Page 70: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 70

Assim como uma atividade, um serviço pode definir filtros que permitem a intenção de

outros componentes para invocar o serviço utilizando as intenções implícitas. Ao

declarar a intenção de filtros, componentes de qualquer aplicativo instalados no

aparelho do usuário podem, potencialmente, iniciar o seu serviço se o serviço de

declarar a intenção de filtro que corresponde à intenção outro aplicativo passa a

startService() .

Se você planeja usar o seu serviço apenas localmente (as outras aplicações não o usam),

então você não precisa (e não deve) prestar quaisquer filtros de intenção. Sem qualquer

intenção de filtros, você deve iniciar o serviço usando uma intenção explícita dos nomes

de classe do serviço.

Além disso, você pode garantir que seu serviço seja privado, basta somente você incluir

o atributo android:exported e defini-lo como "false". É eficaz mesmo se o serviço

fornece filtros de intenção.

Criando um serviço iniciado

Um serviço iniciado é um de outro

componente que se inicia chamando

startService(), resultando em uma chamada

para o método onStartCommand() do serviço.

Quando um serviço é iniciado, ele tem um

ciclo de vida que é independente do

componente que começou e o serviço pode

funcionar em segundo plano por tempo

indeterminado, mesmo se o componente que

o começou é destruído. Como tal, o serviço

deve parar quando seu trabalho é feito

chamando stopSelf(), ou outro componente

pode pará-lo, chamando stopService().

Um componente de aplicação, como uma atividade, pode iniciar o serviço chamando

startService() e passando uma Intent que especifica o serviço e inclui todos os dados

para o serviço deve usar. O serviço recebe essa Intent no método onStartCommand().

Segmentação Android 1.6 ou inferior

Se você estiver construindo uma

aplicação para o Android 1.6 ou

inferior, você precisa implementar

onStart(), em vez de

onStartCommand() (no Android 2.0,

onStart() foi depreciado em favor do

onStartCommand()).

Para obter mais informações sobre a

prestação de compatibilidade com

versões do Android superiores a 2.0,

consulte a documentação de

onStartCommand().

Page 71: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 71

Por exemplo, suponha que uma atividade precisa salvar alguns dados para um banco de

dados on-line. A atividade pode iniciar um serviço e entregá-lo para guardar os dados,

passando a intenção de startService(). O serviço recebe a intenção em

onStartCommand(), se conecta à Internet e executa a operação de banco de dados.

Quando a transação estiver concluída, o serviço o pára e ele é destruído.

Atenção: Um serviço executa no mesmo processo do aplicativo no qual ele é declarado

e na thread principal da aplicação, por padrão. Assim, se o serviço realiza intensivo ou

o bloqueio de operações, enquanto o usuário interage com uma atividade a partir do

mesmo aplicativo, o serviço vai abrandar o desempenho da atividade. Para evitar

afetar o desempenho do aplicativo, você deve iniciar uma nova thread dentro do

serviço.

Tradicionalmente, há duas classes que você pode estender para criar um serviço

iniciado:

Service

Esta é a classe base para todos os serviços. Quando você estender essa classe, é

importante que você crie um novo segmento para fazer todo o trabalho, pois o

serviço usa a linha principal do aplicativo, por padrão, o que poderia diminuir o

desempenho de qualquer atividade de sua aplicação que está rodando.

IntentService

Esta é uma subclasse de Service que utiliza um thread de trabalho para lidar com

todos os pedidos de início, um de cada vez. Esta é a melhor opção se você não

exigir que o serviço de lidar com várias solicitações em simultâneo. Tudo que

você precisa fazer é implementar onHandleIntent(), que recebe a intenção de

cada solicitação de início para que você possa fazer o trabalho de fundo.

Estendendo a classe IntentService

Porque a maioria dos serviços iniciados não precisam lidar com múltiplas solicitações

ao mesmo tempo (que pode realmente ser um cenário perigoso de multi-threading), é

melhor se você implementar o seu serviço usando o IntentService.

O IntentService faz o seguinte:

Page 72: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 72

Cria um thread de trabalho padrão que executa todas as intenções entregues ao

onStartCommand() em separado do thread principal de sua aplicação.

Cria uma fila de trabalho que passa uma intenção de cada vez para seu

onHandleIntent() de execução, para que não tenha que se preocupar com multi-

threading.

Interrompe o serviço, após todos os pedidos de início ter sido manipulados,

então você nunca tem que chamar stopSelf().

Fornece implementação padrão de onBind() que retorna nulo.

Fornece uma implementação padrão de onStartCommand() que envia a intenção

da fila de trabalho e, em seguida, à onHandleIntent() de execução.

Tudo isto se acrescenta ao fato de que tudo que você precisa fazer é implementar

onHandleIntent() para fazer o trabalho fornecido pelo cliente. (Embora, você também

precisa fornecer um construtor pequeno para o serviço).

Aqui está um exemplo de implementação de IntentService :

public class HelloIntentService extends IntentService {

/**

* A constructor is required, and must call the super IntentService(String)

* constructor with a name for the worker thread.

*/

public HelloIntentService() {

super("HelloIntentService");

}

/**

* The IntentService calls this method from the default worker thread with

* the intent that started the service. When this method returns, IntentService

* stops the service, as appropriate.

*/

@Override

protected void onHandleIntent(Intent intent) {

// Normally we would do some work here, like download a file.

// For our sample, we just sleep for 5 seconds.

long endTime = System.currentTimeMillis() + 5*1000;

while (System.currentTimeMillis() < endTime) {

synchronized (this) {

try {

wait(endTime - System.currentTimeMillis());

} catch (Exception e) {

}

Page 73: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 73

}

}

}

}

Isso é tudo que você precisa: um construtor e uma implementação de onHandleIntent().

Se você decidir também substituir os métodos de retorno de chamada, tais como

onCreate(), onStartCommand(), ou onDestroy(), não se esqueça de chamar a

implementação de super, de modo que o IntentService possa lidar corretamente com a

vida do thread.

Por exemplo, onStartCommand() deve retornar a implementação padrão (que é como a

intenção é entregue a onHandleIntent()):

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

return super.onStartCommand(intent,flags,startId);

}

Além do onHandleIntent(), o único método a partir do qual você não precisa chamar o

super classe é onBind() (mas você só precisa implementar caso o serviço permita a

ligação).

Estendendo a classe de serviço

Como você viu na seção anterior, utilizar IntentService faz sua implementação de um

serviço iniciado muito simples. Se, no entanto, é necessário o seu serviço executar

multi-threading (em vez de processar pedidos através da fila de trabalho), então você

pode estender a classe Service para lidar com cada intenção.

Para efeito de comparação, o código de exemplo a seguir é uma implementação do

Service que executa o mesmo trabalho exatamente como o exemplo acima usando

IntentService. Ou seja, para cada solicitação, ele usa um thread para executar o trabalho

e os processos de um único pedido por vez.

public class HelloService extends Service {

private Looper mServiceLooper;

private ServiceHandler mServiceHandler;

// Handler that receives messages from the thread

private final class ServiceHandler extends Handler {

public ServiceHandler(Looper looper) {

Page 74: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 74

super(looper);

}

@Override

public void handleMessage(Message msg) {

// Normally we would do some work here, like download a file.

// For our sample, we just sleep for 5 seconds.

long endTime = System.currentTimeMillis() + 5*1000;

while (System.currentTimeMillis() < endTime) {

synchronized (this) {

try {

wait(endTime - System.currentTimeMillis());

} catch (Exception e) {

}

}

}

// Stop the service using the startId, so that we don't stop

// the service in the middle of handling another job

stopSelf(msg.arg1);

}

}

@Override

public void onCreate() {

// Start up the thread running the service. Note that we create a

// separate thread because the service normally runs in the process's

// main thread, which we don't want to block. We also make it

// background priority so CPU-intensive work will not disrupt our UI.

HandlerThread thread = new HandlerThread("ServiceStartArguments",

Process.THREAD_PRIORITY_BACKGROUND);

thread.start();

// Get the HandlerThread's Looper and use it for our Handler

mServiceLooper = thread.getLooper();

mServiceHandler = new ServiceHandler(mServiceLooper);

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

// For each start request, send a message to start a job and deliver the

// start ID so we know which request we're stopping when we finish the job

Message msg = mServiceHandler.obtainMessage();

msg.arg1 = startId;

mServiceHandler.sendMessage(msg);

// If we get killed, after returning from here, restart

return START_STICKY;

}

@Override

public IBinder onBind(Intent intent) {

// We don't provide binding, so return null

return null;

}

Page 75: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 75

@Override

public void onDestroy() {

Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();

}

}

Page 76: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 76

Como você pode ver, dá muito mais trabalho do que usar um IntentService.

No entanto, como você lida com cada chamada para onStartCommand() por si só, você

pode executar várias solicitações em simultâneo. Isso não é o que este exemplo faz, mas

se é isso que você quer então você pode criar um novo tópico para cada pedido e

executá-los imediatamente (em vez de aguardar a solicitação anterior para terminar).

Observe que o método onStartCommand() deve retornar um inteiro. O inteiro é um

valor que descreve como o sistema deve continuar o serviço a partir do evento em que o

sistema o mata (como discutido acima, a implementação padrão para IntentService trata

isso para você, mas você é capaz de modificá-lo). O valor de retorno de

onStartCommand() deve ser uma das seguintes constantes:

START_NOT_STICKY

Se o sistema mata o serviço após onStartCommand() retornar, não recria o

serviço, salvo se houver pendência de intenções para entregar. Esta é a opção

mais segura para evitar a execução de seu serviço quando não for necessário e

quando a sua aplicação pode simplesmente reiniciar os trabalhos inacabados.

START_STICKY

Se o sistema mata o serviço após onStartCommand() retornar, recria o serviço e

chamar onStartCommand(), mas não entrega novamente a última intenção. Em

vez disso, o sistema chama onStartCommand() com uma intenção nula, a menos

que houver pendência de intenções para iniciar o serviço, nesse caso, os

propósitos são entregues. Isso é adequado para media players (ou afins) que não

estão executando comandos, mas rodam indefinidamente à espera de uma

interação.

START_REDELIVER_INTENT

Se o sistema mata o serviço após onStartCommand() retornar, recria o serviço e

chama onStartCommand() com a última intenção que foi entregue para o

serviço. Quaisquer intenções pendentes são entregues em troca. Isso é adequado

para os serviços que estão ativamente realizando um trabalho que deve ser

imediatamente reiniciado, como baixar um arquivo.

Page 77: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 77

Iniciando um serviço

Você pode iniciar um serviço de uma atividade ou componente de outro aplicativo por

meio de um Intent (especificando o serviço para iniciar) para startService(). O sistema

Android chama o método onStartCommand() do serviço do e passa a Intent. (Você

nunca deve chamar onStartCommand() diretamente.)

Por exemplo, uma atividade pode iniciar o serviço de exemplo na seção anterior

(HelloSevice) com a intenção explícita com startService():

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

startService(intent);

O startService() retorna imediatamente e o sistema Android chama o método

onStartCommand() do serviço. Se o serviço não estiver sendo executado, o sistema

chama primeiro onCreate(), em seguida, chama onStartCommand().

Se o serviço não é provê ligação, a intenção entregue com startService() é o único modo

de comunicação entre o componente de aplicação e o serviço. No entanto, se você

quiser o serviço para enviar um resultado de volta, o cliente que inicia o serviço pode

criar uma PendingIntent para uma transmissão (com getBroadcast()) e entregá-lo ao

serviço da Intent que inicia o serviço. O serviço pode então usar a transmissão para

fornecer um resultado.

Vários pedidos para iniciar o resultado do serviço em várias correspondências chamam

o onStartCommand() do serviço. No entanto, apenas um pedido para parar o serviço

(com stopSelf() ou stopService()) é necessário.

Parando um serviço

O serviço iniciado deve gerenciar seu próprio ciclo de vida. Ou seja, o sistema não para

ou destrói o serviço a menos que ele deva recuperar a memória do sistema e o serviço

continua a funcionar após onStartCommand() retornar. Assim, o serviço deve parar,

chamando stopSelf() ou outro componente pode pará-lo, chamando stopService().

Uma vez solicitado a parar com stopSelf() ou stopService(), o sistema destrói o serviço

o mais rapidamente possível.

No entanto, se o serviço trabalha com pedidos múltiplos para onStartCommand() ao

mesmo tempo, então você não deve interromper o serviço quando tiver terminado o

Page 78: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 78

processamento de um pedido inicial, porque você pode ter uma vez recebido um pedido

novo começo (parando no final do primeiro pedido iria encerrar a segunda). Para evitar

esse problema, você pode usar stopSelf(int) para garantir que o seu pedido para parar o

serviço é sempre baseado no início pedido mais recente. Ou seja, quando você chamar

stopSelf(int), você passa o ID do pedido inicial (o startId entregue para

onStartCommand()) ao qual o pedido de parada corresponde. Então, se o serviço

recebeu um pedido antes de você fosse capaz de chamar stopSelf(int) , o ID não

corresponderá e o serviço não vai parar.

Atenção: É importante que o aplicativo pare seus serviços quando o trabalho está

pronto, para evitar o desperdício de recursos do sistema e consumo de bateria. Se

necessário, outros componentes podem interromper o serviço pela chamada de

stopService(). Mesmo se você permitir chamada ao serviço, você deve sempre parar o

serviço a si mesmo se ele já recebeu uma chamada para onStartCommand().

Criando um serviço vinculado

Um serviço vinculado é aquele que permite que os componentes do aplicativo se

vinculem a ele chamando bindService() para criar uma ligação de longa data (e

geralmente não permitem aos componentes iniciá-lo, chamando startService()).

Você deve criar um serviço ligado quando você quiser interagir com o serviço de

atividades e outros componentes em seu aplicativo ou para expor algumas das

funcionalidades do aplicativo para outros aplicativos, através da comunicação entre

processos (IPC).

Para criar um serviço vinculado, você deve implementar o método onBind()para

retornar um IBinder que define a interface de comunicação com o serviço. Outros

componentes do aplicativo pode então chamar bindService() para recuperar a interface e

começar a chamar os métodos do serviço. O serviço só vive para servir o componente

de aplicação que é ligado a ele, então quando não há componentes vinculados ao

serviço, o sistema o destrói (você não precisa parar um serviço ligado no jeito que você

tem quando o serviço é iniciado através onStartCommand()).

Para criar um serviço vinculado, a primeira coisa que você deve fazer é definir a

interface que especifica como um cliente pode se comunicar com o serviço. Essa

interface entre o serviço e o cliente deve ser uma implementação de IBinder e é o que o

Page 79: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 79

seu serviço deve retornar a partir do método de retorno onBind(). Uma vez que o cliente

recebe a IBinder , pode começar a interagir com o serviço por meio dessa interface.

Vários clientes podem se ligar ao serviço de uma só vez. Quando um cliente concluiu a

interação com o serviço, ele chama unbindService() para se desvincular. Uma vez que

não há clientes vinculados ao serviço, o sistema destrói o serviço.

Enviando notificações para o usuário

Uma vez em execução, um serviço pode notificar o usuário de eventos usando

notificações toast ou notificações da barra de status.

Uma notificação toast é uma mensagem que aparece na superfície da janela atual por

um momento e depois desaparece, enquanto uma notificação de barra de status fornece

um ícone na barra de status com uma mensagem, que o usuário pode selecionar a fim de

tomar uma ação (como iniciar uma atividade).

Normalmente, uma notificação de barra de status é a melhor técnica quando algum

trabalho de fundo tenha sido concluído (como um download do arquivo completo) e

agora o usuário pode agir sobre ela. Quando o usuário seleciona a notificação a partir da

visão expandida, a notificação pode iniciar uma atividade (tal como para visualizar o

arquivo baixado).

Executando um serviço em primeiro plano

Um serviço de primeiro plano é um serviço que é considerado como sendo algo que o

usuário esteja atento e, portanto, não é um candidato para o sistema matar quando com

pouca memória. Um serviço de primeiro plano deve prever uma notificação na barra de

status, que é colocado abaixo da posição "em curso", o que significa que a notificação

não pode ser dispensada a menos que o serviço ou está parado ou foi removido do

primeiro plano.

Por exemplo, um leitor de música que toca a partir de um serviço, deve ser definido para

ser executado em primeiro plano, porque o usuário está explicitamente consciente do

seu funcionamento. A notificação na barra de status pode indicar a música atual e

permitir que o usuário inicie uma atividade para interagir com o leitor de música.

Page 80: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 80

Para solicitar que o serviço seja executado em primeiro plano, chame startForeground().

Este método tem dois parâmetros: um número inteiro que identifica a notificação e a

notificação na barra de status. Por exemplo:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),

System.currentTimeMillis());

Intent notificationIntent = new Intent(this, ExampleActivity.class);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(this, getText(R.string.notification_title),

getText(R.string.notification_message), pendingIntent);

startForeground(ONGOING_NOTIFICATION, notification);

Para remover o serviço do primeiro plano, chama-se stopForeground(). Este método tem

um valor booleano, indicando se deseja remover a notificação de barra de status

também. Este método não para o serviço. No entanto, se você parar o serviço enquanto

ele ainda está executando em primeiro plano a notificação também é removida.

Nota: Os métodos startForeground() e stopForeground() foram introduzidos no

Android 2.0 (API Nível 5).

Gerenciando ciclo de vida de um serviço

O ciclo de vida de um serviço é muito mais simples do que o de uma atividade. No

entanto, é ainda mais importante que você preste atenção à forma como o serviço é

criado e destruído, porque um serviço pode ser executado em segundo plano, sem que o

utilizador perceba.

O ciclo de vida do serviço – desde quando ele é criado até quando ele é destruído - pode

seguir dois caminhos diferentes:

O serviço começou

O serviço é criado quando um outro componente chama startService(). O serviço é

executado indefinidamente e, em seguida, deve ser parado chamando stopSelf().

Outro componente também pode interromper o serviço pela chamada de

stopService(). Quando o serviço for interrompido, o sistema o destrói.

Um serviço vinculado

O serviço é criado quando um outro componente (um cliente) chama bindService().

O cliente se comunica com o serviço através de uma interface IBinder. O cliente

Page 81: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 81

pode fechar a conexão chamando unbindService(). Vários clientes podem chamar o

mesmo serviço e quando todos eles desvincularem-se, o sistema destrói o serviço.

(O serviço não precisa parar si mesmo).

Estes dois caminhos não são totalmente distintos. Ou seja, você pode chamar um

serviço que já foi iniciado com startService(). Por exemplo, um serviço de música de

fundo pode ser iniciado chamando startService() com uma Intent que identifica a música

a tocar. Mais tarde, possivelmente quando o usuário deseja exercer algum controle sobre

o player ou obter informações sobre a música atual, uma atividade pode se ligar ao

serviço chamando bindService(). Em casos como este, stopService() ou stopSelf() não

chegam a parar o serviço até que todos os clientes se desacoplem.

Aplicando o ciclo de vida dos callbacks

Como uma atividade, um ciclo de vida do serviço tem métodos de retorno que você

pode implementar para monitorar as mudanças no estado do serviço e realizar o trabalho

no momento oportuno. O seguinte esqueleto de serviço demonstra todos os métodos de

ciclo de vida:

Figura 2. O ciclo de vida do serviço. O diagrama à esquerda mostra o ciclo de vida quando o serviço é

criado com startService() e o diagrama da direita mostra o ciclo de vida quando o serviço é criado com

bindService().

Page 82: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 82

public class ExampleService extends Service {

int mStartMode; // indicates how to behave if the service is killed

IBinder mBinder; // interface for clients that bind

boolean mAllowRebind; // indicates whether onRebind should be used

@Override

public void onCreate() {

// The service is being created

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// The service is starting, due to a call to startService()

return mStartMode;

}

@Override

public IBinder onBind(Intent intent) {

// A client is binding to the service with bindService()

return mBinder;

}

@Override

public boolean onUnbind(Intent intent) {

// All clients have unbound with unbindService()

return mAllowRebind;

}

@Override

public void onRebind(Intent intent) {

// A client is binding to the service with bindService(),

// after onUnbind() has already been called

}

@Override

public void onDestroy() {

// The service is no longer used and is being destroyed

}

}

Nota: Ao contrário métodos de retorno do ciclo de vida da atividade, você não é

obrigado a chamar a implementação da superclasse dos métodos de callback.

Ao implementar esses métodos, você pode controlar dois loops aninhados do ciclo de

vida do serviço:

A vida inteira de um serviço acontece entre o momento onCreate() ser

chamado e o tempo onDestroy() retornado. Como uma atividade, um serviço

tem a sua configuração inicial em onCreate() e libera todos os recursos

remanescentes em onDestroy(). Por exemplo, um serviço de reprodução de

música pode criar o segmento onde a música será tocada em onCreate(),

então para a thread em onDestroy().

Page 83: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 83

Os métodos onCreate() e onDestroy() são chamados para todos os serviços,

sejam eles criados por startService() ou bindService() .

A vida ativa de um serviço começa com uma chamada de um

onStartCommand() ou onBind(). Cada método entrega a Intent que foi passado

tanto startService() quanto bindService(), respectivamente.

Se o serviço for iniciado, o tempo de vida ativa termina ao mesmo tempo em que

toda a vida termina (o serviço continua ativo mesmo após onStartCommand()

retornar). Se o serviço está vinculado, a vida ativa termina quando onUnbind()

retorna.

Nota: Apesar de um serviço iniciado ser interrompido por uma chamada para uma

stopSelf() ou stopService(), não há um retorno para o respectivo serviço (não há

onStop() de callback). Então, a menos que o serviço esteja vinculado a um cliente, o

sistema destrói quando o serviço for interrompido, onDestroy() é o retorno recebido

apenas.

A figura 2 ilustra os métodos típicos de retorno de um serviço. Embora a figura separa

os serviços que são criados por startService() daquelas criadas pelos bindService() ,

tenha em mente que qualquer serviço, não importa como ele é iniciado, pode

potencialmente permitir que os clientes chamem-no. Assim, um serviço que foi

inicialmente iniciado com onStartCommand() (por um cliente chamando startService() )

pode ainda receber uma chamada para onBind() (quando um cliente solicita

bindService()).

Page 84: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 84

Serviços vinculados

Um serviço vinculado é o servidor em uma interface cliente-servidor. O serviço permite

que os componentes ligados (como em atividades) vinculem-se ao serviço, enviem

pedidos, recebam respostas, e até mesmo realizem a comunicação entre processos (IPC).

Um serviço ligado normalmente vive apenas enquanto ela serve a outro componente da

aplicação e não é executado em segundo plano por tempo indeterminado.

O básico

Um serviço ligado é uma

implementação da classe

Service que permite que

outros aplicativos se liguem e

interajam com ele. Para

fornecer ligação para um

serviço, você deve

implementar o onBind()

método de retorno. Esse

método retorna um objeto

IBinder que define a interface

de programação que os

clientes podem usar para

interagir com o serviço.

Um cliente pode se ligar ao

serviço chamando

bindService(). Quando isso

acontecer, ele deve fornecer

uma implementação de

ServiceConnection, que

monitora a conexão com o

serviço. O método

bindService() retorna

imediatamente sem um valor,

Vinculação a um serviço iniciado

Conforme discutido no capítulo sobre Serviço, você pode

criar um serviço que seja iniciado e vinculado. Ou seja, o

serviço pode ser iniciado chamando startService(), que

permite que o serviço seja executado indefinidamente, e

também permite que um cliente se amarre ao serviço

chamando bindService().

Se você permitir que seu serviço seja iniciado e ligado, em

seguida, quando o serviço for iniciado, o sistema não

destruirá o serviço quando todos os clientes desacoplarem.

Em vez disso, você deve explicitamente parar o serviço,

chamando stopSelf() ou stopService().

Embora você geralmente deva implementar onBind() ou

onStartCommand(), às vezes é necessário aplicar a ambos.

Por exemplo, um tocador de música pode ser útil para

permitir o serviço a ser executado indefinidamente e

também fornecer vínculo. Desta forma, uma atividade pode

iniciar o serviço para jogar alguma música e a música

continua a tocar mesmo se o usuário sai do aplicativo.

Então, quando o usuário retorna para a aplicação, a

atividade pode ligar para o serviço para recuperar o

controle da reprodução.

Certifique-se de ler a seção sobre Gerenciamento do ciclo

de vida de um serviço vinculado, para obter mais

informações sobre o ciclo de vida do serviço, quando for

feita adição de ligação para um serviço iniciado.

Page 85: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 85

mas quando o sistema Android cria a conexão entre o cliente e o serviço, ele chama

onServiceConnected() na ServiceConnection para entregar o IBinder que o cliente pode

usar para se comunicar com o serviço.

Vários clientes podem se conectar ao serviço de uma só vez. No entanto, o sistema

chama o método onBind() do serviço para recuperar o IBinder somente quando o cliente

liga-se em primeiro lugar. O sistema, em seguida, oferece os mesmos IBinder para

quaisquer clientes que ligam, sem chamar onBind() novamente.

Quando o libera último cliente do serviço, o sistema destrói o serviço (a menos que o

serviço também foi iniciado por startService() ).

Quando você implementar o seu serviço vinculado, a parte mais importante é definir a

interface que o seu método callback onBind() retorna. Existem algumas maneiras

diferentes que você pode definir o seu serviço da interface IBinder e a seção a seguir

discute cada técnica.

Criando um serviço ligado

Ao criar um serviço que oferece ligação, você deve fornecer um IBinder que fornece a

interface de programação que os clientes podem utilizar para interagir com o serviço.

Existem três maneiras com as quais você pode definir a interface:

Estendendo a classe Binder

Se o serviço é privado para sua própria aplicação e é executado no mesmo

processo do cliente (o que é comum), você deve criar a sua interface extendendo

o Binder e retornando uma instância a partir onBind(). O cliente recebe o Binder

e pode usá-lo para acessar diretamente os métodos públicos disponíveis em

qualquer um dos Binder implementados ou até mesmo o Service.

Esta é a técnica preferida quando o serviço é apenas um trabalhador de fundo

para o seu próprio aplicativo. A única razão em que você não deve criar a

interface desta maneira é porque o serviço é utilizado por outras aplicações ou

através de processos separados.

Page 86: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 86

Usando um Messenger

Se você precisa de sua interface para trabalhar em processos diferentes, você

pode criar uma interface para o serviço com um Messenger. Desta forma, o

serviço define um Handler, que responde a diferentes tipos de mensagem de

objetos. Este Handler é a base para um Messenger que podem compartilhar um

IBinder com o cliente, permitindo que o cliente envie comandos para o serviço

usando mensagem de objetos. Além disso, o cliente pode definir um Messenger

próprio para que o serviço possa enviar mensagens de volta.

Esta é a maneira mais simples para realizar a comunicação entre processos

(IPC), porque o Messenger enfilera todas as solicitações em um único segmento

para que você não tenha que projetar seu serviço a ser um thread-safe.

Usando AIDL

AIDL (Android Interface Definition Language) realiza todo o trabalho de

decompor os objetos primitivos em que o sistema operacional possa entender

através de processos para executar IPC. A técnica anterior, usando um

Messenger, é realmente baseado em AIDL como a sua estrutura subjacente.

Como mencionado acima, o Messenger cria uma fila de todas as solicitações do

cliente em um único segmento, para que o serviço receba solicitações de um de

cada vez. Se, no entanto, você quiser que o seu serviço lide com múltiplas

solicitações ao mesmo tempo, então você pode usar AIDL diretamente. Neste

caso, o serviço deve ser capaz de multi-threading e ser construído thread-safe.

Para usar AIDL diretamente, você deve criar uma arquivo .aidl que define a

interface de programação. As ferramentas do Android SDK usam esse arquivo

para gerar uma classe abstrata que implementa a interface e lida com o IPC, que

você pode estender dentro do seu serviço.

Nota: A maioria dos aplicativos não devem usar AIDL para criar um serviço

ligado, porque ele pode exigir recursos de multithreading e pode resultar em

uma implementação mais complicada. Como tal, AIDL não é adequado para a

maioria das aplicações e este documento não explica como usá-lo para seu

serviço.

Page 87: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 87

Estendendo a classe Binder

Se o serviço é usado somente pela aplicação local e não precisa trabalhar em todos os

processos, então você pode implementar seu próprio Binder que fornece o acesso direto

do cliente com os métodos públicos no serviço.

Nota: Isto só funciona se o cliente e o serviço estão no mesmo aplicativo e processo, o

que é mais comum. Por exemplo, isso iria funcionar bem para um aplicativo de música

que tem o efeito de vincular uma atividade a seu próprio serviço de música que está

tocando no fundo.

Veja como configurá-lo:

1. Em seu serviço, criar uma instância do Binder que:

o contém métodos públicos que o cliente pode chamar

o retorna o atual Service, que tem métodos públicos que o cliente pode

chamar

o ou, retorna uma instância de outra classe hospedada pelo serviço com

métodos públicos que o cliente pode chamar

2. Retornar essa instância do Binder do método de retorno onBind().

3. No cliente, receber a Binder do método de retorno onServiceConnected() e

fazer chamadas para o serviço vinculado usando os métodos fornecidos.

Nota: A razão pela qual o serviço e o cliente devem estar no mesmo pedido é porque o

cliente pode converter o objeto retornado e chamar propriamente de sua API. O serviço

e o cliente também devem estar no mesmo processo, porque essa técnica não exerce

qualquer triagem em todos os processos.

Por exemplo, aqui está um serviço que oferece aos clientes o acesso a métodos no

serviço por meio de um Binder de execução:

public class LocalService extends Service {

// Binder given to clients

private final IBinder mBinder = new LocalBinder();

// Random number generator

private final Random mGenerator = new Random();

Page 88: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 88

/**

* Class used for the client Binder. Because we know this service always

* runs in the same process as its clients, we don't need to deal with IPC.

*/

public class LocalBinder extends Binder {

LocalService getService() {

// Return this instance of LocalService so clients can call public methods

return LocalService.this;

}

}

@Override

public IBinder onBind(Intent intent) {

return mBinder;

}

/** method for clients */

public int getRandomNumber() {

return mGenerator.nextInt(100);

}

}

O LocalBinder fornece o método getService() de clientes para recuperar a instância

atual do LocalService. Isso permite aos clientes chamarem métodos públicos no serviço.

Por exemplo, os clientes podem chamar getRandomNumber() do serviço.

Aqui está uma atividade que se liga a LocalService e solicita getRandomNumber()

quando um botão é clicado:

public class BindingActivity extends Activity {

LocalService mService;

boolean mBound = false;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

@Override

protected void onStart() {

super.onStart();

// Bind to LocalService

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

bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

}

@Override

protected void onStop() {

super.onStop();

// Unbind from the service

if (mBound) {

Page 89: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 89

unbindService(mConnection);

mBound = false;

}

}

/** Called when a button is clicked (the button in the layout file attaches to

* this method with the android:onClick attribute) */

public void onButtonClick(View v) {

if (mBound) {

// Call a method from the LocalService.

// However, if this call were something that might hang, then this request should

// occur in a separate thread to avoid slowing down the activity performance.

int num = mService.getRandomNumber();

Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();

}

}

/** Defines callbacks for service binding, passed to bindService() */

private ServiceConnection mConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName className,

IBinder service) {

// We've bound to LocalService, cast the IBinder and get LocalService instance

LocalBinder binder = (LocalBinder) service;

mService = binder.getService();

mBound = true;

}

@Override

public void onServiceDisconnected(ComponentName arg0) {

mBound = false;

}

};

}

O exemplo acima mostra como o cliente liga para o serviço usando uma implementação

do ServiceConnection e o callback de onServiceConnected().

Nota: O exemplo acima não é explicitamente desvinculado do serviço, mas todos os

clientes devem desvincular em um tempo adequado (por exemplo, quando a atividade

pausa).

Page 90: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 90

Usando um Messenger

Se você precisar de seu serviço para se

comunicar com processos remotos,

então você pode usar um Messenger

para fornecer a interface para o serviço.

Esta técnica permite realizar a

comunicação entre processos (IPC),

sem a necessidade de utilizar AIDL.

Aqui está um resumo de como usar um

Messenger:

O serviço implementa um

Handler que recebe um callback

para cada chamada de um

cliente.

O Handler é usado para criar uma Messenger (que é uma referência para o

Handler).

O Messenger cria um IBinder que o serviço retorna para clientes de onBind().

Os clientes usam o IBinder para instanciar o Messenger (que faz referência ao

serviço Handler), que o cliente usa para enviar mensagens para o serviço.

O serviço recebe cada mensagem em seu Handler, especificamente, no método

handleMessage().

Desta forma, não existem "métodos" para o cliente para chamar o serviço. Em vez

disso, o cliente fornece as "mensagens" (objetos Message) que o serviço recebe em seu

Handler.

Aqui está um exemplo de serviço simples que usa um Messenger interface:

public class MessengerService extends Service {

/** Command to the service to display a message */

static final int MSG_SAY_HELLO = 1;

/**

* Handler of incoming messages from clients.

Comparado com AIVD

Quando você precisa realizar IPC, utilizar um

Messenger para a sua interface é mais simples do

que implementá-la com AIDL, porque Messenger

enfilera todas as chamadas para o serviço, que,

uma interface AIDL pura envia pedidos

simultâneos para o serviço, que deve, então, lidar

com multi-threading.

Para a maioria das aplicações, o serviço não

precisa executar multi-threading, portanto, usar

um Messenger permite ao serviço lidar com uma

chamada ao mesmo tempo. Se é importante que o

seu serviço seja multi-threaded, então você deve

usar AIDL para definir sua interface.

Page 91: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 91

*/

class IncomingHandler extends Handler {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_SAY_HELLO:

Toast.makeText(getApplicationContext(), "hello!",

Toast.LENGTH_SHORT).show();

break;

default:

super.handleMessage(msg);

}

}

}

/**

* Target we publish for clients to send messages to IncomingHandler.

*/

final Messenger mMessenger = new Messenger(new IncomingHandler());

/**

* When binding to the service, we return an interface to our messenger

* for sending messages to the service.

*/

@Override

public IBinder onBind(Intent intent) {

Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();

return mMessenger.getBinder();

}

}

Observe que o método handleMessage() no Handler é onde o serviço recebe a entrada

Message e decide o que fazer, com base no membro what.

Tudo o que um cliente precisa fazer é criar um Messenger com base no IBinder

retornado pelo serviço e enviar uma mensagem usando o send(). Por exemplo, aqui é

uma atividade simples, que liga para o serviço e oferece a mensagem

MSG_SAY_HELLO para o serviço:

public class ActivityMessenger extends Activity {

/** Messenger for communicating with the service. */

Messenger mService = null;

/** Flag indicating whether we have called bind on the service. */

boolean mBound;

/**

* Class for interacting with the main interface of the service.

*/

private ServiceConnection mConnection = new ServiceConnection() {

public void onServiceConnected(ComponentName className, IBinder service) {

Page 92: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 92

// This is called when the connection with the service has been

// established, giving us the object we can use to

// interact with the service. We are communicating with the

// service using a Messenger, so here we get a client-side

// representation of that from the raw IBinder object.

mService = new Messenger(service);

mBound = true;

}

public void onServiceDisconnected(ComponentName className) {

// This is called when the connection with the service has been

// unexpectedly disconnected -- that is, its process crashed.

mService = null;

mBound = false;

}

};

public void sayHello(View v) {

if (!mBound) return;

// Create and send a message to the service, using a supported 'what' value

Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);

try {

mService.send(msg);

} catch (RemoteException e) {

e.printStackTrace();

}

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

@Override

protected void onStart() {

super.onStart();

// Bind to the service

bindService(new Intent(this, MessengerService.class), mConnection,

Context.BIND_AUTO_CREATE);

}

@Override

protected void onStop() {

super.onStop();

// Unbind from the service

if (mBound) {

unbindService(mConnection);

mBound = false;

}

}

}

Page 93: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 93

Observe que este exemplo não mostra como o serviço pode responder ao cliente. Se

você deseja que o serviço responda, então você também precisa criar um Messenger no

cliente. Então, quando o cliente recebe a chamada onServiceConnected(), ele envia uma

mensagem para o serviço, que inclui o Messenger do cliente no parâmetro replyTo d

método send().

Você pode ver um exemplo de como fornecer mensagens bidirecionais no

MessengerService.java (serviço) e MessengerServiceActivities.java (cliente).

Vinculação a um serviço

Componentes da aplicação (clientes) podem se ligar a um serviço chamando

bindService(). O sistema Android, em seguida, chama o serviço de onBind(), método

que retorna um IBinder para interagir com o serviço.

A ligação é assíncrona, bindService() retorna imediatamente e não retorna a IBinder

para o cliente. Para receber o IBinder, o cliente deve criar uma instância de

ServiceConnection e o passa para bindService(). O ServiceConnection inclui um

método de resposta que o sistema chama para entregar o IBinder.

Nota: Só as atividades, serviços e provedores de conteúdo podem se ligar a um serviço

- você não pode se ligar a um serviço a partir de um receptor de broadcast.

Assim, para ligar a um serviço de seu cliente, você deve:

1. Implementar ServiceConnection .

A implementação deve substituir dois métodos:

onServiceConnected(): O sistema chama isso para entregar o IBinder retornado

pelo método onBind() do serviço.

onServiceDisconnected(): O sistema Android chama isso quando a conexão

com o serviço é perdida inesperadamente, como quando o serviço foi paralisada

ou tenha sido morta. Isto não é chamado quando o cliente libera.

2. Call bindService(), passando a implementação ServiceConnection.

Page 94: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 94

3. Quando o sistema chama o método de retorno onServiceConnected(), você pode

começar a fazer chamadas para o serviço, utilizando os métodos definidos pela

interface.

4. Para desligar do serviço, chame unbindService().

Quando o cliente for destruído, ele irá desvincular-se do serviço, mas você deve

sempre desvincular quando terminar a interação com o serviço ou quando a

atividade pausar, então o serviço pode parar enquanto não está sendo utilizado.

Por exemplo, o seguinte trecho liga o cliente ao serviço criado anteriormente por

estender a classe Binder, então tudo o que deve fazer é o lançar o IBinder retornado ao

LocalService e solicitar a LocalService:

LocalService mService;

private ServiceConnection mConnection = new ServiceConnection() {

// Called when the connection with the service is established

public void onServiceConnected(ComponentName className, IBinder service) {

// Because we have bound to an explicit

// service that is running in our own process, we can

// cast its IBinder to a concrete class and directly access it.

LocalBinder binder = (LocalBinder) service;

mService = binder.getService();

mBound = true;

}

// Called when the connection with the service disconnects unexpectedly

public void onServiceDisconnected(ComponentName className) {

Log.e(TAG, "onServiceDisconnected");

mBound = false;

}

};

Com este ServiceConnection, o cliente pode ligar para um serviço, passando este para

bindService(). Por exemplo:

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

bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

O primeiro parâmetro de bindService() é uma Intent que, explicitamente, nomeia

o serviço a ser vinculado (pensando que a intenção pode ser implícita).

O segundo parâmetro é o objeto ServiceConnection.

O terceiro parâmetro é um sinalizador que indica as opções para a ligação. Deve

ser geralmente BIND_AUTO_CREATE, a fim de criar o serviço se ainda não

Page 95: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 95

estiver vivo. Outros valores possíveis são BIND_DEBUG_UNBIND e

BIND_NOT_FOREGROUND, ou 0 para nenhum.

Notas adicionais

Aqui estão algumas anotações importantes sobre a ligação a um serviço:

Você deve sempre lançar exceções DeadObjectException, que são lançados

quando a conexão foi quebrada. Esta é a única exceção acionada por métodos

remotos.

Os objetos são referência contada através de processos.

Você normalmente deverá emparelhar a ligação e desligamento durante a

correspondente destruição e desmontagem de momentos do ciclo de vida do

cliente. Por exemplo:

o Se você só precisa interagir com o serviço, enquanto sua atividade é

visível, você deve se ligar durante onStart() e desvincular durante

onStop().

o Se você quiser que a sua atividade receba as respostas, mesmo quando

ele está parado no fundo, então você pode se ligar durante onCreate() e

desvincular durante onDestroy(). Cuidado com o que isso implica que a

sua atividade necessita para usar o serviço durante todo o tempo ele está

rodando (mesmo no fundo), por isso, se o serviço está em outro processo,

então você aumentar o peso do processo e torna-se mais provável do

sistema matá-lo.

Nota: Você normalmente não deveria vincular e desvincular a sua atividade durante o

onResume() e onPause(), porque esses retornos ocorrem em todas as transições do

ciclo de vida e você deve manter o processamento que ocorre nessas transições para

um nível mínimo. Além disso, se há várias atividades em sua aplicação para vincular o

mesmo serviço e não há uma transição entre duas dessas atividades, o serviço pode ser

destruído e recriado como desassocia a atividade atual (durante a pausa) antes da

próxima ligação (durante a retomada).

Page 96: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 96

Gerenciando o ciclo de vida de um serviço de

associação

Figura 1. O ciclo de vida de um serviço que é iniciado e também permite a ligação.

Quando um serviço é desvinculado de todos os clientes, o sistema Android o destrói (a

menos foi iniciado com onStartCommand()). Como tal, você não tem que gerenciar o

ciclo de vida do seu serviço se ele é meramente um limite de serviço - o sistema

Android gerencia isso para você saber se ele está ligado a algum cliente.

No entanto, se você optar por implementar o método de retorno onStartCommand(),

então você deve explicitamente parar o serviço, porque o serviço é considerado agora a

Page 97: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 97

ser iniciado. Neste caso, o serviço é executado até que o serviço para com stopSelf() ou

outro componente chama stopService(), independentemente se está ligado a outros

clientes.

Além disso, se o serviço é iniciado e aceita a ligação, então quando o sistema solicita

sua onUnbind(), você pode opcionalmente retornar true se você gostaria de receber uma

chamada para onRebind() na próxima vez que um cliente chama o serviço (ao invés de

receber uma chamada para onBind()). onRebind() retorna void, mas o cliente ainda

recebe o IBinder na sua onServiceConnected(). A figura 1 ilustra a lógica para este tipo

de ciclo de vida.

Page 98: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 98

Processos e threads

Quando um componente de aplicativo se inicia e a aplicação não possui nenhum outro

componente rodando, o sistema Android inicia um novo processo Linux para a

aplicação com um único thread. Por padrão, todos os componentes do mesmo aplicativo

executam no mesmo processo e thread (chamada de thread "main"). Se um componente

do aplicativo é iniciado e já existe um processo para aquela aplicação (porque um outro

componente do aplicativo existe), então o componente é iniciado dentro desse processo

e usa o mesmo thread de execução. No entanto, você pode arranjar para diferentes

componentes em seu aplicativo para serem executados em processos separados, e você

pode criar threads adicionais para qualquer processo.

Processos

Por padrão, todos os componentes do mesmo aplicativo executam no mesmo processo e

a maioria das aplicações não devem mudar isso. No entanto, se você achar que você

precisa controlar a que processo um determinado componente pertence, pode fazê-lo no

arquivo de manifesto.

A entrada de manifesto para cada tipo de elemento - <activity>, <service , <receiver> e

<provider> - suporta um atributo android:process que pode indicar um processo em que

esse componente deve ser executado. Você pode definir esse atributo para que cada

componente seja executado em seu próprio processo, ou que algumas componentes

compartilham um processo, enquanto outros não. Você também pode definir

android:process de modo que os componentes de diferentes aplicações sejam

executados no mesmo processo - desde que as aplicações compartilhem o mesmo ID de

usuário do Linux e assinem com os mesmos certificados.

O elemento <application> também suporta um atributos android:process, para definir

um valor padrão que se aplica a todos os componentes.

Android pode decidir encerrar um processo em algum momento, quando houver pouca

memória e exigido por outros processos que estão mais imediatamente servindo ao

usuário. Componentes de aplicativos em execução no processo que está morto são

conseqüentemente destruídos. Um processo é iniciado novamente para esses

componentes, quando há trabalho novamente para que eles façam.

Page 99: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 99

Ao decidir quais os processos matar, o sistema Android pesa a sua importância relativa

para o usuário. Por exemplo, é mais facilmente desligado um processo hospedando

atividades que não são mais visíveis na tela, em comparação com um processo

hospedando atividades visíveis. A decisão de encerrar um processo, portanto, depende

do estado dos componentes em execução no processo.

Ciclo de vida do processo

O sistema Android tenta manter um processo de candidatura pelo maior tempo possível,

mas, eventualmente, precisa remover os processos antigos para recuperar a memória

para novos processos ou mais importantes. Para determinar quais os processos manter e

quais matar, o sistema coloca cada processo em uma "hierarquia de importância" com

base nos componentes em execução no processo e o estado desses componentes.

Processos com menor importância são eliminadas em primeiro lugar, em seguida,

aqueles com a menor importância seguinte, e assim por diante, sempre que necessário

para recuperar os recursos do sistema.

Há cinco níveis na hierarquia de importância. A lista a seguir apresenta os diferentes

tipos de processos em ordem de importância (o primeiro processo é o mais importante e

é morto por último):

1. Processo em primeiro plano: Um processo que é necessário para que o usuário

esteja fazendo atualmente. Um processo é considerado em primeiro plano, se

qualquer uma das seguintes condições forem verdadeiras:

Abriga uma Activity com a qual o usuário está interagindo com (o

método onResume() da Activity foi chamado).

Abriga um Service que está vinculado à atividade que o usuário está

interagindo.

Abriga um Service que está sendo executado "em primeiro plano", o

serviço chamado é startForeground().

Abriga um Service que está executando um ciclo de vida de seus retornos

(onCreate(), onStart(), ou onDestroy()).

Abriga um BroadcastReceiver que está executando a sua onReceive().

Geralmente, apenas um processo de background existe em um determinado

momento. Eles são mortos apenas como um último recurso - se a memória é tão

Page 100: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 100

pouca que ele não pode continuar rodando. Geralmente, nesse ponto, o

dispositivo atingiu um estado de paginação de memória, então, matar alguns

processos de primeiro plano é necessário para manter a interface de usuário

sensível.

2. Processo Visível

Um processo que não tem nenhum componente de primeiro plano, mas ainda

pode afetar o que o usuário vê na tela. Um processo é considerado visível se

qualquer uma das seguintes condições forem verdadeiras:

Abriga uma Activity que não está em primeiro plano, mas ainda é visível

para o usuário (seu método onPause() foi chamado). Isso pode ocorrer,

por exemplo, se a atividade iniciou um plano de diálogo, que permite que

a atividade anterior possa ser vista por trás dele.

Abriga um Service que está vinculado a uma atividade (ou plano) visível.

Um processo visível é considerado extremamente importante e não vai ser morto

a menos que isso é necessário para manter todos os processos de primeiro plano

em execução.

3. Processo de serviço

Um processo que está executando um serviço que foi iniciado com o método

startService() e não se enquadra em nenhuma das duas categorias acima. Embora

os processos de serviço não estejam diretamente ligados a qualquer coisa que o

usuário vê, eles estão geralmente fazendo coisas que o usuário se preocupa

(como a reprodução de música no fundo ou baixando arquivo na rede), então o

sistema os mantém rodando, a menos que não haja memória suficiente para retê-

los, juntamente com o primeiro plano e processos visíveis.

4. Processo em segundo plano

Um processo que mantém uma atividade que não é visível para o usuário no

momento (o método onStop() da atividade foi chamado). Esses processos não

têm impacto direto sobre a experiência do usuário, e o sistema pode matá-los a

qualquer momento para recuperar a memória para primeiro plano, visibilidade,

ou processo de serviço. Normalmente, há muitos processos em execução no

Page 101: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 101

fundo, então eles são mantidos em uma LRU (menos recentemente usada), lista

para garantir que o processo com a atividade que mais recentemente foi visto

pelo usuário é o último a ser morto. Se uma atividade implementa métodos de

seu ciclo de vida corretamente, e salva o seu estado atual, matar o seu processo

não terá um efeito visível sobre a experiência do usuário, pois quando o usuário

navega de volta para a atividade, a atividade restaura todo o seu estado visível.

5. Processo vazio

Um processo que não possui todos os componentes do aplicativo ativos. A única

razão para manter esse tipo de processo vivo é para fins de armazenamento em

cache, para melhorar o tempo de inicialização da próxima vez que um

componente precisa ser executado. O sistema mata muitas vezes estes processos

a fim de equilibrar os recursos do sistema global entre os caches do processo e

os cachês do kernel subjacente.

Android enfileira um processo ao mais alto nível que pode, com base na importância do

componente ativo no processo. Por exemplo, se um processo hospeda um serviço e uma

atividade visível, o processo é classificado como um processo visível, e não um

processo de serviço.

Além disso, o ranking de um processo pode ser acrescido, pois outros processos são

dependentes dele - um processo que está servindo a outro processo nunca pode ser

menor classificado do que o processo que está servindo. Por exemplo, se um provedor

de conteúdo em um processo A está a servir um cliente no processo de B, ou se um

serviço em um processo A está ligado a um componente no processo de B, processo A é

sempre considerado pelo menos tão importante como o processo B.

Porque um processo executando um serviço é melhor classificado do que um processo

com atividades em segundo plano, uma atividade que inicia uma operação de longa

duração pode muito bem começar um serviço para essa operação, ao invés de

simplesmente criar uma thread de trabalhador - particularmente se a operação

provavelmente durará mais que a atividade. Por exemplo, uma atividade que carrega

uma foto em um site deve iniciar um serviço para realizar o upload, então o upload pode

continuar em segundo plano, mesmo se o usuário deixar a atividade. Usar um serviço

garante que a operação terá, pelo menos, prioridade "no processo do serviço",

Page 102: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 102

independentemente do que acontece com a atividade. Este é o mesmo motivo que os

receptores de broadcast devem contratar serviços ao invés de simplesmente colocar

operações demoradas em um thread.

Thread

Quando uma aplicação é lançada, o sistema cria um thread de execução para o

aplicativo, chamado de "main". Esta thread é muito importante, pois é responsável por

despachar os eventos para os apropriados widgets de interface de usuário, incluindo

eventos de desenho. É também o segmento em que sua aplicação interage com os

componentes da UI Toolkit Android (componentes da android.widget e pacotes

android.view). Como tal, o thread principal é chamado também às vezes de UI thread.

O sistema não cria um thread separado para cada instância de um componente. Todos os

componentes que são executados no mesmo processo são instanciados no thread de

interface do usuário e as chamadas do sistema para cada componente são despachadas a

partir desse thread. Conseqüentemente, os métodos que respondem às callbacks do

sistema (como onKeyDown() para relatar as ações do usuário ou um método de retorno

do ciclo de vida) sempre executam no thread da interface do usuário do processo.

Por exemplo, quando o usuário toca um botão na tela, seu thread UI do aplicativo

despacha o evento de toque para o widget, que por sua vez, define seu estado

pressionado e lança uma requisição inválida para a fila de eventos. O thread UI

desenfilera a requisição e notifica o widget que deve se redesenhar.

Quando o aplicativo executa um trabalho intensivo em resposta à interação do usuário,

este único thread pode produzir mal desempenho a menos que você implemente a sua

aplicação de forma adequada. Especificamente, se tudo está acontecendo no thread de

interface do usuário, realizando longas operações, tais como acesso à rede ou consultas

de banco de dados, irá bloquear a interface toda. Quando o thread está bloqueado,

nenhum evento pode ser enviado, incluindo eventos de desenho. Do ponto de vista do

usuário, o aplicativo parece travar. Pior ainda, se o segmento de interface do usuário

estiver bloqueado por mais de alguns segundos (cerca de 5 segundos atualmente) ao

usuário é apresentado o abominável "aplicativo não responde" (ANR). O usuário pode,

então, decidir abandonar a aplicação e desinstalá-la se está infeliz.

Page 103: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 103

Além disso, o Andoid UI Toolkit não é thread-safe. Então, você não deve manipular a

sua interface a partir de um thread de trabalho - você deve fazer toda a manipulação de

sua interface a partir de um thread UI. Assim, existem apenas duas regras para o modelo

de thread única no Android:

1. Não bloquear o thread de UI

2. Não acessar o Android UI Toolkit de fora do thread UI

Threads funcionais

Devido ao modelo de thread único acima descrito, é vital para a capacidade de resposta

da interface do usuário do seu aplicativo que você não bloqueie o thread. Se você tiver

de realizar operações que não são instantâneas, você deve certificar-se de fazê-las em

threads separados ("background" ou threads "worker").

Por exemplo, abaixo está um código para um usuário que faz o download de uma

imagem de um thread separado e é exibida em uma ImageView:

public void onClick(View v) {

new Thread(new Runnable() {

public void run() {

Bitmap b = loadImageFromNetwork("http://example.com/image.png");

mImageView.setImageBitmap(b);

}

}).start();

}

Em princípio, isso parece funcionar bem, porque cria uma nova thread para lidar com a

operação da rede. No entanto, ele viola a segunda regra do modelo de única thread: não

acessar o Android UI Toolkit de fora da UI-thread - esta amostra modifica o

ImageView do thread de trabalho em vez do thread UI. Isso pode resultar em um

comportamento indefinido e inesperado, que pode ser difícil e demorado para rastrear.

Para corrigir esse problema, o Android oferece várias maneiras para acessar a thread de

interface do usuário a partir de outros threads. Aqui está uma lista de métodos que

podem ajudar:

Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

Page 104: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 104

Por exemplo, você pode corrigir o código acima usando o método

View.post(Runnable):

public void onClick(View v) {

new Thread(new Runnable() {

public void run() {

final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");

mImageView.post(new Runnable() {

public void run() {

mImageView.setImageBitmap(bitmap);

}

});

}

}).start();

}

Agora, essa implementação é thread-safe: o funcionamento da rede é feito a partir de

um segmento separado, enquanto o ImageView é manipulado a partir do thread de

interface de usuário.

No entanto, como a complexidade da operação cresce, este tipo de código pode ser

complicado e difícil de manter. Para lidar com interações mais complexas com um

thread de trabalho, você pode considerar usar um Handler, para processar as mensagens

entregues a partir do thread. Talvez a melhor solução, porém, é estender a classe

AsyncTask, o que simplifica a execução de tarefas do thread de trabalho que precisam

interagir com a interface do usuário.

Usando AsyncTask

AsyncTask permite executar trabalho assíncrono em sua interface com o usuário. Ela

executa as operações de bloqueio em um segmento de trabalho e, em seguida, publica os

resultados no segmento de interface do usuário, sem que você precise lidar com tópicos

e/ou manipuladores de si mesmo.

Para usá-lo, você deve incorporar AsyncTask e implementar o método de retorno

doInBackground(), que é executado em um pool de threads de background. Para

atualizar sua interface, você deve implementar onPostExecute(), que fornece o resultado

da doInBackground() e é executado no thread da interface do usuário, assim você pode

escolher atualizar o UI. Você pode então executar a tarefa, chamando execute() do

thread UI.

Page 105: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 105

Por exemplo, você pode implementar o exemplo anterior usando AsyncTask desta

forma:

public void onClick(View v) {

new DownloadImageTask().execute("http://example.com/image.png");

}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {

/** The system calls this to perform work in a worker thread and

* delivers it the parameters given to AsyncTask.execute() */

protected Bitmap doInBackground(String... urls) {

return loadImageFromNetwork(urls[0]);

}

/** The system calls this to perform work in the UI thread and delivers

* the result from doInBackground() */

protected void onPostExecute(Bitmap result) {

mImageView.setImageBitmap(result);

}

}

Agora a interface do usuário é segura e o código é simples, porque separa o trabalho na

parte que deve ser feita em um thread de trabalho e a parte que deve ser feita no thread

de interface de usuário.

Você deve ler o AsyncTask de referência para um entendimento completo de como usar

essa classe, mas aqui está uma visão geral de como funciona:

Você pode especificar o tipo dos parâmetros, os valores de progresso e o valor

final da tarefa, usando genéricos

O método doInBackground() é executado automaticamente em um segmento de

trabalho

onPreExecute(), onPostExecute(), e onProgressUpdate() são invocados no thread

de interface do usuário

O valor retornado por doInBackground() é enviado para onPostExecute()

Você pode chamar publishProgress() em qualquer outro momento do

doInBackground() para executar onProgressUpdate() na thread da UI

Você pode cancelar a tarefa a qualquer hora, de qualquer thread

Atenção: Outro problema que se pode encontrar ao usar um segmento de trabalho é

reinícios inesperados na sua atividade devido a uma alteração de configuração em

tempo de execução (como quando o usuário muda a orientação da tela), que podem

Page 106: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 106

destruir seu segmento de trabalho. Para ver como você pode manter a sua tarefa

durante um desses reinícios e como fazer corretamente um cancelamento da tarefa

quando a atividade for destruída, veja o código fonte para o aplicativo de amostra da

Prateleira.

Métodos de Thread-safe

Em algumas situações, os métodos a implementar poderiam ser chamados de mais de

um thread e, portanto, deve ser escrito para ser thread-safe.

Isto é principalmente verdadeiro para métodos que podem ser chamados remotamente,

como métodos de um serviço vinculado. Quando uma chamada de um método

implementado em um IBinder se origina no mesmo processo em que o IBinder está

executando, o método é executado na thread de quem chamou. No entanto, quando a

chamada é originada em outro processo, o método é executado em um thread escolhido

de um pool de threads que o sistema mantém no mesmo processo que o IBinder (não é

executado no thread da interface do usuário do processo). Por exemplo, considerando

que um método onBind() do serviço seria chamado do de interface do usuário do

processo de serviço, métodos implementados no objeto que onBind() retorna (por

exemplo, uma subclasse que implementa métodos RPC) seriam chamados threads no

pool. Como um serviço pode ter mais de um cliente, mais de uma pool de threads pode

envolver o mesmo método IBinder, ao mesmo tempo. Métodos IBinder devem,

portanto, ser implementadas para ser thread-safe.

Da mesma forma, um provedor de conteúdo pode receber dados de pedidos que se

originam em outros processos. Embora o ContentResolver e ContentProvider ocultam

os detalhes de como a comunicação entre processos é gerenciada, métodos

ContentProvider que respondem a esses pedidos - os métodos query(), insert(), delete(),

update(), e getType() são chamados a partir de um pool de threads no processo de

provedor de conteúdo, e não o thread UI para o processo. Como esses métodos podem

ser chamados a partir de qualquer número de threads ao mesmo tempo, eles também

devem ser implementados para ser thread-safe.

Comunicação entre processos

Android oferece um mecanismo para comunicação entre processos (IPC) através de

chamadas de procedimento remoto (RPCs), em que um método é chamado por uma

Page 107: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 107

atividade ou um componente de outro aplicativo, mas executado remotamente (em outro

processo), com qualquer resultado retornado de volta para o chamador. Isto implica a

decomposição de uma chamada de método e de seus dados a um nível que o sistema

operacional possa entender, transmiti-lo a partir do processo do processo local e espaço

de endereçamento para o processo remoto e espaço de endereço, em seguida, remontar e

reencenar a chamada. Os valores de retorno são transmitidos na direção oposta.

Android fornece todo o código para executar essas operações IPC, para que você possa

se concentrar na definição e implementação da interface de programação do RPC.

Para executar o IPC, o aplicativo deve ligar para um serviço, utilizando bindService().

Page 108: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 108

Interface de usuário

Em um aplicativo do Android, a interface do usuário é construída usando objetos View

e ViewGroup. Existem muitos tipos de views e view groups, cada um dos quais é um

descendente da classe View.

Objetos view são as unidades básicas de expressão da interface do usuário na

plataforma Android. A classe View serve como base para as subclasses chamadas de

"widgets", que oferecem completa implementação de objetos de interface do usuário,

como campos de texto e botões. A classe ViewGroup serve como base para as

subclasses chamadas de "layouts", que oferecem diferentes tipos de layout de

arquitetura, como a linear, tabular e relativa.

Um objeto View é uma estrutura de dados, cujas propriedades armazenam os

parâmetros de layout e de conteúdo para uma determinada área retangular da tela. Um

objeto View lida com sua própria medida, layout, desenho, mudança de foco, scrolling e

interações telcla/gesto para a área retangular da tela na qual ele reside. Como um objeto

na interface do usuário, uma View é também um ponto de interação para o usuário e o

receptor de eventos de interação.

Hierarquia de view

Sobre a plataforma Android, você define uma interface de atividades, usando uma

hierarquia de view e nós ViewGroup, como mostrado no diagrama abaixo. Esta árvore

de hierarquia pode ser tão simples ou complexa como você precisa que ela seja, e você

pode construí-la usando o conjunto do Android de widgets e layouts pré-definidos, ou

com views customizados que você mesmo cria.

Page 109: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 109

A fim de fixar a árvore de hierarquia de views à tela para renderização, sua atividade

deve chamar o método setContentView() e passar uma referência ao objeto nó raiz. O

sistema Android recebe esta referência e usa-o para invalidar, medir e desenhar a

árvore. O nó raiz da hierarquia requisita que seus nós filhos desenhem a si próprios - por

sua vez, cada nó do ViewGroup é responsável por chamar cada um de seus views filhos

para desenhar a si mesmos. Os filhos podem solicitar um tamanho e localização com os

pais, mas o objeto-mãe tem a decisão final sobre onde e qual o tamanho que cada

criança pode ter. Android analisa os elementos do layout em ordem (a partir do topo da

árvore de hierarquia), instanciando os Views e adicionando-os aos seus pais. Porque

estes são desenhados em ordem, se há elementos que se sobrepõem posições, o último a

ser elaborado vai ficar em cima dos outros previamente elaborados para aquele espaço.

Como o Android desenha views

Quando uma atividade recebe o foco, ela será convidada a desenhar o layout. O

framework Android vai lidar com o processo de elaboração, mas a atividade deve

apresentar o nó raiz da sua hierarquia de layout.

Desenho começa com o nó raiz do layout. É solicitado para medir e desenhar a árvore

de layout. Desenho é tratado pelo pé da árvore e renderização de cada vista que cruza a

região inválida. Por sua vez, cada grupo View é responsável por solicitar a cada um dos

seus filhos a ser desenhado (com o método draw()) e cada View é responsável pelo

desenho de si. Como a árvore é percorrida em ordem, isso significa que os pais serão

desenhados antes do que seus filhos, com irmãos elaborados na ordem em que aparecem

na árvore.

Page 110: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 110

Desenhar o layout é um processo de duas passagens: a passagem de medidas e uma

passagem de layout. A passagem de medição é implementada com measure(int, int) e é

uma passagem top-down da árvore. Cada view empurra as especificações de dimensão

para baixo na árvore durante a recursividade. No final da passagem de medidas, cada

View tem armazenado suas medidas. A segunda fase acontece no layout(int, int, int, int)

e também é top-down. Durante o passar, cada pai é responsável pelo posicionamento de

todos os seus filhos usando os tamanhos computados na passagem de medidas.

Durante um método de retorno measure() da view, os valores getMeasuredWidth() e

getMeasuredHeight() devem ser definidos, juntamente com as de todos os descendentes

da view. Os valores de medição de largura e altura da view devem respeitar as restrições

impostas pelos pais da view. Isso garante que, no final da passagem de medidas, todos

os pais aceitam todas as medições de seus filhos. Um pai view pode chamar measure()

mais de uma vez sobre seus filhos. Por exemplo, os pais podem medir cada filho uma

vez com dimensões não especificadas para descobrir o quão grande eles querem ser,

então chama measure() neles novamente com números reais, se a soma dos tamanhos

dos filhos irrestrita é muito grande ou muito pequena (Ou seja, se os filhos não

concordam entre si a respeito de quanto espaço cada um ganha, o pai vai intervir e

estabelecer as regras na segunda passagem).

A passagem de medidas utiliza duas classes para

comunicar dimensões. A classe

View.MeasureSpec é usada por views para

contar aos pais como eles querem ser medidos e

posicionados. A classe base LayoutParams

apenas descreve quão grandes as views querem

ser para a largura e altura. Para cada dimensão, pode especificar um dos seguintes:

um número exato

FILL_PARENT, o que significa que o view quer ser tão grande quanto

seu pai (menos padding)

WRAP_CONTENT, o que significa que o view quer ser grande o

suficiente para colocar o seu conteúdo (mais padding).

Para dar início a um esquema, chama-

se requestLayout(). Este método

normalmente é chamado por uma view

sobre si mesma quando se acredita que

é possível não caber mais dentro de

seus limites atuais.

Page 111: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 111

Há subclasses de LayoutParams para diferentes subclasses de ViewGroup. Por exemplo,

RelativeLayout tem a sua própria subclasse de LayoutParams, que inclui a capacidade

de centralizar os filhos horizontalmente e verticalmente.

MeasureSpecs são usados para empurrar os requisitos para baixo da árvore de pai para

filho. Um MeasureSpec pode estar em um dos três modos:

NÃO ESPECIFICADO: Isto é usado por um dos pais para determinar a

dimensão desejada de um filho View. Por exemplo, um LinearLayout

pode chamar measure() em seu filho com a altura indefinida e uma

largura de exatamente 240.

EXATAMENTE: Este é usado pelos pais para impor um tamanho exato

sobre a criança. A criança deve usar esse tamanho, e garante que todos os

seus descendentes se encaixem dentro desse tamanho.

NO MÁXIMO: Este é usado pelos pais para impor um tamanho máximo

para a criança. A criança deve garantir que ele e todos os seus

descendentes vão caber dentro deste tamanho.

Layout

A maneira mais comum para definir o seu layout e expressar a hierarquia de view é com

um arquivo de layout XML. XML oferece uma estrutura legível para o layout, muito

parecido com HTML. Cada elemento em XML é ou um View ou ViewGroup (ou

descendentes dos mesmos). Objetos view são folhas da árvore, objetos ViewGroup são

ramos da árvore.

O nome de um elemento XML é respectivo para a classe Java que representa. Assim,

um elemento <TextView> cria um na sua interface do usuário, e um elemento

<LinearLayout> cria um ViewGroup. Quando você carregar um recurso de layout, o

sistema Android inicializa esses objetos em tempo de execução, correspondentes aos

elementos em seu layout.

Por exemplo, um layout simples vertical, com um text view e um botão parece com

este:

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

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

android:layout_width="fill_parent"

Page 112: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 112

android:layout_height="fill_parent"

android:orientation="vertical" >

<TextView android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello, I am a TextView" />

<Button android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello, I am a Button" />

</LinearLayout>

Observe que o elemento LinearLayout contém o TextView e o Botão. Você pode

aninhar outro LinearLayout (ou outro tipo de grupo de exibição) aqui dentro, para

alongar a hierarquia de view e criar um layout mais complexo.

Dica: Você também pode desenhar View e ViewGroups em código Java, utilizando

métodos para inserir dinamicamente novos objetos View e ViewGroup.

Há uma variedade de maneiras em que você pode formatar os seus view. Usando mais

tipos diferentes de grupos de exibição, você pode estruturar views herdados e view

groups em um número infinito de maneiras. Alguns view groups pré-definidos

oferecidas pelo Android (chamando layouts) incluem LinearLayout, RelativeLayout,

TableLayout, GridLayout e outros. Cada um oferece um conjunto exclusivo de

parâmetros de layout que são usados para definir as posições das views herdadas e a

estrutura de layout.

To learn about some of the different kinds of view groups used for a layout, read . Para

saber mais sobre alguns dos diferentes tipos de grupos de vista usado para um layout,

leia os objetos de layout comum .

Widgets

Um widget é um objeto View que serve como uma interface de interação com o usuário.

Android fornece um conjunto de widgets plenamente implementadas, como botões,

caixas de seleção, e os campos de entrada de texto, assim você pode construir

rapidamente sua interface do usuário. Alguns widgets fornecidos pelo Android são mais

complexos, como um selecionador de data, um relógio, e controles de zoom. Mas você

não está limitado aos tipos de elementos fornecidos pela plataforma Android. Se você

quiser fazer algo mais personalizado e criar seus próprios elementos, pode, através da

Page 113: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 113

definição de seu objeto View próprio ou através da extensão e combinação de elementos

existentes.

Page 114: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 114

Eventos UI

Uma vez que você adicionou alguns views/widgets para a interface do usuário, você

provavelmente quer saber sobre a interação do usuário com eles, para que você possa

executar ações. Para ser informado de eventos de interface, você precisa fazer uma de

duas coisas:

Definir um receptor de evento e registrá-lo com a view. Muitas vezes, é assim

que você vai receber os eventos. A classe View contém uma coleção de

interfaces aninhadas nomeadas <something> Listener, cada um com um método

de retorno de chamada On <something>(). Por exemplo, View.OnClickListener

(para lidar com os "cliques" na View), View.OnTouchListener (para lidar com

eventos de tela de toque em uma exibição), e View.OnKeyListener (para lidar

com teclas pressionadas no dispositivo dentro de uma View). Então se você quer

sua View para ser notificado quando ela é "clicada" (como quando um botão é

selecionado), implemente OnClickListener e defina o seu método de retorno

onClick() (onde você executar a ação após o clique), e registra-o para a View

com setOnClickListener().

Substituir um método de retorno existente para a View. Isto é o que você

deve fazer quando você implementou sua própria classe View e quer receber

eventos específicos que ocorrem dentro dele. Exemplificando eventos você pode

manipular inclusão quando a tela é tocada (onTouchEvent()), quando o trackball

é movido (onTrackballEvent()), ou quando uma tecla no dispositivo é

pressionada (onKeyDown()). Isso permite que você defina o comportamento

padrão para cada evento dentro da sua View personalizada e determinar se o

evento deve ser transmitido para alguma outra View filho. Novamente, essas são

chamadas de retorno para a classe View, assim, sua única chance de defini-los é

quando você cria um componente personalizado.

Menus

Os menus de aplicativos são outra parte importante da interface do usuário de um

aplicativo. Menus oferecem uma interface confiável, que revela funções de aplicativos e

configurações. O menu de aplicativos mais comuns é revelado, pressionando a tecla

MENU no dispositivo. No entanto, você também pode adicionar menus de contexto,

Page 115: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 115

que podem ser revelados quando o usuário pressiona e mantém pressionado em um

item.

Os menus também são estruturados usando uma hierarquia View, mas você não define

essa estrutura por si mesmo. Em vez disso, você define métodos de retorno como

onCreateOptionsMenu() ou onCreateContextMenu() para a sua atividade, e declara os

itens que você deseja incluir em seu menu. Em momento oportuno, o Android irá criar

automaticamente a necessária hierarquia View para o menu e desenhar cada um dos

seus itens de menu na mesma.

Menus também lidam com seus próprios eventos, por isso não há necessidade de

registrar eventos listeners sobre os itens do seu menu. Quando um item no seu menu é

selecionado, o método onOptionsItemSelected() ou onContextItemSelected() será

chamado pelo framework.

E, assim como o layout do aplicativo, você tem a opção de declarar os itens para seu

menu em um arquivo XML.

Tópicos Avançados

Uma vez que você aprendeu os fundamentos da criação de uma interface, você pode

explorar algumas características avançadas para a criação de uma interface de aplicação

mais complexa.

Adaptadores

Às vezes você deseja preencher um view group com algumas informações que não

podem ser codificados, em vez disso, você quer associar a sua view a uma fonte externa

de dados. Para fazer isso, use um AdapterView como seu grupo de visão e cada filho

view é inicializado e preenchido com os dados do adaptador.

O objeto AdapterView é uma implementação do ViewGroup que determina as views

dos filhos com base em um determinado adaptador. O adaptador funciona como um

mensageiro entre a fonte de dados (talvez um array de strings externa) e os

AdapterView, que exibe. Existem várias implementações da classe Adapter, para tarefas

específicas, como a CursorAdapter para leitura de dados banco de dados de um cursor

ou um ArrayAdapter para a leitura de uma matriz arbitrária.

Page 116: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 116

Estilos e Temas

Talvez você não esteja satisfeito com a aparência dos widgets padrão. Para revê-los,

você pode criar alguns dos seus próprios estilos e temas.

Um estilo é um conjunto de um ou mais atributos de formatação que você pode

aplicar como uma unidade de elementos individuais em seu layout. Por exemplo,

você pode definir um estilo que especifica um determinado tamanho de texto e

cor, em seguida, aplicá-lo apenas a elementos específicos.

Um tema é um conjunto de um ou mais atributos de formatação que você pode

aplicar como uma unidade para todas as atividades em uma aplicação, ou apenas

uma atividade única. Por exemplo, você pode definir um tema que define as

cores específicas para a moldura da janela e o fundo do painel, e define o

tamanho dos textos e cores dos menus. Este tema pode ser aplicado a atividades

específicas ou todo o aplicativo.

Estilos e temas são recursos. Android oferece alguns recursos de estilo padrão e tema

que você pode usar, ou você pode declarar o seu próprio estilo customizado e recursos

de tema.

Page 117: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 117

Declarando Layout

Seu layout é a arquitetura para interface de usuário em uma atividade. Ela define a

estrutura de layout e possui todos os elementos que aparecem para o usuário. Você pode

declarar o seu layout de duas maneiras:

Declare elementos UI em XML - Android fornece um vocabulário XML

simples que corresponde às classes View e subclasses, tais como widgets e

layouts.

Instanciar elementos de layout em tempo de execução - O aplicativo pode

criar objetos de View e ViewGroup (e manipular suas propriedades) de forma

programaticamente.

O framework Android lhe dá a flexibilidade para usar um ou ambos os métodos para

declarar e gerenciar a interface do usuário do seu aplicativo. Por exemplo, você poderia

declarar layouts padrão de seu aplicativo em XML, incluindo os elementos da tela que

aparecerão nela e suas propriedades. Você pode então adicionar o código em seu

aplicativo que iria alterar o estado dos objetos da tela, incluindo aqueles declarados em

XML, em tempo de execução.

A vantagem de declarar a sua

interface em XML é que ela permite

melhor separar a apresentação da sua

aplicação do código que controla o

seu comportamento. Suas descrições

de interface do usuário são externas à

sua aplicação, o que significa que

você pode modificá-lo ou adaptá-lo

sem ter que modificar seu código-

fonte e recompilar. Por exemplo, você

pode criar layouts XML para

orientações de tela diferentes,

diferentes tamanhos de tela do

dispositivo, e línguas diferentes.

Além disso, declarar o layout em XML torna mais fácil visualizar a estrutura de sua

O plugin ADT para Eclipse oferece um

preview do seu layout XML - com o

arquivo XML aberto, selecione a guia

Layout.

Você também deve tentar a ferramenta de

Hierarquia Viewer para depuração de

layouts – ela revela propriedade de layout,

desenha wireframes com indicadores

padding/margin e view completas

renderizadas enquanto você depurar no

emulador ou dispositivo.

A ferramenta layoutopt permite analisar

rapidamente os seus layouts e hierarquias

de ineficiências ou outros problemas.

Page 118: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 118

interface do usuário, por isso é mais fácil depurar problemas. Como tal, este documento

centra-se em ensiná-lo a declarar o seu layout em XML. Se você estiver interessado em

instanciar objetos view em tempo de execução, consulte as classes ViewGroup e View.

Em geral, o vocabulário XML para a declaração de elementos da interface segue de

perto a estrutura e nomenclatura das classes e métodos, onde os nomes dos elementos

correspondem aos nomes de classes e nomes de atributos correspondem aos métodos.

De fato, a correspondência é muitas vezes tão direta que você pode adivinhar o atributo

XML corresponde a um método de classe, ou adivinhar o que a classe corresponde a um

determinado elemento XML. No entanto, note que nem todo vocabulário é idêntico. Em

alguns casos, existem ligeiras diferenças de nomenclatura. Por exemplo, o elemento

EditText tem um atributo text que corresponde a EditText.setText() .

Dica: Saiba mais sobre os diferentes tipos de layout em Common Layout Objects.

Escreve o XML

Usando o vocabulário do Android XML, você pode rapidamente criar layouts de

interface do usuário e os elementos de tela que eles contêm, da mesma forma que você

cria páginas web em HTML - com uma série de elementos aninhados.

Cada arquivo de layout deve conter exatamente um elemento de raiz, que deve ser um

objeto de view ou ViewGroup. Depois de definir o elemento raiz, você pode adicionar

objetos de layout adicionais ou widgets como elementos filho para criar gradualmente

uma hierarquia de exibição que define o layout. Por exemplo, aqui está um esquema

XML que usa um LinearLayout vertical que contém um TextView e um Button :

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

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

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical" >

<TextView android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello, I am a TextView" />

<Button android:id="@+id/button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello, I am a Button" />

</LinearLayout>

Page 119: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 119

Depois de ter declarado o seu layout em XML, salve o arquivo com a extensão .xml em

seu projeto Android no diretório res/layout/, assim ele vai compilar corretamente.

Carregar os recursos XML

Quando você compilar sua aplicação, cada arquivo de layout XML é compilado em um

View de recurso. Você deve carregar o recurso de layout de sua aplicação, em sua

implementação de retorno Activity.onCreate(). Faça isso chamando setContentView(),

passando a referência ao seu recurso de layout na forma de: R.layout. layout_file_name.

Por exemplo, se o seu layout XML é salvo como main_layout.xml, você deve carregá-lo

para a sua atividade da seguinte forma:

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main_layout);

}

O método de retorno onCreate() em sua atividade é chamado pelo framework Android

quando sua atividade é lançada.

Atributos

Cada objeto View e ViewGroup apóia a sua própria variedade de atributos XML.

Alguns atributos são específicos para um objeto View (por exemplo, TextView apóia o

atributo textSize), mas esses atributos também são herdadas por qualquer objetos View

que podem estender esta classe. Alguns são comuns a todos objetos View, porque eles

são herdados da classe View raiz (como o atributo id). E, outros atributos são

considerados "parâmetros de layout", que são atributos que descrevem determinadas

orientações de layout do objeto View.

ID

Qualquer objeto View pode ter um ID de inteiro associado a ele, para identificar o View

dentro da árvore. Quando o aplicativo é compilado, essa identificação é referenciada

como um inteiro, mas a identificação é normalmente atribuída no layout do arquivo

XML como uma string, no atributo id. Este é um atributo XML comum a todos os

objetos View (definido pela classe View) e você vai usá-lo muitas vezes. A sintaxe para

uma identificação, dentro de uma tag XML é:

Page 120: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 120

android:id="@+id/my_button"

O símbolo de arroba (@) no início da string indica que o analisador XML deve analisar

e ampliar o resto da seqüência de identificação e identificá-lo como um recurso de

identificação. O sinal de mais (+) significa que este é um novo nome de recurso que

deve ser criado e adicionado aos nossos recursos (no arquivo R.java). Há uma série de

recursos de outro tipo de identificação que são oferecidos pela estrutura do Android. Ao

fazer referência a uma identificação de recurso Android, você não precisa do sinal de

mais, mas deve adicionar o namespace de pacote android, assim:

android:id="@android:id/empty"

Com o namespace de pacote android no lugar, agora estamos fazendo referência a uma

ID da classe de recursos android.R, ao invés da classe de recursos locais.

A fim de criar views e referenciá-los a partir da aplicação, um padrão comum é:

1. Definir uma view/widget no arquivo de layout e atribuir um ID único:

<Button android:id="@+id/my_button"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/my_button_text"/>

2. Em seguida, crie uma instância do objeto view e capture-o a partir do layout

(geralmente no método onCreate()):

Button myButton = (Button) findViewById(R.id.my_button);

Definir os IDs dos objetos view é importante ao criar um RelativeLayout. Em um

RelativeLayout, views irmãos podem definir a sua disposição em relação a outro view

irmão, que é referenciada pela ID exclusiva.

Uma identificação não precisa ser única para toda a árvore, mas deve ser exclusiva

dentro da parte da árvore que você está procurando (o que pode muitas vezes ser a

árvore inteira, por isso é melhor ser completamente original quando possível).

Parâmetros de layout

Atributos de layout XML chamados layout_something definem os parâmetros de layout

para a view que sejam adequadas para a ViewGroup em que ele reside.

Page 121: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 121

Cada classe ViewGroup implementa uma classe aninhada que estende

ViewGroup.LayoutParams. Esta subclasse contém os tipos de propriedades que definem

o tamanho e a posição de cada view filha, conforme apropriado para o view group.

Como você pode ver na figura 1, o grupo de exibição pai define os parâmetros de layout

para cada view filho (incluindo o view group dos filhos).

Figura 1. Visualização de uma hierarquia de views com os parâmetros de layout associado a cada

exibição.

Note que cada subclasse LayoutParams tem sua própria sintaxe para definir valores.

Cada elemento filho deve definir LayoutParams que são apropriadas para seu pai,

embora possa também definir LayoutParams diferentes para seus filhos.

Todos os view groups incluem uma largura e altura (layout_width e layout_height), e

cada view é necessária para defini-los. Muitos LayoutParams também incluem margens

opcional e fronteiras.

Você pode especificar a largura e altura com medidas exatas, embora você

provavelmente não irá querer fazer isso com freqüência. Mais freqüentemente, você vai

usar uma dessas constantes para definir a largura ou altura:

wrap_content diz a seu view a arranjar a si próprio para as dimensões exigidas

pelo seu conteúdo.

Page 122: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 122

fill_parent (rebatizada match_parent na API Nível 8) diz à sua view para se

tornar tão grande quanto o view group dos pais irá permitir.

Em geral, especificar uma largura de layout e altura, utilizando unidades absolutas, tais

como pixels não é recomendada. Em vez disso, por meio de medidas como a densidade

relativa independente unidades de pixel (DP), wrap_content ou fill_parent, é uma

abordagem melhor, porque ajuda a garantir que seu aplicativo irá exibir corretamente

através de uma variedade de tamanhos de tela do dispositivo. Os tipos de medição

aceitos são definidos no documento Recursos Disponíveis.

Posição de Layout

A geometria de uma view é a de um retângulo. Uma view tem uma localização,

expresso como um par de coordenadas esquerda e superior, e duas dimensões, expressa

em uma largura e uma altura. A unidade para a localização e as dimensões é o pixel.

É possível recuperar o local de uma visão chamando os métodos getLeft() e getTop().

O primeiro retorna a esquerda, ou X, de coordenadas do retângulo que representa o

view. O segundo retorna o topo, ou Y, coordenadas do retângulo que representa o view.

Estes dois métodos devolvem o local da view em relação ao seu pai. Por exemplo,

quando o Getleft() retorna 20, significa que o ponto de vista está localizado a 20 pixels

para a direita da borda esquerda da sua controladora direta.

Além disso, vários métodos de conveniência são oferecidos para evitar cálculos

desnecessários, ou seja, getRight() e getBottom(). Esses métodos retornam as

coordenadas das bordas direita e inferior do retângulo que representa o view. Por

exemplo, chamando getRight() é semelhante ao seguinte cálculo: getLeft() +

getWidth().

Tamanho, padding e margin

O tamanho de uma view é expressa com uma largura e uma altura. Uma visão realmente

possue dois pares de valores de largura e altura.

O primeiro par é conhecido como largura medida e altura medida. Essas dimensões

definem quão grande quer ser em vista de seu pai. As dimensões medidas podem ser

obtidas chamando getMeasuredWidth() e getMeasuredHeight().

Page 123: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 123

O segundo par é conhecido simplesmente como largura e altura, ou às vezes a largura

de desenho e altura de desenho. Essas dimensões definem o tamanho real do view sobre

tela, em tempo de desenho e depois de layout. Esses valores podem ser, mas não tem

que ser diferentes da largura e altura medidos. A largura e altura podem ser obtidas

chamando getWidth() e getHeight().

Para medir as suas dimensões, uma view leva em conta o seu preenchimento. O

preenchimento é expresso em pixels para as partes esquerda, superior, direita e inferior

do view. Padding pode ser utilizado para compensar o conteúdo da visão de uma

determinada quantidade de pixels. Por exemplo, um padding-left de 2 vai empurrar o

conteúdo do view por 2 pixels à direita da margem esquerda. Padding pode ser definido

usando o método setPadding(int, int, int, int) e consultado chamando getPaddingLeft(),

getPaddingTop(), getPaddingRight() e getPaddingBottom().

Apesar de uma exibição poder definir um padding, isso não prevê qualquer apoio para

as margens. No entanto, view groups prestam esse apoio.

Page 124: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 124

Criando Menus

Os menus são uma parte importante da interface de uma atividade do usuário, que

fornecem aos usuários uma forma familiar para executar ações. Android oferece um

quadro simples para você adicionar menus padrão para seu aplicativo.

Existem três tipos de menus de aplicação:

Menu de Opções

A coleção de itens do menu principal para uma atividade, que aparece quando o

usuário toca no botão MENU. Quando o aplicativo está rodando o Android 3.0

ou posterior, você pode fornecer acesso rápido para selecionar itens de menu,

colocando-os diretamente na barra de ação, como "itens de ação."

Menu de Contexto

Uma lista de itens de menu flutuante que aparece quando o usuário toca e tem

uma visão que está registrada para fornecer um menu de contexto.

Submenu

Uma lista de itens de menu flutuante que aparece quando o usuário toca um item

de menu que contém um menu aninhado.

Criando um recurso de menu

Em vez de instanciar um Menu no seu código do aplicativo, você deve definir um menu

e todos os seus itens em um XML menu resource, em seguida, inflar o recurso de menu

(carregá-lo como um objeto programável) no seu código do aplicativo. Utilizando um

recurso de menu para definir o seu menu é uma boa prática, pois separa o conteúdo do

menu de seu código do aplicativo. É também mais fácil de visualizar a estrutura e o

conteúdo de um menu em XML.

Para criar um recurso de menu, crie um arquivo XML dentro de seu projeto do diretório

res/menu/ e crie o menu com os seguintes elementos:

Page 125: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 125

<menu>

Define um Menu, que é um recipiente para itens de menu. Um elemento

<menu> deve ser o nó raiz para o arquivo e pode conter um ou mais <item> e

elementos <group>.

<item>

Cria um MenuItem, o que representa um único item em um menu. Este elemento

pode conter um elemento <menu> aninhado, a fim de criar um submenu.

<group>

Um opcional, recipiente invisível para elementos <item>. Ele permite que você

categorize itens de menu para que compartilhe as propriedades tais como estado

ativo e visibilidade.

Aqui está um exemplo de menu chamado game_menu.xml:

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

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

<item android:id="@+id/new_game"

android:icon="@drawable/ic_new_game"

android:title="@string/new_game" />

<item android:id="@+id/help"

android:icon="@drawable/ic_help"

android:title="@string/help" />

</menu>

Este exemplo define um menu com dois itens. Cada item inclui os atributos:

android:id

A identificação do recurso que é único para o item, que permite que o aplicativo

possa reconhecer o item quando o usuário seleciona-o.

android:icon

Uma referência para um desenho para usar como ícone do item.

android:title

Uma referência a uma string para usar como título do item.

Page 126: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 126

Há muito mais atributos que você pode incluir em um <item>, incluindo alguns que

especificam como o item pode aparecer na barra de ação.

Inflar um recurso de menu

A partir de seu código de aplicativo, você pode inflar um recurso de menu (converter o

recurso de XML em um objeto programável) utilizando MenuInflater.inflate(). Por

exemplo, o seguinte código infla o arquivo game_menu.xml definido acima, durante o

método de retorno onCreateOptionsMenu(), para usar o menu como a atividade do

Menu de Opções:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

MenuInflater inflater = getMenuInflater();

inflater.inflate(R.menu.game_menu, menu);

return true;

}

O método getMenuInflater() retorna uma MenuInflater para a atividade. Com este

objetivo, você pode chamar inflate(),que infla um recurso de menu em um objeto Menu.

Neste exemplo, o recurso de menu definido por game_menu.xml é inflado no Menu que

foi passado para onCreateOptionsMenu().

Criando um Menu de Opções

Quando executado em um dispositivo com Android

2.3 e inferior, o Menu de Opções aparece na parte

inferior da tela, como mostrado na figura 1. Quando

aberto, a primeira porção visível do Menu de Opções é

o menu do ícone. Ele mantém os seis primeiros itens

do menu. Se você adicionar mais de seis itens do

Menu de Opções, o Android coloca o sexto item e

aqueles após no menu de estouro, que o usuário pode

abrir tocando no item "More" do menu.

Figura 1. Screenshot do menu de opções no browser.

Page 127: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 127

No Android 3.0 e superior, os itens do Menu de Opções são colocados na barra de ação,

que aparece no topo da atividade no lugar da barra de título tradicional. Por padrão

todos os itens do Menu de Opções são colocados no menu de estouro, que o usuário

pode abrir ao tocar no ícone do menu no lado direito da barra de ação. No entanto, você

pode colocar itens de menu diretamente na Barra de ação como "itens de ação", para

acesso instantâneo, como mostrado na figura 2.

Figura 2. Imagem da barra de ação na aplicação de e-mail, com dois itens de ação do Menu de Opções,

além do menu de estouro.

Quando o sistema Android cria o Menu de Opções, pela primeira vez, ele chama o

método onCreateOptionsMenu() da sua atividade. Substituir este método em sua

atividade e preencher o Menu que é passado para o método, Menu inflando um recurso

de menu como descrito acima em Inflating a Menu Resource. Por exemplo:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

MenuInflater inflater = getMenuInflater();

inflater.inflate(R.menu.game_menu, menu);

return true;

}

Você também pode preencher o menu em código, usando add() para adicionar itens ao

Menu.

Nota: No Android 2.3 e inferiores, o sistema chama onCreateOptionsMenu() para criar

o menu de opções quando o usuário abre pela primeira vez, mas no Android 3.0 e

superior, o sistema cria assim que a atividade é criada, a fim e preencher a barra de

ação.

Respondendo à ação do usuário

Quando o usuário seleciona um item de menu do Menu de Opções (incluindo itens de

ação na barra de ação), o sistema chama o método onOptionsItemSelected() da sua

atividade. Este método passa o MenuItem que o usuário selecionou. Você pode

identificar o item de menu chamando getItemId(), que retorna o ID único para o item de

menu (definido pelo atributo android:id no recurso de menu ou com um número inteiro

Page 128: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 128

dado ao método add()). Você pode combinar esta identificação contra itens do menu

conhecidos e executar a ação apropriada. Por exemplo:

@Override

public boolean onOptionsItemSelected(MenuItem item) {

// Handle item selection

switch (item.getItemId()) {

case R.id.new_game:

newGame();

return true;

case R.id.help:

showHelp();

return true;

default:

return super.onOptionsItemSelected(item);

}

}

Neste exemplo, getItemId() consulta o ID do item de menu selecionado e o comando

switch compara o ID contra os IDs de recursos que foram atribuídos aos itens do menu

no recurso XML. Quando um switch case manipula o item de menu, ela retorna true

para indicar que a seleção de item foi tratada. Caso contrário, a declaração padrão passa

o item de menu para a super classe, no caso dele poder lidar com o item selecionado.

(Se você diretamente estendeu a classe Activity, então, a super classe retorna false, mas

é uma boa prática passar itens de menu não tratados para a classe super em vez de

diretamente retornar false).

Além disso, o Android 3.0 adiciona a capacidade de definir o comportamento de clicar

em um item de menu no menu de recursos XML, usando o atributo android:onClick.

Então você não precisa implementar onOptionsItemSelected(). Usando o atributo

android:onClick, você pode especificar um método a ser chamado quando o usuário

seleciona o item de menu. Sua atividade deve, então, aplicar o método especificado no

android:onClick para que ele aceite um único MenuItem de parâmetro, quando o

sistema chama este método, ele passa o item de menu selecionado.

Dica: Se seu aplicativo contém atividades múltiplas e algumas delas oferecem o mesmo

Menu de Opções, considere a criação de uma atividade que não executa nada, exceto

os métodos onCreateOptionsMenu() e onOptionsItemSelected(). Em seguida, estenda

esta classe para cada atividade que deve compartilhar o mesmo menu de opções. Dessa

forma, você tem que gerenciar apenas um conjunto de código para manipular ações de

menu e cada classe descendente herda o comportamento do menu.

Page 129: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 129

Se você quiser adicionar itens de menu para um dos descendentes de suas atividades,

sobreponha onCreateOptionsMenu() nessa atividade. Chame

super.onCreateOptionsMenu(menu) para que os itens do menu inicial sejam criados,

em seguida, adicione novos itens de menu com menu.add(). Você também pode

substituir o comportamento da super classe para os itens de menu individuais.

Alterando os itens de menu em tempo de execução

Uma vez que a atividade é criada, o método onCreateOptionsMenu() é chamado apenas

uma vez, como descrito acima. O sistema mantém e re-utiliza o Menu que você define

neste método até que sua atividade seja destruída. Se você quiser mudar o menu de

opções a qualquer momento após ter sido criado pela primeira vez, você deve substituir

o método onPrepareOptionsMenu(). Isto lhe passa o objeto Menu como ele existe

atualmente. Isso é útil se você quiser remover, adicionar, desativar ou ativar itens de

menu, dependendo do estado atual de sua aplicação.

No Android 2.3 e inferiores, o sistema chama onPrepareOptionsMenu() cada vez que o

usuário abre o Menu de Opções.

No Android 3.0 e superior, você deve chamar invalidateOptionsMenu() quando você

desejar atualizar o menu, porque o menu está sempre aberto. O sistema irá então chamar

onPrepareOptionsMenu(), assim você pode atualizar os itens de menu.

Nota: Você nunca deve alterar itens no menu de opções com base no View atualmente

em foco. Quando estiver no modo de toque (quando o usuário não estiver usando um

trackball ou d-pad), views podem não ter foco, então você nunca deve usar o foco como

base para a modificação de itens do Menu de Opções. Se você quiser fornecer itens de

menu que são sensíveis ao contexto para uma View, use um menu de contexto.

Se você está desenvolvendo para o Android 3.0 ou superior, não se esqueça de ler

também Usando a Barra de Ação.

Criando um Menu de Contexto

Um menu de contexto é conceitualmente semelhante ao menu exibido quando o usuário

executa um "clique-direito" em um PC. Você deve usar um menu de contexto para

proporcionar ao usuário o acesso às ações que pertencem a um item específico na

Page 130: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 130

interface do usuário. No Android, um menu de contexto é exibido quando o usuário

executa um "toque longo" (pressiona e segura) em um item.

Você pode criar um menu de contexto para qualquer view, apesar de menus de contexto

serem mais freqüentemente utilizados para os itens em um ListView. Quando o usuário

pressiona longamente em um item em uma ListView e a lista está registrada para

fornecer um menu de contexto, o item da lista sinaliza para o usuário que um menu de

contexto está disponível animando sua cor de fundo, que faz a transição do laranja ao

branco antes de abrir o menu de contexto. (O aplicativo Contatos demonstra esta

característica.)

A fim de fornecer um menu de contexto,

você deve "registrar" a view de um menu

de contexto. Chame

registerForContextMenu() e passe a View

que você quer dar um menu de contexto.

Quando esta view receber um toque de

longa duração, ela exibe um menu de

contexto.

Para definir a aparência e comportamento

do menu de contexto, substitua seus

métodos de retorno do menu de contexto

da atividade, onCreateContextMenu() e onContextItemSelected().

Por exemplo, aqui está um onCreateContextMenu() que usa o recurso de menu

context_menu.xml:

@Override

public void onCreateContextMenu(ContextMenu menu, View v,

ContextMenuInfo menuInfo) {

super.onCreateContextMenu(menu, v, menuInfo);

MenuInflater inflater = getMenuInflater();

inflater.inflate(R.menu.context_menu, menu);

}

MenuInflater é usado para inflar o menu de contexto de um recurso de menu. (Você

também pode usar o add() para adicionar itens de menu). Os parâmetros de método de

retorno incluem o View que o usuário selecionou e ContextMenu.ContextMenuInfo que

Registre uma ListView

Se a sua atividade usa um ListView e você

deseja que todos os itens da lista forneçam

um menu de contexto, registre todos os

itens de um menu de contexto, passando o

ListView para registerForContextMenu().

Por exemplo, se você estiver usando uma

ListActivity, registre todos os itens da lista

como esta:

registerForContextMenu( getListView() );

Page 131: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 131

fornece informações adicionais sobre o item selecionado. Você pode usar esses

parâmetros para determinar qual menu de contexto deve ser criado, mas neste exemplo,

todos os menus de contexto para a atividade são os mesmos.

Então, quando o usuário seleciona um item no menu de contexto, o sistema chama

onContextItemSelected(). Aqui está um exemplo de como você pode manipular os itens

selecionados:

@Override

public boolean onContextItemSelected(MenuItem item) {

AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();

switch (item.getItemId()) {

case R.id.edit:

editNote(info.id);

return true;

case R.id.delete:

deleteNote(info.id);

return true;

default:

return super.onContextItemSelected(item);

}

}

A estrutura deste código é semelhante ao exemplo de criação de um Menu de Opções,

em que getItemId() consulta o ID do item de menu selecionado e um switch

corresponde ao item para as identificações que são definidos no recurso de menu. E

como o exemplo do menu de opções, a declaração padrão chama a super classe no caso

dela poder lidar com itens de menu não tratados aqui, se necessário.

Neste exemplo, o item selecionado é um item da ListView. Para executar uma ação

sobre o item selecionado, o aplicativo precisa saber o ID da lista para o item selecionado

(a sua posição no ListView). Para obter o ID, o aplicativo chama getMenuInfo(), que

retorna um objeto AdapterView.AdapterContextMenuInfo que inclui a identificação de

lista para o item selecionado no campo id. Os métodos locais editNote() e deleteNote()

aceitam essa identificação da lista para executar uma ação nos dados especificados pelo

ID da lista.

Nota: Os itens em um menu de contexto não suportam ícones ou teclas de atalho.

Page 132: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 132

Criando Submenus

Um submenu é um menu que o usuário pode abrir por selecionar um item em outro

menu. Você pode adicionar um submenu a qualquer menu (com exceção de um

submenu). Submenus são úteis quando seu aplicativo tem um monte de funções que

podem ser organizadas em tópicos, como os itens na barra de uma aplicação para PC de

menu (Arquivo, Editar, Exibir, etc.)

Ao criar o seu recurso de menu, você pode criar um submenu, adicionando um

elemento <menu> como o filho de um <item>. Por exemplo:

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

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

<item android:id="@+id/file"

android:icon="@drawable/file"

android:title="@string/file" >

<!-- "file" submenu -->

<menu>

<item android:id="@+id/create_new"

android:title="@string/create_new" />

<item android:id="@+id/open"

android:title="@string/open" />

</menu>

</item>

</menu>

Quando o usuário seleciona um item do submenu, o respectivo método de retorno on-

item-selected do menu pai recebe o evento. Por exemplo, se o menu acima é aplicado

como um Menu de Opções, então o método onOptionsItemSelected() é chamado

quando um item de submenu é selecionado.

Você também pode usar addSubMenu() para adicionar dinamicamente um SubMenu a

um Menu já existente. Isso retorna o novo objeto SubMenu, para o qual você pode

adicionar itens de submenu, usando add().

Outras funções do menu

Aqui estão algumas outras características que podem ser aplicadas a itens do menu.

Grupos de Menu

Um grupo de menu é uma coleção de itens de menu que compartilham certas

características.

Page 133: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 133

Com um grupo, você pode:

Mostrar ou ocultar todos os itens com setGroupVisible()

Ativar ou desativar todos os itens com setGroupEnabled()

Especificar se todos os itens são verificados com setGroupCheckable()

Você pode criar um grupo aninhando elementos <item> dentro de um elemento

<group> em seu recurso de menu ou pela especificação de um ID de grupo com o

método add().

Aqui está um exemplo de recurso de menu que inclui um grupo:

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

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

<item android:id="@+id/item1"

android:icon="@drawable/item1"

android:title="@string/item1" />

<!-- menu group -->

<group android:id="@+id/group1">

<item android:id="@+id/groupItem1"

android:title="@string/groupItem1" />

<item android:id="@+id/groupItem2"

android:title="@string/groupItem2" />

</group>

</menu>

Os itens que estão no grupo aparecem da mesma forma que o primeiro item que não está

em um grupo - todos os três itens no menu são irmãos. No entanto, você pode modificar

as características dos dois elementos do grupo, referenciando o ID do grupo e utilizando

os métodos listados acima.

Itens de menu verificados

Um menu pode ser útil como uma interface fazendo das opções

ativas e inativas, usando uma caixa de seleção para as opções de

stand-alone, ou radion buttons para grupos com opções mutuamente

exclusivas. A Figura 2 mostra um submenu com os itens que são

verificados com radio buttons.

Figura 3. Screenshot de um submenu com os itens verificados.

Page 134: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 134

Nota: Os itens de menu no menu de ícone (no menu Opções) não podem exibir uma

caixa de seleção ou radio button. Se você optar por fazer os itens no menu do ícone

selecionáveis, você deve manualmente indicar o estado marcado por trocar o ícone

e/ou texto cada vez que ocorrem as mudanças de estado.

Você pode definir o comportamento checkable para itens individuais de menu usando o

atributo android:checkable no elemento <item>, ou para um grupo inteiro com o

atributo android:checkableBehavior no elemento <group>. Por exemplo, todos os itens

deste grupo de menu são verificados com um botão de rádio:

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

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

<group android:checkableBehavior="single">

<item android:id="@+id/red"

android:title="@string/red" />

<item android:id="@+id/blue"

android:title="@string/blue" />

</group>

</menu>

O atributo android:checkableBehavior aceita tanto:

single

Apenas um item do grupo pode ser verificado (botões de rádio)

all

Todos os itens podem ser verificados (caixas)

none

Nenhum item pode ser verificado

Você pode aplicar um estado padrão verificado de um item usando o atributo

android:checked no elemento <item> e alterá-lo em código com o método setChecked().

Quando um item checkable é selecionado, o sistema chama o seu método de retorno

item-selected (como onOptionsItemSelected()). É aqui que você deve definir o estado

da caixa, pois um botão de opção ou de rádio não muda o seu estado automaticamente.

Page 135: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 135

Você pode consultar o estado atual do item (como era antes que o usuário selecionou)

com isChecked() e, em seguida, definir o estado de verificado com setChecked(). Por

exemplo:

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.vibrate:

case R.id.dont_vibrate:

if (item.isChecked()) item.setChecked(false);

else item.setChecked(true);

return true;

default:

return super.onOptionsItemSelected(item);

}

}

Se você não definir o estado de “verificado” dessa forma, o estado visível do item (a

caixa de seleção ou botão de rádio) não vai mudar quando o usuário selecioná-lo.

Quando você definir o estado, a atividade preserva o estado de verificado do item para

que quando o usuário abra o menu mais tarde, o estado de verificado que você definiu

esteja visível.

Nota: os itens de menu verificáveis se destinam a serem usados apenas em uma sessão

base e não serão salvos após a aplicação ser destruída. Se você tiver as configurações

do aplicativo que você gostaria de salvar para o usuário, você deve armazenar os

dados usando Preferências Compartilhadas.

As teclas de atalho

Para facilitar o acesso rápido aos itens do menu de opções quando o dispositivo do

usuário tem um teclado físico, você pode adicionar atalhos de acesso rápido usando

letras e/ou números, com os atributos android:alphabeticShortcut e

android:numericShortcut no elemento <item>. Você também pode usar os métodos

setAlphabeticShortcut(char) e setNumericShortcut(char). Teclas de atalho não são case

sensitive.

Por exemplo, se você aplicar o caractere "s" como um atalho alfabético para um item

"salvar" de menu, então quando o menu está aberto (ou quando o usuário segura o botão

MENU) e o usuário pressiona a tecla "s", o item "Salvar" é selecionado.

Page 136: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 136

Esta tecla de atalho é exibida como uma dica no item de menu, abaixo do nome do item

de menu (com exceção de itens no menu do ícone, que são exibidas somente se o

usuário segura o botão MENU).

Nota: as teclas de atalho para itens de menu só funcionam em dispositivos com um

teclado de hardware. Atalhos não podem ser adicionado a itens de um menu de

contexto.

Adicionar intenções em menu dinamicamente

Às vezes você quer um item de menu para iniciar uma atividade usando uma Intent (seja

uma atividade em seu aplicativo ou outro aplicativo). Quando você sabe a intenção que

você deseja usar e tem um item de menu específico que deve iniciar a intenção, você

pode executar a intenção com startActivity() durante o apropriado método de retorno

on-item-selected (como o callback onOptionsItemSelected()).

No entanto, se você não tiver certeza de que o dispositivo do usuário contém um

aplicativo que lida com a intenção, então adicionar um item de menu que chama isso

pode resultar em um item de menu que não funciona, porque a intenção pode não

resolver a uma atividade. Para resolver isso, Android permite a você adicionar

dinamicamente os itens do menu para o menu quando Android encontra atividades

sobre o dispositivo que lidam com sua intenção.

Para adicionar itens de menu baseado em atividades disponíveis que aceitam uma

intenção:

1. Definir uma intenção, com a categoria CATEGORY_ALTERNATIVE e/ou

CATEGORY_SELECTED_ALTERNATIVE, além de quaisquer outras

exigências.

2. Chamar Menu.addIntentOptions(). Android então procura todas as aplicações

que podem executar a intenção e adiciona-os ao seu menu.

Se não houver aplicativos instalados que satisfazem a intenção, então não há itens de

menu que sejam adicionados.

Page 137: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 137

Nota: CATEGORY_SELECTED_ALTERNATIVE é usada para segurar o elemento

selecionado atualmente na tela. Assim, ele só deve ser utilizado para a criação de um

menu em onCreateContextMenu().

Por exemplo:

@Override

public boolean onCreateOptionsMenu(Menu menu){

super.onCreateOptionsMenu(menu);

// Create an Intent that describes the requirements to fulfill, to be included

// in our menu. The offering app must include a category value of

Intent.CATEGORY_ALTERNATIVE.

Intent intent = new Intent(null, dataUri);

intent.addCategory(Intent.CATEGORY_ALTERNATIVE);

// Search and populate the menu with acceptable offering applications.

menu.addIntentOptions(

R.id.intent_group, // Menu group to which new items will be added

0, // Unique item ID (none)

0, // Order for the items (none)

this.getComponentName(), // The current activity name

null, // Specific items to place first (none)

intent, // Intent created above that describes our requirements

0, // Additional flags to control items (none)

null); // Array of MenuItems that correlate to specific items (none)

return true;

}

Para cada atividade descoberta que fornece uma intenção de filtro correspondente a

intenção definida um item de menu é adicionado, utilizando o valor na intenção do filtro

android:label como o título do item de menu e o ícone do aplicativo como o item ícone

do menu. O método addIntentOptions() retorna o número de itens de menu que foram

acrescentados.

Nota: Quando você chamar addIntentOptions(), ela substitui qualquer e todos os itens

do menu, o grupo de menu especificado no primeiro argumento.

Permitindo a sua atividade a ser adicionada para outros menus

Você também pode oferecer os serviços de sua atividade para outras aplicações, para

que seu aplicativo possa ser incluído no menu de outros (inverter os papéis descritos

acima).

Page 138: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 138

Para ser incluído em outros menus de aplicativos, você precisa definir uma intenção de

filtro, como de costume, mas não se esqueça de incluir o

CATEGORY_ALTERNATIVE e/ou CATEGORY_SELECTED_ALTERNATIVE

para o filtro de categoria intenções. Por exemplo:

<intent-filter label="Resize Image">

...

<category android:name="android.intent.category.ALTERNATIVE" />

<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />

...

</intent-filter>

Leia mais sobre como escrever filtros de intenção em Intents and Intents Filters.

Page 139: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 139

Usando a barra de ação

A barra de ação é um widget para atividades que substitui a tradicional barra de título no

topo da tela. Por padrão, a barra de ação inclui o logotipo da aplicação do lado

esquerdo, seguido do título da atividade, e todos os itens disponíveis no menu Opções

no lado direito. A Barra de ação oferece vários recursos úteis, incluindo a capacidade

de:

Exibir itens do Menu de Opções diretamente na Barra de ação, como "itens de

ação" - fornecendo acesso imediato às ações-chave dos usuários.

Os itens de menu que não aparecem como itens de ação são colocados no menu

de estouro, revelado por uma lista suspensa na barra de ações.

Fornecer as guias para navegar entre os fragmentos.

Forneça uma lista drop-down para a navegação.

Fornecer interativas "visões de ação" no lugar de itens de ação (como uma caixa

de pesquisa).

Figura 1. Uma imagem da barra de ação na aplicação de e-mail, contendo itens de ação para compor

novo email e atualizar a caixa de entrada.

Adicionando a barra de ação

A barra de ação é incluída por padrão em todas as atividades que visam o Android 3.0

ou superior. Mais especificamente, todas as atividades que usam o novo tema

"holográfico" incluem a barra de ação, e qualquer aplicação que tem como alvo o

Android 3.0 automaticamente recebe esse tema. Uma aplicação é considerada para "o

alvo" Android 3.0 quando é definido tanto o atributo android:minSdkVersion ou

android:targetSdkVersion no elemento <uses-sdk> para "11" ou superior. Por exemplo:

Page 140: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 140

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

package="com.example.helloworld"

android:versionCode="1"

android:versionName="1.0">

<uses-sdk android:minSdkVersion="4"

android:targetSdkVersion="11" />

<application ... >

...

</application>

</manifest>

Neste exemplo, a aplicação requer uma versão mínima do API Nível 4 (Android 1.6),

mas também atinge API Nível 11 (Android 3.0). Dessa forma, quando o aplicativo é

instalado em um dispositivo rodando Android 3.0 ou superior, o sistema aplica o tema

holográfico para cada atividade, e assim, cada atividade inclui a Barra de Ações.

No entanto, se você quiser usar APIs de Barra de Ações, tais como guias para adicionar

ou modificar estilos da barra de ação, você precisa definir o android:minSdkVersion

para "11", assim você pode acessar a classe ActionBar.

Removendo a barra de ação

Se você quiser remover a barra de ação para uma determinada atividade, defina o tema

da atividade para Theme.Holo.NoActionBar. Por exemplo:

<activity android:theme="@android:style/Theme.Holo.NoActionBar">

Dica: Se você tem um tema de atividade personalizado no qual você gostaria de

remover a barra de ação, defina a propriedade de estilo android:windowActionBar

false.

Você também pode ocultar a barra de ação em tempo de execução chamando hide(), e

depois mostrá-la novamente chamando show(). Por exemplo:

ActionBar actionBar = getActionBar();

actionBar.hide();

Quando a barra de ação se esconde, o sistema ajusta seu conteúdo das atividades para

preencher todo o espaço disponível na tela.

Nota: Se você remover a Barra de ação usando um tema, a janela não vai permitir a

barra de ação a todos, então você não pode adicioná-la em tempo de execução -

chamar getActionBar() irá retornar null.

Page 141: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 141

Adicionando itens de ação

Um item de ação que é simplesmente um item do menu de opções que você declara

deve aparecer diretamente na barra de ações. Um item de ação pode incluir um ícone

e/ou texto. Se um item de menu não aparece como um item de ação, o sistema coloca no

menu de estouro, que o usuário pode abrir com o ícone do menu no lado direito da barra

de ação.

Figura 2. Uma barra de ação com dois itens de ação e menu de estouro.

Quando a primeira atividade começa, o sistema preenche a barra de ação e menu de

estouro chamando onCreateOptionsMenu() para sua atividade. Conforme discutido no

guia para a criação de menus, é neste método callback que você define o menu de

opções para a atividade.

Você pode especificar um item de menu para aparecer como um item de ação, se não

houver espaço para isso, a partir do seu recurso de menu, declarando

android:showAsAction="ifRoom" para o elemento <item>. Desta forma, o item de

menu aparece na barra de ação para um acesso rápido apenas se houver espaço

disponível para ele. Se não houver espaço suficiente, o item é colocado no menu de

transbordamento (revelado pelo ícone do menu no lado direito da barra de ação).

Você também pode declarar um item de menu para aparecer como um item de ação a

partir do seu código de aplicativo, chamando setShowAsAction() no MenuItem e

passando SHOW_AS_ACTION_IF_ROOM.

Se o item de menu fornece tanto um título como um ícone, então o item de ação mostra

apenas o ícone por default. Se você quiser incluir o texto do item de ação, adicione a

flag "with text": em XML, adicionar withText para o atributo android:showAsAction

ou, no código do aplicativo, use a flag SHOW_AS_ACTION_WITH_TEXT ao chamar

setShowAsAction(). A Figura 2 mostra uma barra de ação que tem dois itens de ação

com o texto e o ícone para o menu de estouro.

Aqui está um exemplo de como você pode declarar um item de menu como um item de

ação em um arquivo de recurso de menu:

Page 142: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 142

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

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

<item android:id="@+id/menu_add"

android:icon="@drawable/ic_menu_save"

android:title="@string/menu_save"

android:showAsAction="ifRoom|withText" />

</menu>

Neste caso, tanto as flags ifRoom e withText estão definidas, de modo que quando este

item aparece como um item de ação, inclui o texto do título, juntamente com o ícone.

Um item de menu colocado na barra de ação dispara os mesmos métodos de callback

que outros itens do menu de opções. Quando o usuário seleciona um item de ação, sua

atividade recebe uma chamada para onOptionsItemSelected(), passando o ID do item.

Nota: Se você adicionou o item de menu de um fragmento, o respectivo método

onOptionsItemSelected()é chamado para esse fragmento. No entanto, a atividade tem a

chance de manipulá-lo primeiro, então o sistema chama onOptionsItemSelected() da

atividade antes de chamar o fragmento.

Você também pode declarar um item que sempre aparece como um item de ação, mas

você deve evitar fazê-lo, porque ele pode criar uma interface confusa se houver muitos

itens de ação e também podem colidir com outros elementos na barra de ações.

Usando o ícone do aplicativo como um item de ação

Por padrão, o ícone do aplicativo aparece na barra de ações, no lado esquerdo. Ele

também responde à interação do usuário (quando o usuário toca, ele responde

visualmente do mesmo jeito que os itens de ação) e que é sua responsabilidade fazer

algo quando o usuário toca-os.

Figura 3. Barra de ação de email, com o ícone do aplicativo no lado esquerdo.

O comportamento deve ser normal para a sua aplicação para regressar à "HOME" à

atividade ou ao estado inicial (como quando a atividade não mudou, mas os fragmentos

foram alterados) quando o usuário toca o ícone. Se o usuário já está em casa ou no

estado inicial, então você não precisa fazer nada.

Page 143: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 143

Quando o usuário toca o ícone, o sistema chama o método onOptionsItemSelected() de

sua atividade com o ID android.R.id.home. Então, você precisa adicionar uma condição

para o seu método onOptionsItemSelected() para receber android.R.id.home e executar

a ação apropriada, como iniciar a atividade inicial ou remover recentes transações do

fragmento fora da pilha.

Se você responder para o ícone do aplicativo, retornando à atividade inicial, você deve

incluir o flag FLAG_ACTIVITY_CLEAR_TOP na Intent. Com este indicador, se a

atividade que você está começando já existe na tarefa atual, então todas as atividades

em cima dela são destruídas e essa é trazida para frente. Você deve favorecer essa

abordagem, porque ir para "HOME" é uma ação que é equivalente a "voltar atrás" e

você geralmente não deve criar uma nova instância da atividade inicial. Caso contrário,

você pode acabar com uma longa pilha de atividades na tarefa atual.

Por exemplo, aqui está uma implementação do onOptionsItemSelected() que retorna

para a aplicação da "HOME" de atividade:

@Override

public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case android.R.id.home:

// app icon in Action Bar clicked; go home

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

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

startActivity(intent);

return true;

default:

return super.onOptionsItemSelected(item);

}

}

Usando o ícone do aplicativo para navegar "para cima"

Você também pode usar o ícone do aplicativo para oferecer uma

navegação "para cima" para o usuário. Isto é especialmente útil

quando o aplicativo é composto por atividades que geralmente

aparecem em uma certa ordem e se pretende facilitar a

habilidade do usuário para navegar na hierarquia de atividade.

A maneira como você responde a este evento é a mesma de quando navegando para

HOME (como discutido acima, exceto se você iniciar uma atividade diferente, com base

Page 144: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 144

na atividade corrente). Tudo que você precisa fazer para indicar ao usuário que o

comportamento é diferente é definir a barra de ação para "mostrar a HOME como „para

cima‟". Você pode fazer isso chamando setDisplayHomeAsUpEnabled(true) na barra de

ações da sua atividade. Quando o fizer, o sistema retira o ícone do aplicativo com uma

seta indicando o comportamento „para cima‟, como mostrado acima.

Por exemplo, aqui está como você pode mostrar o ícone do aplicativo como uma ação

"para cima":

@Override

protected void onStart() {

super.onStart();

ActionBar actionBar = this.getActionBar();

actionBar.setDisplayHomeAsUpEnabled(true);

}

Então, sua atividade deve responder ao usuário tocar no ícone, a partir do

onOptionsItemSelected(), recebendo o ID android.R.id.home (como mostrado acima).

Neste caso, ao navegar para cima, é ainda mais importante que você use o

FLAG_ACTIVITY_CLEAR_TOP na Intent, de modo que você não cria uma nova

instância da atividade do pai, se já existe um.

Adicionando uma exibição de ação

Figura 5. Uma visão de ação com um widget SearchView.

Uma exibição de ação é um widget que aparece na barra de ação como um substituto

para um item de ação. Por exemplo, se você tem um item no menu opções para "Busca",

você pode adicionar uma exibição de ação para o item que fornece um widget

SearchView na barra de ação sempre que o item é ativado como um item de ação.

Ao adicionar uma visão de ação para um item de menu, é importante que você ainda

permita ao item se comportar como um item de menu normal quando ela não aparece na

barra de ações. Por exemplo, um item de menu para realizar uma pesquisa deve, por

padrão, abrir a janela de pesquisa do Android, mas se o item é colocado na barra de

ação, a visão de ação aparece com um widget SearchView. A Figura 5 mostra um

exemplo do SearchView em uma visão de ação.

Page 145: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 145

A melhor forma de declarar uma visão de ação para um item está em seu recurso de

menu, usando o atributo android:actionLayout ou android:actionViewClass:

O valor para android:actionLayout deve ser um ponteiro de recurso para um

arquivo de layout. Por exemplo:

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

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

<item android:id="@+id/menu_search"

android:title="Search"

android:icon="@drawable/ic_menu_search"

android:showAsAction="ifRoom"

android:actionLayout="@layout/searchview" />

</menu>

O valor para android:actionViewClass deve ser um nome de classe qualificado

para o View que você deseja usar. Por exemplo:

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

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

<item android:id="@+id/menu_search"

android:title="Search"

android:icon="@drawable/ic_menu_search"

android:showAsAction="ifRoom"

android:actionViewClass="android.widget.SearchView" />

</menu>

Você deve incluir android:showAsAction="ifRoom" para que o item apareça como uma

visão de ação quando o room está disponível. Se necessário, no entanto, pode forçar o

item que sempre apareça como uma visão de ação, definindo android:showAsAction

para "always".

Agora, quando o item de menu é exibido como um item de ação, esta vista de ação

aparece em vez do ícone e/ou texto do título. No entanto, se não houver espaço

suficiente na barra de ação, o item aparece no menu de estouro como um item de menu

normal e você deve responder a ela a partir do método de retorno

onOptionsItemSelected().

Quando a primeira atividade começa, o sistema preenche a barra de ação e menu de

estouro chamando onCreateOptionsMenu(). Depois de ter inflado seu menu neste

método, você pode adquirir elementos em uma visão de ação (talvez a fim de anexar

ouvintes) chamando findItem() com o ID do item de menu, então getActionView() no

Page 146: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 146

MenuItem retornado. Por exemplo, o widget de busca do modelo acima é adquirido

como este:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.options, menu);

SearchView searchView = (SearchView)

menu.findItem(R.id.menu_search).getActionView();

// Set appropriate listeners for searchView

...

return super.onCreateOptionsMenu(menu);

}

Adicionando abas

Figura 6. Screenshot de abas na barra de ação, do modelo de aplicação do Honeycomb Gallery.

A Barra de ação pode exibir guias que permitem ao usuário navegar entre diferentes

fragmentos na atividade. Cada guia pode incluir um título e/ou um ícone.

Para começar, o esquema deve incluir um View, em que cada Fragment associado com

uma guia é exibida. Tenha certeza de que tem uma identificação que pode ser usada

para fazer referência a ela em seu código.

Para adicionar guias para a barra de ações:

1. Criar uma aplicação de ActionBar.TabListener para manipular os eventos de

interação na barra de guias de ação. Você deve implementar todos os métodos:

onTabSelected(), onTabUnselected(), e onTabReselected().

Cada método de retorno passa a ActionBar.Tab que recebeu o evento e um

FragmentTransaction para você efetuar as transações do fragmento (adicionar ou

remover fragmentos).

Page 147: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 147

Por exemplo:

private class MyTabListener implements ActionBar.TabListener {

private TabContentFragment mFragment;

// Called to create an instance of the listener when adding a new tab

public TabListener(TabContentFragment fragment) {

mFragment = fragment;

}

@Override

public void onTabSelected(Tab tab, FragmentTransaction ft) {

ft.add(R.id.fragment_content, mFragment, null);

}

@Override

public void onTabUnselected(Tab tab, FragmentTransaction ft) {

ft.remove(mFragment);

}

@Override

public void onTabReselected(Tab tab, FragmentTransaction ft) {

// do nothing

}

}

Esta implementação de ActionBar.TabListener adiciona um construtor que salva

o Fragment associado com um guia para que cada retorno possa adicionar ou

remover esse fragmento.

2. Receba a ActionBar para a sua atividade, chamando getActionBar() de sua

atividade, durante onCreate() (mas não se esqueça de fazer isso depois de você

ter chamado setContentView()).

3. Chame setNavigationMode(NAVIGATION_MODE_TABS) para habilitar o

modo guia para a ActionBar .

4. Criar cada guia para a barra de ação:

a. Criar uma nova ActionBar.Tab chamando newTab() na ActionBar .

b. Acrescentar o texto do título e/ou um ícone para o guia, chamando

setText() e/ou setIcon().

Dica: Esses métodos retornam a mesma instância ActionBar.Tab, assim

você pode encadear as chamadas juntas.

Page 148: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 148

c. Declare o ActionBar.TabListener para usar a guia por meio de um

exemplo de sua aplicação para setTabListener().

5. Adicione cada ActionBar.Tab à barra de ação, chamando addTab() na ActionBar

e passe a ActionBar.Tab.

Por exemplo, o código a seguir combina as etapas 2-5 para criar duas guias e adicioná-

las à Barra de ação:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// setup Action Bar for tabs

final ActionBar actionBar = getActionBar();

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

// remove the activity title to make space for tabs

actionBar.setDisplayShowTitleEnabled(false);

// instantiate fragment for the tab

Fragment artistsFragment = new ArtistsFragment();

// add a new tab and set its title text and tab listener

actionBar.addTab(actionBar.newTab().setText(R.string.tab_artists)

.setTabListener(new TabListener(artistsFragment)));

Fragment albumsFragment = new AlbumsFragment();

actionBar.addTab(actionBar.newTab().setText(R.string.tab_albums)

.setTabListener(new TabListener(albumsFragment)));

}

Todos os comportamentos que ocorrem quando uma guia é selecionada devem ser

definidos pelo seu método de callback ActionBar.TabListener. Quando uma guia é

selecionada, ela recebe uma chamada para onTabSelected() e é aí que você deve

adicionar o fragmento apropriado para a exibição designada em seu layout, utilizando

add() com o previsto FragmentTransaction. Da mesma forma, quando uma guia é

desmarcada (porque outra guia é selecionada), você deve remover o fragmento do

layout, utilizando remove().

Atenção: Você não deve chamar commit() para essas operações, o sistema chama-o

para você e pode lançar uma exceção se você chamá-lo. Você também não pode

adicionar essas transações de fragmento para o fundo da pilha.

Se a sua atividade é interrompida, você deve manter a guia selecionada no momento

com o estado salvo de modo que quando o usuário retorna à sua aplicação, você pode

Page 149: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 149

abrir a guia. Quando é hora de salvar o estado, você pode consultar a atual guia

selecionada com getSelectedNavigationIndex(). Isso retorna a posição do índice da guia

selecionada.

Atenção: É importante que você salve o estado de cada fragmento quando necessário,

pois quando o usuário alterna fragmentos com as guias, e em seguida, retorna a um

fragmento anterior, aparece o caminho que ele deixou. Para obter informações sobre

como salvar o seu estado de fragmento, consulte o guia do desenvolvedor sobre

Fragmentos.

Adicionando de navegação drop-down

Como um outro modo de navegação em sua atividade, você pode fornecer uma lista

suspensa na barra de ações. Por exemplo, a lista drop-down pode fornecer os meios

alternativos para classificar o conteúdo da atividade ou mudar a conta do usuário.

Aqui está uma lista rápida de medidas para permitir a navegação drop-down:

1. Criar um SpinnerAdapter que fornece a lista de itens selecionáveis para o drop-

down e a disposição para usar ao desenhar cada item na lista.

2. Implementar ActionBar.OnNavigationListener para definir o comportamento

quando o usuário seleciona um item da lista.

3. Habilitar o modo de navegação para a barra de ação com setNavigationMode().

Por exemplo:

ActionBar actionBar = getActionBar();

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

Nota: Você deve executar isto durante a sua atividade do método onCreate().

4. Em seguida, defina o retorno para a lista drop-down com

setListNavigationCallbacks(). Por exemplo:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

Este método tem a sua SpinnerAdapter e ActionBar.OnNavigationListener.

Essa é a configuração básica. No entanto, a implementação da SpinnerAdapter e

ActionBar.OnNavigationListener é onde a maioria do trabalho é feito. Há muitas

Page 150: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 150

maneiras que você pode aplicar isto para definir a funcionalidade para o seu drop-down

de navegação e implementar vários tipos de SpinnerAdapter além do escopo deste

documento (você deve referenciar para a classe SpinnerAdapter para obter mais

informações). No entanto, abaixo há um exemplo simples para um SpinnerAdapter e

ActionBar.OnNavigationListener para você começar.

Exemplo de SpinnerAdapter e OnNavigationListener

SpinnerAdapter é um adaptador que fornece dados para um widget spinner, como a lista

drop-down na barra de ações. SpinnerAdapter é uma interface que você pode

implementar, mas Android inclui algumas implementações úteis que você pode

estender, como ArrayAdapter e SimpleCursorAdapter. Por exemplo, aqui está uma

maneira fácil de criar um SpinnerAdapter usando a implementação de ArrayAdapter,

que usa uma matriz de string como fonte de dados:

SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,

R.array.action_list, android.R.layout.simple_spinner_dropdown_item);

O método createFromResource() usa três parâmetros: a aplicação Context, a

identificação de recursos para array de string, e o layout a ser usado para cada item da

lista.

Um array de string é definido em um recurso parecido com este:

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

<resources>

<string-array name="action_list">

<item>Mercury</item>

<item>Venus</item>

<item>Earth</item>

</string-array>

</pre>

O ArrayAdapter retornado por createFromResource() está completo e pronto para você

passar a setListNavigationCallbacks() (na etapa 4 acima). Antes de fazer, porém, você

precisa criar o OnNavigationListener.

A implementação do ActionBar.OnNavigationListener é onde você lida com as

mudanças de fragmento ou outras modificações à sua atividade quando o usuário

seleciona um item da lista drop-down. Há apenas um método callback para implementar

o receptor: onNavigationItemSelected().

Page 151: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 151

O método onNavigationItemSelected() recebe a posição do item na lista e um ID de

único item fornecido pela SpinnerAdapter.

Aqui está um exemplo que instancia uma implementação anônima de

OnNavigationListener, que insere um Fragment dentro do recipiente layout identificado

por R.id.fragment_container:

mOnNavigationListener = new OnNavigationListener() {

// Get the same strings provided for the drop-down's ArrayAdapter

String[] strings = getResources().getStringArray(R.array.action_list);

@Override

public boolean onNavigationItemSelected(int position, long itemId) {

// Create new fragment from our own Fragment class

ListContentFragment newFragment = new ListContentFragment();

FragmentTransaction ft = openFragmentTransaction();

// Replace whatever is in the fragment container with this fragment

// and give the fragment a tag name equal to the string at the position selected

ft.replace(R.id.fragment_container, newFragment, strings[position]);

// Apply changes

ft.commit();

return true;

}

};

Esta instância de OnNavigationListener está completo e agora você pode chamar

setListNavigationCallbacks() (na etapa 4), passando o ArrayAdapter e este

OnNavigationListener.

Neste exemplo, quando o usuário seleciona um item da lista drop-down, um fragmento

é adicionado ao layout (que substitui o fragmento atual no R.id.fragment_container

vista). O fragmento adicional é dada uma etiqueta que identifica-lo, que é a mesma

seqüência de caracteres usado para identificar o fragmento na lista drop-down.

Page 152: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 152

Aqui está um olhar da classe ListContentFragment que define cada fragmento neste

exemplo:

public class ListContentFragment extends Fragment {

private String mText;

@Override

public void onAttach(Activity activity) {

// This is the first callback received; here we can set the text for

// the fragment as defined by the tag specified during the fragment transaction

super.onAttach(activity);

mText = getTag();

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// This is called to define the layout for the fragment;

// we just create a TextView and set its text to be the fragment tag

TextView text = new TextView(getActivity());

text.setText(mText);

return text;

}

}

Estilizando a barra de ação

A barra de ação é o título do seu aplicativo e um ponto de interação primário para os

usuários, então você pode querer modificar alguns em seu projeto, a fim de torná-lo

sentir mais integrado com o design do aplicativo. Há várias maneiras que você pode

fazer isso se quiser.

Para modificações simples para o ActionBar, você pode usar os métodos a seguir:

setBackgroundDrawable()

Define um drawable para usar como fundo da barra de ação. O drawable deve

ser uma imagem Nine-patch, uma forma, ou uma cor sólida, para que o sistema

pode redimensionar a drawable com base no tamanho da barra de ação (você não

deve usar uma imagem bitmap de tamanho fixo).

setDisplayUseLogoEnabled()

Permite o uso de uma imagem alternativa (a "logo") na barra de ação, em vez do

ícone do aplicativo padrão. Um logotipo é muitas vezes uma imagem mais

ampla, mais detalhada que representa a aplicação. Quando isso estiver ativado, o

Page 153: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 153

sistema utiliza a imagem do logotipo definido para a aplicação (ou a atividade

individual) no arquivo de manifesto, com o atributo android:logo. O logotipo

será redimensionado, conforme necessário para ajustar a altura da barra de ação.

(Melhores práticas é projetar o logotipo com o mesmo tamanho do ícone do

aplicativo.)

Para personalizações mais complexas, você pode usar estilos e temas do Android para

remodelar sua barra de ação de várias maneiras.

A Barra de ação tem dois temas padrão, "dark" e "light". O tema escuro é aplicado com

o tema padrão holográfico, conforme especificado pelo tema Theme.Holo. Se você

quiser um fundo branco com texto escuro, em vez disso, você pode aplicar o tema

Theme.Holo.Light para a atividade no arquivo de manifesto. Por exemplo:

<activity android:name=".ExampleActivity"

android:theme="@android:style/Theme.Holo.Light" />

Para ter mais controle, você pode substituir o tema Theme.Holo ou Theme.Holo.Light e

aplicar estilos personalizados para determinados aspectos da Barra de Ações. Algumas

das propriedades da barra de ação você pode personalizar incluindo o seguinte:

android:actionBarTabStyle

Estilo de abas na barra de ação.

android:actionBarTabBarStyle

Estilo para a barra que aparece abaixo das abas na barra de ação.

android:actionBarTabTextStyle

Estilo para o texto nos separadores.

android:actionDropDownStyle

Estilo para a lista drop-down utilizado para o menu de navegação de

transbordamento e drop-down.

android:actionButtonStyle

Estilo para a imagem de fundo usado para os botões na barra de ação.

Page 154: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 154

Por exemplo, aqui está um arquivo de recurso que define um tema personalizado para a

barra de ação, baseado no tema padrão Theme.Holo:

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

<resources>

<!-- the theme applied to the application or activity -->

<style name="CustomActionBar" parent="android:style/Theme.Holo.Light">

<item

name="android:actionBarTabTextStyle">@style/customActionBarTabTextStyle</item>

<item name="android:actionBarTabStyle">@style/customActionBarTabStyle</item>

<item

name="android:actionBarTabBarStyle">@style/customActionBarTabBarStyle</item>

</style>

<!-- style for the tab text -->

<style name="customActionBarTabTextStyle">

<item name="android:textColor">#2966c2</item>

<item name="android:textSize">20sp</item>

<item name="android:typeface">sans</item>

</style>

<!-- style for the tabs -->

<style name="customActionBarTabStyle">

<item name="android:background">@drawable/actionbar_tab_bg</item>

<item name="android:paddingLeft">20dp</item>

<item name="android:paddingRight">20dp</item>

</style>

<!-- style for the tab bar -->

<style name="customActionBarTabBarStyle">

<item name="android:background">@drawable/actionbar_tab_bar</item>

</style>

</resources>

Nota: Para que a imagem de fundo guia mude, dependendo do estado de separador

atual (selecionado, pressionado, não selecionado), o recurso drawable utilizado deve

ser uma lista de estado drawable. Também é certo que o tema declara um tema

principal, da qual ele herda todos os estilos não explicitamente declarados em seu

tema.

Você pode aplicar o seu tema personalizado para o aplicativo inteiro ou para atividades

individuais no arquivo de manifesto, como este:

<application android:theme="@style/CustomActionBar"

... />

Além disso, se você quer criar um tema personalizado para a sua atividade que remove a

barra de ação completamente, use os atributos de estilo a seguir:

Page 155: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 155

android:windowActionBar

Defina esta propriedade de estilo como false para remover a barra de ações.

android:windowNoTitle

Defina esta propriedade de estilo como true também para remover a barra de

título tradicional.

Page 156: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 156

Criando caixas de diálogo

Uma caixa de diálogo é geralmente uma pequena janela que aparece na frente da

atividade atual. A atividade em causa perde o foco e o diálogo aceita todas as interações

do usuário. Os diálogos são normalmente utilizados para as notificações que devem

interromper o usuário e para executar tarefas curtas que se relacionam diretamente com

a aplicação em curso (como uma barra de progresso ou um prompt de login).

A classe Dialog é a classe base para a criação de diálogos. No entanto, você geralmente

não deve instanciar um Dialog diretamente. Em vez disso, você deve usar uma das

seguintes subclasses:

AlertDialog

Uma caixa de diálogo que pode gerenciar zero, um, dois ou três botões, e/ou

uma lista de itens selecionáveis que podem incluir caixas de verificação ou

botões de rádio. O AlertDialog é capaz de construir a maioria das interfaces de

diálogo com o usuário e é o tipo de caixa de diálogo sugerido.

ProgressDialog

Uma caixa de diálogo que exibe uma roda de progresso ou barra de progresso.

Porque é uma extensão do AlertDialog, ele também suporta botões.

DatePickerDialog

Um diálogo que permite que o usuário selecione uma data.

TimePickerDialog

Um diálogo que permite ao usuário selecionar um tempo.

Se você quiser personalizar a sua própria caixa de diálogo, você pode estender a base

Dialog ou qualquer objeto das subclasses acima e definir um novo layout.

Mostrando uma caixa de diálogo

Um diálogo é sempre criado e exibido como parte de uma Activity. Você normalmente

deve criar diálogos de dentro do método de retorno onCreateDialog(int) da sua

atividade. Quando você usa esta chamada, o sistema Android gerencia automaticamente

Page 157: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 157

o estado de cada diálogo e os ganchos para a atividade, efetivamente tornando-o o

"dono" de cada diálogo. Como tal, cada diálogo herda algumas propriedades da

atividade. Por exemplo, quando uma janela é aberta, a tecla Menu revela o menu de

opções definidas para a atividade e as teclas de volume modificam o fluxo de áudio

usado pela atividade.

Nota: Se você decidir criar um diálogo fora do método onCreateDialog(), não irá ser

anexado a uma atividade. Você pode, entretanto, anexá-lo a uma atividade com

setOwnerActivity(Activity).

Quando você quer mostrar uma janela, chame showDialog(int) e passe um número

inteiro que identifica a caixa de diálogo que você deseja exibir.

Quando uma janela é solicitada pela primeira vez, o Android chama

onCreateDialog(int) de sua atividade, que é onde você deve criar uma instância do

Dialog. Neste método de retorno é passado o mesmo ID que você passou para

showDialog(int). Depois de criar o diálogo, retorne o objeto no final do método.

Antes que o diálogo ser exibido, o Android também chama o método callback opcional

onPrepareDialog(int, Dialog). Defina nesse método se você deseja alterar as

propriedades da caixa de diálogo cada vez que for aberta. Este método é chamado toda

vez que uma caixa de diálogo é aberta, enquanto onCreateDialog(int) é chamado apenas

na primeira vez que uma caixa de diálogo é aberta. Se você não definir

onPrepareDialog(), então o diálogo continuará a ser o mesmo que era o tempo anterior

que foi aberto. Este método também passa o ID do diálogo, além de um diálogo do

objeto que você criou na onCreateDialog().

A melhor maneira de definir os métodos de retorno onCreateDialog(int) e

onPrepareDialog(int, Dialog) é com uma instrução switch que verifica o parâmetro id

que é passado para o método. Cada caso deve verificar se há uma identificação única de

diálogo e, em seguida, criar e definir o respectivo diálogo. Por exemplo, imagine um

jogo que usa dois diálogos distintos: um para indicar que o jogo tem uma pausa e outra

para indicar que o jogo acabou. Primeiro, defina um ID de número inteiro para cada

caixa de diálogo:

static final int DIALOG_PAUSED_ID = 0;

static final int DIALOG_GAMEOVER_ID = 1;

Page 158: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 158

Em seguida, defina a chamada onCreateDialog(int) com um caso de interruptor para

cada ID:

protected Dialog onCreateDialog(int id) {

Dialog dialog;

switch(id) {

case DIALOG_PAUSED_ID:

// do the work to define the pause Dialog

break;

case DIALOG_GAMEOVER_ID:

// do the work to define the game over Dialog

break;

default:

dialog = null;

}

return dialog;

}

Nota: Neste exemplo, não há nenhum código dentro da declaração de caso, porque o

procedimento para a definição de seu diálogo está fora do escopo desta seção.

Quando é hora de mostrar um dos diálogos, chame showDialog(int) com o ID de um

diálogo:

showDialog(DIALOG_PAUSED_ID);

Dispensar um diálogo

Quando estiver pronto para fechar o diálogo, você pode descartá-lo chamando dismiss()

no objeto Dialog. Se necessário, você também pode chamar dismissDialog(int) da

atividade, o que efetivamente chama dismiss() na caixa de diálogo para você.

Se você estiver usando onCreateDialog(int) para gerir o seu estado de diálogos (como

discutido na seção anterior), então cada vez que o diálogo é indeferido, o estado do

objeto de diálogo é mantido pela atividade. Se você decidir que você não precisa mais

desse objeto ou é importante que o estado esteja limpo, então você deve chamar

removeDialog(int). Isto irá remover todas as referências internas ao objeto e se o

diálogo está mostrando, vai dispensá-lo.

Usando demissão de receptores

Se você quiser que seu aplicativo execute alguns procedimentos no momento em que

um diálogo é dispensado, então você deve anexar um receptor „on-dismiss‟ no seu

diálogo.

Page 159: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 159

Primeiro defina a interface DialogInterface.OnDismissListener. Essa interface possui

apenas um método, onDismiss(DialogInterface), que será chamado quando o diálogo

for descartado. Depois, passe a sua implementação OnDismissListener para

setOnDismissListener().

No entanto, note que os diálogos também podem estar "cancelados". Este é um caso

especial que indica que o diálogo foi explicitamente cancelado por parte do usuário.

Isso ocorrerá se o usuário pressiona o botão "BACK " para fechar a janela, ou se a caixa

de diálogo solicita explicitamente cancel() (talvez a partir de um botão "Cancelar" na

caixa de diálogo). Quando um diálogo for cancelado, o OnDismissListener ainda será

notificado, mas se você gostaria de ser informado de que o diálogo foi expressamente

cancelado (e não dispensado normalmente), então você deve registrar um

DialogInterface.OnCancelListener com setOnCancelListener().

Criando um AlertDialog

Um AlertDialog é uma extensão da classe Dialog. É capaz de construir a maioria das

interfaces de usuário de diálogo e é o tipo de diálogo sugerido. Você deve usá-lo para o

diálogo que usam qualquer uma das seguintes características:

Um título

Uma mensagem de texto

Um, dois ou três botões

Uma lista de itens selecionáveis (com checkbox ou radio-button)

Para criar um AlertDialog, use a subclasse AlertDialog.Builder. Receba um construtor

com AlertDialog.Builder(Context) e depois use os métodos públicos de classe para

definir todas as propriedades AlertDialog. Depois que está finalizado com o construtor,

recupere o objeto AlertDialog com create().

Os tópicos a seguir mostram como definir várias propriedades do AlertDialog usando a

classe AlertDialog.Builder. Se você usar qualquer um dos seguintes códigos de exemplo

dentro do seu método de retorno onCreateDialog(), você pode retornar o objeto

resultante de diálogo para exibir o diálogo.

Page 160: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 160

Adicionando botões

Para criar um AlertDialog com botões lado a lado, como a mostrada na imagem à

direita, use o método set...Button():

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

builder.setMessage("Are you sure you want to exit?")

.setCancelable(false)

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

public void onClick(DialogInterface dialog, int id) {

MyActivity.this.finish();

}

})

.setNegativeButton("No", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

dialog.cancel();

}

});

AlertDialog alert = builder.create();

Primeiro, adicione uma mensagem para o diálogo com setMessage(CharSequence) .

Então, comece método de encadeamento e definir a janela para não ser cancelado (por

isso o usuário não poderá fechar o diálogo com o botão traseiro) com

setCancelable(boolean) . Para cada botão, use um dos set...Button() métodos, como

setPositiveButton() , que aceita o nome do botão e um DialogInterface.OnClickListener

que define as medidas a tomar quando o usuário seleciona o botão.

Nota: Você só pode adicionar um botão de cada tipo à AlertDialog. Ou seja, você não

pode ter mais de um botão "positivo". Isso limita o número de botões possíveis para

três: positivo, neutro e negativo. Estes nomes são tecnicamente irrelevantes para a

funcionalidade real de seus botões, mas deve ajudá-lo a acompanhar o que faz o quê.

Page 161: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 161

Adicionando uma lista

Para criar um AlertDialog com uma lista de itens selecionáveis como o mostrado à

esquerda, use o método setItems():

final CharSequence[] items = {"Red", "Green", "Blue"};

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

builder.setTitle("Pick a color");

builder.setItems(items, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int item) {

Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();

}

});

AlertDialog alert = builder.create();

Primeiro, adicione um título para o diálogo com setTitle(CharSequence). Em seguida,

adicione uma lista de itens selecionáveis com setItems(), que aceita um conjunto de

itens a serem exibidos e um DialogInterface.OnClickListener que define as medidas a

tomar quando o usuário seleciona um item.

Adicionando caixas de seleção e botões de rádio

Para criar uma lista de itens de escolha múltipla

(caixas) ou itens de escolha única (botões de rádio)

dentro do diálogo, use os métodos

setMultiChoiceItems() e setSingleChoiceItems(),

respectivamente. Se você criar uma destas listas

selecionáveis no método de retorno onCreateDialog(),

o Android gerencia o estado da lista para você.

Contanto que a atividade esteja ativa, o diálogo se lembra dos itens que foram

previamente selecionados, mas quando o usuário sai da atividade, a seleção está

perdida.

Page 162: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 162

Nota: Para salvar a seleção quando o usuário deixa ou faz pausa na atividade, você

deve salvar e restaurar corretamente a configuração de todo o ciclo de vida de

atividade. Para salvar permanentemente as seleções, mesmo quando o processo de

atividade é completamente parado, você precisa salvar as configurações com uma das

técnicas de Armazenamento de Dados.

Para criar um AlertDialog com uma lista de itens de escolha simples, como a mostrada

acima, use o mesmo código do exemplo anterior, mas substitua o método setItems()

pelo setSingleChoiceItems():

final CharSequence[] items = {"Red", "Green", "Blue"};

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

builder.setTitle("Pick a color");

builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int item) {

Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();

}

});

AlertDialog alert = builder.create();

O segundo parâmetro no método setSingleChoiceItems() é um valor inteiro para o

checkedItem, que indica a posição de lista com base zero do item selecionado padrão.

Use "-1" para indicar que nenhum item deve ser selecionado por padrão.

Criar um ProgressDialog

A ProgressDialog é uma extensão da classe

AlertDialog que pode exibir uma animação de

progresso na forma de uma roda, para uma tarefa com

o progresso indefinido, ou uma barra de progresso,

para uma tarefa que tem uma progressão definida. O diálogo também pode fornecer

botões, como um de cancelar um download.

Abrir uma janela de progresso pode ser tão simples como chamar

ProgressDialog.show(). Por exemplo, o diálogo mostrado acima pode ser facilmente

alcançado sem gerenciar o diálogo através da chamada onCreateDialog(int), conforme

mostrado abaixo:

ProgressDialog dialog = ProgressDialog.show(MyActivity.this, "",

"Loading. Please wait...", true);

Page 163: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 163

O primeiro parâmetro é a aplicação Context, o segundo é um título para o diálogo

(vazio), o terceiro é a mensagem e o último parâmetro é se o progresso é indeterminado

(isso só é relevante quando cria uma barra de progresso, que é discutido na próxima

seção).

O estilo padrão de um diálogo de progresso é a roda. Se você deseja criar uma barra de

progresso que mostra o progresso do carregamento com granularidade, mais código é

necessário, como será discutido na próxima seção.

Mostrando uma barra de progresso

Para mostrar a progressão com uma barra de progresso animada:

1. Inicialize o ProgressDialog com o construtor da classe,

ProgressDialog(Context).

2. Defina o estilo de progressos para "STYLE_HORIZONTAL" com

setProgressStyle(int) e defina as outras propriedades, como a mensagem.

3. Quando estiver pronto para mostrar o diálogo, chame show() ou devolva o

ProgressDialog do onCreateDialog(int) de retorno.

4. Você pode incrementar a quantidade de progresso exibida na barra chamando

tanto setProgress(int) com um valor para a porcentagem total concluída ou

incrementProgressBy(int) com um valor incremental para adicionar à

porcentagem total concluída até agora.

Por exemplo, sua configuração pode se parecer como esta:

ProgressDialog progressDialog;

progressDialog = new ProgressDialog(mContext);

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.setMessage("Loading...");

progressDialog.setCancelable(false);

Page 164: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 164

A configuração é simples. A maior parte do código necessário para criar um diálogo de

progresso está realmente envolvida no processo que atualizá-lo. Você pode achar que é

necessário criar um segundo thread em sua aplicação para este trabalho e, em seguida,

relatar o progresso de volta à atividade do thread de interface do usuário com um

Handler do objeto. Se você não está familiarizado com o uso de threads adicionais com

um manipulador, vejo o exemplo abaixo, que utiliza um segundo thread para

incrementar um diálogo de progresso gerido pela atividade.

Exemplo ProgressDialog com um segundo thread

Este exemplo usa um segundo thread para acompanhar o andamento de um processo

(que na verdade só conta até 100). O thread envia uma Message de volta à atividade

principal através de um Handler a cada hora em que algum progresso é feito. A

atividade principal, em seguida, atualiza o ProgressDialog.

package com.example.progressdialog;

import android.app.Activity;

import android.app.Dialog;

import android.app.ProgressDialog;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

public class NotificationTest extends Activity {

static final int PROGRESS_DIALOG = 0;

Button button;

ProgressThread progressThread;

ProgressDialog progressDialog;

/** Called when the activity is first created. */

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// Setup the button that starts the progress dialog

button = (Button) findViewById(R.id.progressDialog);

button.setOnClickListener(new OnClickListener(){

public void onClick(View v) {

showDialog(PROGRESS_DIALOG);

}

});

}

protected Dialog onCreateDialog(int id) {

Page 165: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 165

switch(id) {

case PROGRESS_DIALOG:

progressDialog = new ProgressDialog(NotificationTest.this);

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

progressDialog.setMessage("Loading...");

return progressDialog;

default:

return null;

}

}

@Override

protected void onPrepareDialog(int id, Dialog dialog) {

switch(id) {

case PROGRESS_DIALOG:

progressDialog.setProgress(0);

progressThread = new ProgressThread(handler);

progressThread.start();

}

// Define the Handler that receives messages from the thread and update the progress

final Handler handler = new Handler() {

public void handleMessage(Message msg) {

int total = msg.arg1;

progressDialog.setProgress(total);

if (total >= 100){

dismissDialog(PROGRESS_DIALOG);

progressThread.setState(ProgressThread.STATE_DONE);

}

}

};

/** Nested class that performs progress calculations (counting) */

private class ProgressThread extends Thread {

Handler mHandler;

final static int STATE_DONE = 0;

final static int STATE_RUNNING = 1;

int mState;

int total;

ProgressThread(Handler h) {

mHandler = h;

}

public void run() {

mState = STATE_RUNNING;

total = 0;

while (mState == STATE_RUNNING) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

Log.e("ERROR", "Thread Interrupted");

}

Message msg = mHandler.obtainMessage();

msg.arg1 = total;

Page 166: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 166

mHandler.sendMessage(msg);

total++;

}

}

/* sets the current state for the thread,

* used to stop the thread */

public void setState(int state) {

mState = state;

}

}

}

Criando uma caixa de diálogo personalizada

Se você quiser um projeto personalizado para um

diálogo, você pode criar seu próprio layout para a

janela de diálogo com elementos de layout e de

widget. Depois de definido o layout, passar o objeto

View raiz ou identificação do recurso de layout para setContentView(View).

Por exemplo, para criar o diálogo mostrado acima:

1. Criar um layout XML salvo como custom_dialog.xml:

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

android:id="@+id/layout_root"

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:padding="10dp"

>

<ImageView android:id="@+id/image"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_marginRight="10dp"

/>

<TextView android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:textColor="#FFF"

/>

</LinearLayout>

Esta XML define um ImageView e um TextView dentro de um LinearLayout.

2. Definir o layout acima como o conteúdo da View da caixa de diálogo e definir o

conteúdo dos elementos ImageView e TextView:

Page 167: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 167

Context mContext = getApplicationContext();

Dialog dialog = new Dialog(mContext);

dialog.setContentView(R.layout.custom_dialog);

dialog.setTitle("Custom Dialog");

TextView text = (TextView) dialog.findViewById(R.id.text);

text.setText("Hello, this is a custom dialog!");

ImageView image = (ImageView) dialog.findViewById(R.id.image);

image.setImageResource(R.drawable.android);

Depois de instanciar o diálogo, definir o layout personalizado de conteúdo como

conteúdo da View da caixa de diálogo com setContentView(int), passando o ID

do recurso de layout. Agora que o diálogo tem um layout definido, você pode

capturar objetos View do layout com findViewById(int) e modificar seu

conteúdo.

3. É isso aí. Agora você pode mostrar o diálogo como descrito em Mostrando um

Diálogo.

Um diálogo feito com a classe de diálogo base deve ter um título. Se você não chamar

setTitle(), o espaço usado para o título continua vazio, mas ainda visível. Se você não

quer um título a todos, então você deve criar o seu diálogo personalizado usando a

classe AlertDialog. No entanto, porque um AlertDialog é mais fácilmente criado com o

AlertDialog.Builder, você não tem acesso ao método setContentView(int) utilizado

acima. Em vez disso, você deve usar setView(View). Este método aceita um objeto

View, por isso é necessário inflar o layout do objeto View da raiz do XML.

Para inflar o layout XML, recuperar o LayoutInflater com getLayoutInflater() (ou

getSystemService()), e depois chamar inflate(int, ViewGroup), onde o primeiro

parâmetro é o ID do recurso layout e o segundo é a identificação da View raiz. Neste

ponto, você pode usar o layout inflado para encontrar objetos View no layout e definir o

conteúdo dos elementos ImageView e TextView. Então instanciar o

AlertDialog.Builder e definir o layout inflados para o diálogo com setView(View).

Aqui está um exemplo, criando um layout personalizado em um AlertDialog:

AlertDialog.Builder builder;

AlertDialog alertDialog;

Context mContext = getApplicationContext();

LayoutInflater inflater = (LayoutInflater)

Page 168: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 168

mContext.getSystemService(LAYOUT_INFLATER_SERVICE);

View layout = inflater.inflate(R.layout.custom_dialog,

(ViewGroup) findViewById(R.id.layout_root));

TextView text = (TextView) layout.findViewById(R.id.text);

text.setText("Hello, this is a custom dialog!");

ImageView image = (ImageView) layout.findViewById(R.id.image);

image.setImageResource(R.drawable.android);

builder = new AlertDialog.Builder(mContext);

builder.setView(layout);

alertDialog = builder.create();

Usando um AlertDialog para o seu layout personalizado permite-lhe tirar partido das

funcionalidades incorporadas AlertDialog como botões geridos, listas selecionáveis, um

título, um ícone e assim por diante.

Page 169: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 169

Manipulando eventos de UI

No Android, há mais de um caminho para interceptar os eventos de interação do usuário

com seu aplicativo. Ao considerar os eventos dentro de sua interface de usuário, a

abordagem consiste em capturar os eventos do objeto View específico com que o

usuário interage. A classe View fornece os meios para fazê-lo.

Entre as diversas classes View que você usará para compor seu layout, você pode

observar vários métodos de retorno públicos que pareçam úteis para eventos de UI.

Esses métodos são chamados pelo framework Android, quando a respectiva ação ocorre

no objeto. Por exemplo, quando uma exibição (como um botão) é tocada, o método

onTouchEvent() é chamado no objeto. No entanto, a fim de interceptar isso, você deve

estender a classe e substituir o método. No entanto, estender cada objeto View, a fim de

lidar com um evento como esse não seria prático. É por isso que a classe View também

contém uma coleção de interfaces aninhadas com callbacks que podem ser muito mais

fácil de definir. Essas interfaces, chamadas de event listeners, são o seu bilhete para

capturar a interação do usuário com sua interface do usuário.

Enquanto você vai utilizar mais comumente os ouvintes de evento para receber a

interação do usuário, pode chegar um momento em que você quer estender uma classe,

no intuito de construir um componente personalizado. Talvez você queira estender a

classe Button para fazer algo mais extravagante. Neste caso, você será capaz de definir

o comportamento de eventos padrão para sua classe usando a classe de manipuladores

de eventos.

Os ouvintes de eventos

Um receptor de evento é uma interface na classe View que contém um método de

retorno único. Esses métodos serão chamados pelo framework Android quando o View

para o receptor tenha sido registado é desencadeada pela interação do usuário com o

item na interface do usuário.

Page 170: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 170

Incluído nas interfaces de ouvinte de evento são os métodos de retorno seguintes:

onClick()

A partir do View.OnClickListener. É chamado quando o usuário toca o item

(quando em modo de tocar), ou incide sobre o item com a navegação por teclas

ou trackball e pressiona a tecla "enter" ou pressiona o trackball.

onLongClick()

De View.OnLongClickListener. Isto é chamado quando o usuário toca e prende

o item (quando no modo de tocar), ou incide sobre o item com a navegação por

teclas ou trackball e pressiona e mantém a tecla "enter" ou pressiona e mantém

pressionada a trackball (por um segundo).

onFocusChange()

De View.OnFocusChangeListener. Isto é chamado quando o usuário navega

para ou longe do ponto, utilizando atalhos ou trackball.

onKey()

De View.OnKeyListener. Isto é chamado quando o usuário está centrado sobre o

item e pressiona ou solta uma tecla no dispositivo.

onTouch()

De View.OnTouchListener. Isto é chamado quando o usuário executa uma ação

qualificada como um evento de toque, incluindo pressionar, soltar, ou qualquer

movimento na tela (dentro dos limites do item).

onCreateContextMenu()

De View.OnCreateContextMenuListener. Isto é chamado quando um menu de

contexto está sendo construído (como o resultado de um "clique longo"

sustentado).

Esses métodos são os únicos habitantes da suas respectivas interfaces. Para definir um

desses métodos e lidar com seus eventos, implemente a interface aninhada em sua

Page 171: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 171

atividade ou defina-a como uma classe anônima. Em seguida, passe uma instância da

sua aplicação com os respectivos métodos View.set...Listener(). (Por exemplo, chamar

setOnClickListener() e passá-la a implementação do OnClickListener).

O exemplo abaixo mostra como registrar um receptor no clique de um botão.

// Create an anonymous implementation of OnClickListener

private OnClickListener mCorkyListener = new OnClickListener() {

public void onClick(View v) {

// do something when the button is clicked

}

};

protected void onCreate(Bundle savedValues) {

...

// Capture our button from layout

Button button = (Button)findViewById(R.id.corky);

// Register the onClick listener with the implementation above

button.setOnClickListener(mCorkyListener);

...

}

Você também pode achar mais conveniente para implementar OnClickListener como

parte de sua atividade. Isso irá evitar a carga horária extra e alocação de objetos. Por

exemplo:

public class ExampleActivity extends Activity implements OnClickListener {

protected void onCreate(Bundle savedValues) {

...

Button button = (Button)findViewById(R.id.corky);

button.setOnClickListener(this);

}

// Implement the OnClickListener callback

public void onClick(View v) {

// do something when the button is clicked

}

...

}

Observe que a chamada de onClick()no exemplo acima não tem valor de retorno, mas

alguns métodos ouvintes devem retornar um boolean. A razão depende do evento. Para

os poucos que o fazem, aqui está o porquê:

onLongClick() - Retorna um booleano para indicar se você tem consumido o

evento e que não deve ser levado adiante. Ou seja, retornar true para indicar que

você tem tratado o evento e deve parar por aqui; retornar false se você não tem

Page 172: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 172

lidado com isso e/ou o evento deve continuar para qualquer outro receptor on-

click.

onKey() - Retorna um booleano para indicar se você tem consumido o evento e

que não deve ser levada adiante. Ou seja, retornar true para indicar que você tem

tratado o evento e deve parar por aqui; retornar false se você não tem lidado com

isso e/ou o evento deve continuar a todo ouvinte on-key.

onTouch() - Retorna um booleano para indicar se o ouvinte consome este

evento. O importante é que este evento pode ter várias ações que se sucedem.

Então, se você retornar falso quando o evento de ação abaixo é recebido, você

indica que não consumiram o evento e também não estão interessados em ações

subseqüentes deste evento. Assim, você não será chamado para outras ações

dentro do evento, como um gesto do dedo, ou um eventual evento de ação

acima.

Lembre-se que os principais eventos são sempre entregues para a corrente View em

foco. Eles são enviados a partir do topo da hierarquia de View e, em seguida, para

baixo, até chegar ao destino apropriado. Se a sua view (ou filho de sua view) atualmente

tem o foco, então você pode ver o curso de eventos através do método

dispatchKeyEvent(). Como alternativa à captura de eventos-chave através da sua view,

você também pode receber todos os eventos dentro de sua atividade com onKeyDown()

e onKeyUp().

Nota: O Android vai chamar os manipuladores de eventos e depois os manipuladores

padrão apropriados a partir da definição de segunda classe. Como tal, retornando true

destes ouvintes de evento irá parar a propagação do evento para ouvintes de eventos e

também irá bloquear o retorno de chamada para o manipulador de eventos padrão no

View. Então, tenha a certeza de que deseja encerrar o caso quando você retornar true.

Manipuladores de eventos

Se você está construindo um componente personalizado de view, então você vai ser

capaz de definir vários métodos de retorno usados como manipuladores de eventos

padrão. No documento Construindo Componentes Personalizados, você aprenderá a ver

alguns dos retornos comuns usados para tratamento de eventos, incluindo:

Page 173: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 173

onKeyDown(int, KeyEvent) - Chamado quando um evento de nova tecla

ocorre.

onKeyUp(int, KeyEvent) - Chamado quando um evento de tecla para cima

ocorre.

onTrackballEvent(MotionEvent) - Chamado quando um evento de movimento

de trackball ocorre.

onTouchEvent(MotionEvent) - Chamado quando um evento do movimento da

tela de toque ocorre.

onFocusChanged(boolean, int, Rect) - Chamado quando a view ganha ou

perde o foco.

Existem alguns outros métodos que você deve estar ciente de que não são parte da

classe View, mas podem impactar diretamente a forma como você é capaz de manipular

eventos. Portanto, ao gerenciar eventos mais complexos dentro de um layout, considere

estes outros métodos:

Activity.dispatchTouchEvent(MotionEvent) - Isso permite que sua atividade

possa interceptar todos os eventos de toque antes de serem enviados para a

janela.

ViewGroup.onInterceptTouchEvent(MotionEvent) - Isso permite que um

ViewGroup possa assistir a eventos como eles são distribuídos aos views filhos.

ViewParent.requestDisallowInterceptTouchEvent(boolean) - Chamar esta

em cima de um pai view para indicar que ela não deve interceptar eventos de

contato com onInterceptTouchEvent(MotionEvent) .

Modo de toque

Quando um usuário está navegando uma interface de usuário com as teclas direcionais

ou um trackball, é necessário dar atenção aos itens de recurso (como botões) que o

usuário possa ver o que vai aceitar a entrada. Se o dispositivo tem capacidades de toque,

no entanto, o usuário começa a interagir com a interface ao tocá-lo, então ele não é mais

Page 174: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 174

necessário para destacar itens, ou dar enfoque a uma visão particular. Assim, existe um

modo de interação com o nome "modo de toque."

Para um dispositivo sensível ao toque, uma vez que o usuário toca a tela, o aparelho

entra em modo de tocar. Deste ponto em diante, somente as views em que o

isFocusableInTouchMode() está true poderão ser focadas, como os widgets de edição

de texto. Outros views que são palpáveis, como botões, não vão tirar o foco quando

tocado; eles vão simplesmente focar seus ouvintes com um clique, quando pressionados.

Toda vez que um usuário pressiona uma tecla direcional ou rola com uma trackball, o

aparelho sairá do modo de tocar, e encontrar um visual de tirar o foco. Agora, o usuário

pode continuar interagindo com a interface do usuário sem tocar na tela.

O estado modo de tocar é mantido ao longo de todo o sistema (todas as janelas e

atividades). Para consultar o estado atual, você pode chamar isInTouchMode() para ver

se o dispositivo está no modo de tocar.

Manipulação do foco

O framework vai lidar com as rotinas de movimento do foco em resposta à entrada do

usuário. Isso inclui a mudança do foco nas views que são removidas ou escondidas, ou

quando as views novos se tornam disponíveis. Views indicam a sua disponibilidade para

tirar o foco através do método isFocusable(). Para definir se uma View pode tirar o

foco, chame setFocusable(). Quando em modo de tocar, você pode consultar se uma

View permite focar com isFocusableInTouchMode(). Você pode mudar isso com

setFocusableInTouchMode().

Movimento do foco é baseado em um algoritmo que encontra o vizinho mais próximo

em uma determinada direção. Em casos raros, o algoritmo padrão pode não coincidir

com o comportamento desejado para o desenvolvedor. Nessas situações, você pode

fornecer substituições explícitas com os atributos XML a seguir no arquivo de layout:

nextFocusDown, nextFocusLeft, nextFocusRight, e nextFocusUp. Adicione um desses

atributos para o View a partir do qual o foco está saindo. Defina o valor do atributo a

ser o ID do view para quem o foco deve ser dado. Por exemplo:

Page 175: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 175

<LinearLayout

android:orientation="vertical"

... >

<Button android:id="@+id/top"

android:nextFocusUp="@+id/bottom"

... />

<Button android:id="@+id/bottom"

android:nextFocusDown="@+id/top"

... />

</LinearLayout>

Normalmente, neste layout vertical, navegando a partir do primeiro botão não leva a

lugar nenhum, nem iria navegar abaixo do segundo botão. Agora que o botão superior

foi definido como um fundo com o nextFocusUp (e vice-versa), o foco de navegação irá

circular de cima para baixo e de baixo para cima.

Se você gostaria de declarar o view como passível de foco em sua interface do usuário

(quando não é tradicional), adicione o atributo XML android:focusable para a view, na

sua declaração de layout. Defina o valor true. Você pode também declarar uma view

como passível de foco enquanto em modo de toque com

android:focusableInTouchMode.

Para solicitar uma exibição especial para ter foco, chame requestFocus().

Para ouvir os eventos de foco (ser notificado quando um view recebe ou perde foco),

use onFocusChange().

Page 176: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 176

Notificar o usuário

Vários tipos de situações podem surgir que requerem a notificação do usuário sobre um

evento que ocorre em sua aplicação. Alguns eventos requerem que o usuário responda e

outros não. Por exemplo:

Quando um evento como salvar um arquivo for concluída, uma pequena

mensagem deve aparecer para confirmar que o salvamento foi bem sucedido.

Se o aplicativo é executado em segundo plano e requer atenção do usuário, o

aplicativo deve criar uma notificação que permite ao usuário responder a

conveniência dele ou dela.

Se o aplicativo está executando o trabalho que o usuário deve aguardar (como

carregar um arquivo), o aplicativo deve mostrar uma roda de progresso pairando

ou uma barra.

Cada uma dessas tarefas de notificação podem ser conseguidas usando uma técnica

diferente:

Uma notificação brinde, por breves mensagens que vêm do fundo.

Uma notificação na barra de status, lembretes persistentes que vêm do fundo e

solicitam resposta do usuário.

Uma notificação de diálogo, para as notificações de atividades relacionadas.

Notificação brinde

Uma notificação brinde é uma mensagem que aparece

na superfície da janela. Ela só enche o espaço

necessário para a mensagem e a atividade atual do

usuário permanece visível e interativa. A notificação

automaticamente desaparece, e não aceita eventos de

interação. Como um brinde pode ser criado a partir de um serviço em background,

aparece mesmo que o aplicativo não esteja visível.

Page 177: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 177

Um brinde é melhor para mensagens de texto curtas, como "Arquivo salvo", quando

você está bastante certo de que o usuário está prestando a atenção na tela. Um brinde

não pode aceitar eventos de interação do usuário, se você gostaria que o usuário

respondesse e agisse, considere usar uma notificação na barra de status.

Criando notificações brinde

A imagem abaixo mostra uma notificação brinde de exemplo da aplicação do alarme.

Uma vez que um alarme é ativado, um brinde é exibido para garantir que o alarme foi

ajustado.

Um brinde pode ser criado e exibido a partir de uma

Activity ou Service. Se você criar uma notificação de

brinde de um serviço, ele aparece na frente da

atividade atualmente em foco.

Se a resposta do usuário com a notificação é exigida,

considere usar uma notificação na barra de status.

O Básico

Primeiro, instanciar um objeto Toast com um dos métodos makeText(). Este método usa

três parâmetros: a aplicação Context, a mensagem de texto e a duração para o brinde.

Ele retorna um objeto Toast inicializado corretamente. Você pode exibir a notificação

brinde com show(), como mostrado no exemplo a seguir:

Context context = getApplicationContext();

CharSequence text = "Hello toast!";

int duration = Toast.LENGTH_SHORT;

Toast toast = Toast.makeText(context, text, duration);

toast.show();

Este exemplo demonstra tudo o que precisa para a maioria das notificações brinde.

Raramente é necessário algo a mais. Você pode, no entanto, querer a posição do brinde

diferente ou até mesmo usar o seu próprio layout, em vez de uma simples mensagem de

texto. As seções a seguir descrevem como você pode fazer essas coisas.

Page 178: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 178

Posicionar o seu brinde

Uma notificação brinde padrão aparece perto da parte inferior da tela, centralizado

horizontalmente. Você pode alterar esta posição com o método setGravity(int, int, int).

Este aceita três parâmetros: a constante Gravity, um deslocamento da posição x e um

deslocamento da posição y.

Por exemplo, se você decidir que o brinde deve aparecer no canto superior esquerdo,

você pode definir a gravidade como este:

toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);

Se você quiser deslocar a posição para a direita, aumente o valor do segundo parâmetro.

Para empurrá-lo para baixo, aumente o valor do último parâmetro.

Criando uma exibição personalizada brinde

Se uma mensagem de texto simples não é suficiente,

você pode criar um layout personalizado para a sua

notificação brinde. Para criar um layout personalizado,

definir um layout de view, em XML ou no código do

aplicativo, e passar a View raiz para o método

setView(View).

Por exemplo, você pode criar o layout para o brinde

visível na imagem à esquerda, com o seguinte XML

(salvo como toast_layout.xml):

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

android:id="@+id/toast_layout_root"

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:padding="10dp"

android:background="#DAAA"

>

<ImageView android:id="@+id/image"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_marginRight="10dp"

/>

<TextView android:id="@+id/text"

Page 179: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 179

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:textColor="#FFF"

/>

</LinearLayout>

Observe que a identificação do elemento LinearLayout é "toast_layout". Você deve usar

essa identificação para inflar o layout do XML, como mostrado aqui:

LayoutInflater inflater = getLayoutInflater();

View layout = inflater.inflate(R.layout.toast_layout,

(ViewGroup) findViewById(R.id.toast_layout_root));

ImageView image = (ImageView) layout.findViewById(R.id.image);

image.setImageResource(R.drawable.android);

TextView text = (TextView) layout.findViewById(R.id.text);

text.setText("Hello! This is a custom toast!");

Toast toast = new Toast(getApplicationContext());

toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);

toast.setDuration(Toast.LENGTH_LONG);

toast.setView(layout);

toast.show();

Primeiro, recupere o LayoutInflater com getLayoutInflater() (ou getSystemService()), e

depois infle o layout de XML usando inflate(int, ViewGroup). O primeiro parâmetro é o

ID do recurso layout e o segundo é a View raiz. Você pode usar esse layout inflado para

encontrar mais objetos view no layout, agora capturar e definir o conteúdo dos

elementos ImageView e TextView. Finalmente, crie um novo brinde com

Toast(Context) e defina algumas propriedades do brinde, tais como a gravidade e

duração. Em seguida, chame setView(View) e passe seu layout inflado. Agora você

pode exibir o brinde com o seu layout personalizado, chamando show().

Nota: Não use o construtor público para um brinde, a menos que vá definir o layout

com setView(View). Se você não tem um layout personalizado para o uso, você deve

usar makeText(Context, int, int) para criar o Toast.

Notificação na barra de status

Uma notificação na barra de status adiciona um ícone para

barra de status do sistema (com opcionais relógio/mensagem

de texto) e uma mensagem expandida na janela

"Notificações". Quando o usuário seleciona a mensagem

Page 180: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 180

expandida, o Android aciona uma Intent que é definida pela notificação (geralmente

para lançar uma Activity). Você também pode configurar a notificação para alertar o

usuário com um som, uma vibração e as luzes piscando no dispositivo.

Este tipo de notificação é ideal quando o aplicativo está funcionando em um serviço de

fundo e há necessidade de notificar o usuário sobre um evento. Se você precisa alertar o

usuário sobre um evento que ocorre durante a sua atividade que ainda está em foco,

considere usar uma notificação de diálogo em vez disso.

Criação de notificações da barra de status

Uma notificação na barra de status deve ser usada para qualquer caso em que um

serviço de background precisa alertar o usuário sobre um evento que exige uma

resposta. Um serviço de fundo nunca deve iniciar uma atividade por conta própria a fim

de receber a interação do usuário. O serviço deveria criar uma notificação na barra de

status que vai lançar a atividade quando selecionado pelo usuário.

A imagem abaixo mostra a barra de status com um ícone de notificação no lado

esquerdo.

A imagem seguinte mostra mensagem expandida de notificação na janela

"Notificações". O usuário pode visualizar a janela de notificações, puxando para baixo a

barra de status (ou selecionando Notificações no menu de opções da Home).

O Básico

Uma Activity ou Service pode iniciar uma notificação na barra de status. Porque uma

atividade pode executar ações somente quando ela está ativa e em foco, você deve criar

suas notificações de um serviço. Desta forma, a notificação pode ser criada a partir do

Page 181: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 181

fundo, enquanto o usuário está usando outra aplicação ou quando o dispositivo estiver

dormindo. Para criar uma notificação, você deve usar duas classes: Notification e

NotificationManager.

Use uma instância da classe Notification para definir as propriedades de sua notificação

na barra de status, como o ícone da barra de status, a mensagem expandida e

configurações extras, como um som para tocar. O NotificationManager é um serviço do

sistema Android que executa e gerencia todas as notificações. Você não pode instanciar

o NotificationManager. A fim de dar-lhe a sua notificação, você deve recuperar uma

referência para o NotificationManager com getSystemService() e então, quando você

quer notificar o usuário, passá-lo com seu objeto de notificação notify().

Para criar uma notificação de barra de status:

1. Obtenha uma referência para o NotificationManager:

String ns = Context.NOTIFICATION_SERVICE;

NotificationManager mNotificationManager = (NotificationManager)

getSystemService(ns);

2. Instanciar a notificação:

int icon = R.drawable.notification_icon;

CharSequence tickerText = "Hello";

long when = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, when);

3. Definir mensagem expandida e intenção da notificação:

Context context = getApplicationContext();

CharSequence contentTitle = "My notification";

CharSequence contentText = "Hello World!";

Intent notificationIntent = new Intent(this, MyClass.class);

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

4. Passe a notificação ao NotificationManager:

private static final int HELLO_ID = 1;

mNotificationManager.notify(HELLO_ID, notification);

É isso aí. Seu usuário já foi notificado.

Page 182: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 182

Gerenciando suas notificações

O NotificationManager é um serviço do sistema que gerencia todas as notificações.

Você deve obter uma referência a ele com o método getSystemService(). Por exemplo:

String ns = Context.NOTIFICATION_SERVICE;

NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);

Quando você quiser enviar sua notificação na barra de status, passar o objeto de

notificação ao NotificationManager com notify(int, Notification). O primeiro parâmetro

é o ID único para a notificação e o segundo é o objeto de notificação. O ID identifica a

notificação a partir da sua aplicação. Isso é necessário se você precisa atualizar a

notificação (se o aplicativo gerencia diferentes tipos de notificações) ou selecionar a

ação apropriada quando o usuário retornar para a sua aplicação através da intenção

definida na notificação.

Para apagar a notificação de barra de status quando o usuário seleciona a partir da janela

de notificações, adicione a flag "FLAG_AUTO_CANCEL" de seu objeto de

notificação. Você também pode limpar manualmente com cancel(int), passando-lhe a

identificação ou notificação ou limpar todas as suas notificações com cancelAll().

Criando uma notificação

Um objeto Notification define os detalhes da mensagem de notificação que é exibida na

barra de status e janela de "Notificações", e qualquer alerta de outras configurações, tais

como sons e luzes piscando.

Uma notificação de barra de status exige o seguinte:

Um ícone da barra de status

Uma mensagem expandida e título para o modo expandido (a menos que você

defina uma exibição personalizada ampliada)

Um PendingIntent, para ser acionado quando a notificação for selecionada

As configurações opcionais para a notificação de barra de status incluem:

Uma mensagem de texto-relógio para a barra de status

Um som de alerta

Page 183: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 183

Uma configuração vibrar

Uma definição LED piscando

O kit de arranque para uma nova notificação inclui o construtor Notification(int,

CharSequence, long) e o método setLatestEventInfo(Context, CharSequence,

CharSequence, PendingIntent). Estes definem todas as definições necessárias para uma

notificação. O trecho a seguir demonstra a configuração de notificação de base:

int icon = R.drawable.notification_icon; // icon from resources

CharSequence tickerText = "Hello"; // ticker-text

long when = System.currentTimeMillis(); // notification time

Context context = getApplicationContext(); // application Context

CharSequence contentTitle = "My notification"; // expanded message title

CharSequence contentText = "Hello World!"; // expanded message text

Intent notificationIntent = new Intent(this, MyClass.class);

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

// the next two lines initialize the Notification, using the configurations above

Notification notification = new Notification(icon, tickerText, when);

notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

Atualizando a notificação

Você pode atualizar as informações em sua notificação de barra de status como eventos

que continuam a ocorrer em seu aplicativo. Por exemplo, quando uma nova mensagem

de texto SMS chega antes que as mensagens anteriores foram lidas, o aplicativo de

mensagens atualiza as notificações existentes para exibir o número total de novas

mensagens recebidas. Esta prática, de uma atualização de notificação existente é muito

melhor do que a adição de novas notificações à NotificationManager porque evita a

desordem na janela de notificações.

Porque cada notificação é unicamente identificada pelo NotificationManager com um

número de identificação inteiro, você pode revisar a notificação chamando

setLatestEventInfo() com novos valores, mudar alguns valores de campo da notificação,

e depois chamar notify() novamente.

Você pode rever cada propriedade com os campos de membro de objeto (exceto para o

contexto e no título da mensagem expandida e texto). Você deve sempre revisar a

mensagem de texto quando você atualizar a notificação chamando setLatestEventInfo()

com novos valores para contentTitle e contentText. Em seguida, chamar notify() para

Page 184: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 184

atualizar a notificação. (Claro, se você criou uma exibição personalizada expandida,

atualizar esses valores de texto e título não terá efeito)

Adicionando um som

Você pode alertar o usuário com o som de notificação padrão (que é definido pelo

usuário) ou com um som especificado pelo seu aplicativo.

Para utilizar o usuário padrão do som, adicione "DEFAULT_SOUND" para o campo de

padrões:

notification.defaults |= Notification.DEFAULT_SOUND;

Para usar um som diferente com as suas notificações, passe uma referência URI para o

campo de som. O exemplo a seguir usa um arquivo de áudio conhecido gravado no

cartão SD do dispositivo:

notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");

No próximo exemplo, o arquivo de áudio é escolhido do MediaStore 's ContentProvider:

notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");

Neste caso, a identificação exata do arquivo de mídia ("6") é conhecido e anexado ao

conteúdo Uri. Se você não souber a identificação exata, você deve consultar todos os

meios disponíveis no MediaStore com ContentResolver.

Se você deseja que o som repeta continuamente até que o usuário responda à notificação

ou a notificação seja cancelada, adicione "FLAG_INSISTENT" para o campo de

sinalizadores.

Nota: Se o campo padrão inclui "DEFAULT_SOUND", então o som padrão substitui

qualquer som definido pelo campo de som.

Adicionando vibração

Você pode alertar o usuário com o modelo padrão de vibração ou com um modelo de

vibração definido pelo seu aplicativo.

Para usar o modelo padrão, adicione "DEFAULT_VIBRATE" para o campo de

padrões:

Page 185: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 185

notification.defaults |= Notification.DEFAULT_VIBRATE;

Para definir o seu padrão de vibração própria, passe uma matriz de valores longos para o

campo vibrar:

long[] vibrate = {0,100,200,300};

notification.vibrate = vibrate;

O longo array define o padrão alternado para o comprimento de vibração e desligando

(em milissegundos). O primeiro valor é o tempo de espera (desligado) antes de começar,

o segundo valor é o comprimento da primeira vibração, o terceiro é o comprimento da

próxima, e assim por diante. O padrão pode ser tão longo quanto queira, mas não pode

ser configurado para repetir.

Nota: Se o campo padrão inclui "DEFAULT_VIBRATE", então a vibração padrão

substitui qualquer vibração definida pelo campo vibrate.

Adicionando luzes a piscar

Para alertar o usuário com luzes LED, você pode implementar o modelo de luz padrão

(se disponível), ou definir sua própria cor e padrão para as luzes.

Para usar a configuração padrão de luz, acrescentar "DEFAULT_LIGHTS" para o

campo de padrões:

notification.defaults |= Notification.DEFAULT_LIGHTS;

Para definir sua própria cor e padrão, definir um valor para o campo ledARGB (para a

cor), o campo ledOffMS (período de tempo, em milésimos de segundo, para manter a

luz apagada), o ledOnMS (período de tempo, em milissegundos, para manter a luz

acesa), e também adicionar "FLAG_SHOW_LIGHTS" para o campo flags:

notification.ledARGB = 0xff00ff00;

notification.ledOnMS = 300;

notification.ledOffMS = 1000;

notification.flags |= Notification.FLAG_SHOW_LIGHTS;

Neste exemplo, a luz verde pisca várias vezes a cada 300 milésimos de segundo e

desliga por um segundo. Nem todas as cores no espectro são suportadas pelo dispositivo

de LEDs, e não é todo dispositivo que suporta as mesmas cores, então o hardware

estima para o melhor de sua capacidade. Verde é a cor mais comum de notificação.

Page 186: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 186

Mais recursos

Você pode adicionar várias características a mais às suas notificações usando campos de

notificação e flags. Algumas características úteis incluem o seguinte:

"FLAG_AUTO_CANCEL"

Adicione isto ao campo de sinalizadores para cancelar automaticamente a

notificação depois que ela é selecionada a partir da janela de notificações.

"FLAG_INSISTENT"

Adicione isto ao campo de sinalizadores para repetir o áudio até que o usuário

responda.

"FLAG_ONGOING_EVENT"

Adicione isto ao campo de sinalizadores para agrupar notificações ao abrigo do

título "em curso" da janela de notificações. Isso indica que o aplicativo está em

curso - seus processos ainda estão em execução em segundo plano, mesmo

quando o aplicativo não é visível (como a música ou uma chamada de telefone).

"FLAG_NO_CLEAR"

Adicione isto ao campo de sinalizadores para indicar que a notificação não deve

ser limpa pelo botão "Limpar notificações". Isso é particularmente útil se a sua

notificação está em curso.

número

Esse valor indica o número atual de eventos representados pela notificação. O

número apropriado é sobreposto em cima do ícone da barra de status. Se você

pretende usar esse campo, então você deve começar com "1" quando a

notificação for criado pela primeira vez. (Se você alterar o valor de zero para

qualquer coisa maior durante uma atualização, o número não é mostrado.)

Page 187: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 187

iconLevel

Esse valor indica o nível atual de um LevelListDrawable que é usado para o

ícone de notificação. Você pode animar o ícone na barra de status, alterando esse

valor para correlacionar com os drawable é definido em um LevelListDrawable.

Criar uma exibição personalizada expandida

Por padrão, o modo expandido utilizado na janela

"Notificações" inclui um título de base e mensagem de

texto. Estes são definidos por parâmetros contentText e

contentTitle do método setLatestEventInfo(). No

entanto, você também pode definir um layout

personalizado para o modo de exibição expandido

usando RemoteViews. A imagem à direita mostra um exemplo de uma exibição

personalizada alargada, que usa um ImageView e TextView em LinearLayout.

Para definir seu próprio layout para a mensagem expandida, instancie um objeto

RemoteViews e passe-o para o campo contentView da sua notificação. Passe o

PendingIntent ao campo contentIntent.

Criar uma exibição personalizada expandido é mais bem entendido com um exemplo:

1. Criar o layout XML para a exibição expandida. Por exemplo, crie um arquivo

chamado layout custom_notification_layout.xml e construa-o assim:

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

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:padding="3dp"

>

<ImageView android:id="@+id/image"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_marginRight="10dp"

/>

<TextView android:id="@+id/text"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:textColor="#000"

/>

</LinearLayout>

Page 188: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 188

Este esquema é utilizado para a visualização expandida, mas o conteúdo do

ImageView e TextView ainda precisam ser definidos pelo aplicativo.

RemoteViews oferece alguns métodos adequados que lhe permitem definir o

conteúdo...

2. No código do aplicativo, use os métodos RemoveViews para definir a imagem e

texto. Em seguida, passe o objeto RemoteViews ao campo contentView da

notificação, conforme mostrado neste exemplo:

RemoteViews contentView = new RemoteViews(getPackageName(),

R.layout.custom_notification_layout);

contentView.setImageViewResource(R.id.image, R.drawable.notification_image);

contentView.setTextViewText(R.id.text, "Hello, this message is in a custom expanded

view");

notification.contentView = contentView;

Como mostrado aqui, passe o nome do aplicativo do pacote e a identificação de

recursos de layout para o construtor RemoteViews. Em seguida, defina o

conteúdo para o ImageView e TextView, usando o setImageViewResource() e

setTextViewText(). Em cada caso, passe o ID de referência do objeto View

conveniente que você deseja definir, juntamente com o valor para essa visão.

Finalmente, o objeto RemoteViews é passado para a notificação no âmbito

contentView.

3. Porque você não precisa do método setLatestEventInfo() quando usando uma

exibição personalizada, você deve definir as intenções para a notificação com o

campo contentIntent, como neste exemplo:

Intent notificationIntent = new Intent(this, MyClass.class);

PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.contentIntent = contentIntent;

4. A notificação pode ser enviada, como de costume:

mNotificationManager.notify(CUSTOM_VIEW_ID, notification);

A classe RemoteViews também inclui métodos que você pode usar para facilmente

adicionar um Chronometer ou ProgressBar na view expandida da notificação. Para obter

mais informações sobre como criar layouts personalizados com RemoteViews, consulte

o RemoteViews.

Page 189: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 189

Nota: Ao criar uma view expandida customizada, você deve tomar cuidado especial

para garantir que as funções personalizadas de seu layout haja corretamente no

dispositivo em orientações e resoluções diferentes. Enquanto este conselho se aplica a

todos os layouts view criados no Android, é especialmente importante neste caso

porque o seu layout de propriedade real é muito restrito. Portanto, não faça o seu

layout personalizado demasiado complexo e não se esqueça de testá-lo em várias

configurações.

Notificação de diálogo

Um diálogo é geralmente uma pequena janela que

aparece na frente da atividade atual. A atividade perde

o foco e o diálogo aceita a interação do usuário. Os

diálogos são normalmente utilizados para notificações

e atividades curtas que se relacionem diretamente com a aplicação em curso.

Você deve usar uma caixa de diálogo quando você precisa mostrar uma barra de

progresso ou uma mensagem curta que requer a confirmação do usuário (como um

alerta com "OK" e "Cancelar"). Você pode usar também as janelas como parte

integrante na interface do aplicativo e para outros fins além de notificações. Para uma

discussão completa sobre todos os tipos disponíveis de diálogos, incluindo seus usos

para as notificações, consulte Criando caixas de diálogo.

Page 190: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 190

Aplicando estilos e temas

Um estilo é um conjunto de propriedades que especificam o aspecto e formato de um

View ou janela. Um estilo pode especificar propriedades como altura, padding, cor de

fonte, tamanho de fonte, cor de fundo e muito mais. Um estilo é definido em um recurso

de XML que é separado do XML que especifica o layout.

Estilos em Android compartilham uma filosofia semelhante à de estilo em cascata em

web-design que permitem a você separar o design do conteúdo.

Por exemplo, usando um estilo, você pode ter este layout XML:

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:textColor="#00FF00"

android:typeface="monospace"

android:text="@string/hello" />

E transformar nisso:

<TextView

style="@style/CodeFont"

android:text="@string/hello" />

Todos os atributos relacionados com o estilo foram removidos do layout XML e

colocado em uma definição de estilo chamado CodeFont, e depois aplicado com o

atributo style. Você verá a definição para esse estilo na seção seguinte.

Um tema é um estilo aplicado a toda uma Activity ou aplicação, ao invés de um

indivíduo View (como no exemplo acima). Quando um estilo é aplicado como um tema,

a cada view da atividade ou da aplicação serão aplicados cada propriedade de estilo que

ele suporta. Por exemplo, você pode aplicar o mesmo estilo CodeFont como um tema

para uma atividade e, em seguida, todo o texto dentro dessa atividade deverá ter fonte

monoespaçada verde.

Definição de estilos

Para criar um conjunto de estilos, salve um arquivo XML no diretório res/values/ do seu

projeto. O nome do arquivo XML é arbitrário, mas deve usar a extensão .xml e ser salvo

na pasta res/values/.

O nó raiz do arquivo XML deve ser <resources>.

Para cada estilo que você quer criar, adicione um elemento <style> para o arquivo com

um nome que identifica o estilo (este atributo é obrigatório). Em seguida, adicione um

elemento <item> para cada propriedade desse estilo, com um nome que declara a

Page 191: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 191

propriedade de estilo e um valor para ir com ele (este atributo é obrigatório). O valor

para o <item> pode ser uma seqüência de palavras-chave, uma cor hexadecimal, uma

referência a outro tipo de recurso, ou outro valor, dependendo da propriedade de estilo.

Aqui está um exemplo de arquivo com um estilo único:

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

<resources>

<style name="CodeFont" parent="@android:style/TextAppearance.Medium">

<item name="android:layout_width">fill_parent</item>

<item name="android:layout_height">wrap_content</item>

<item name="android:textColor">#00FF00</item>

<item name="android:typeface">monospace</item>

</style>

</resources>

Cada filho do elemento <resources> é convertido em um objeto de recurso do aplicativo

em tempo de compilação, que pode ser referenciado pelo valor do atributo name do

elemento <style>. Este estilo de exemplo pode ser referenciado a partir de um layout

XML como @style/CodeFont (como demonstrado na introdução acima).

O atributo parent no elemento <style> é opcional e especifica o ID do recurso de um

outro estilo a partir do qual este estilo deve herdar propriedades. Você pode, então,

substituir as propriedades de estilo herdado se você quiser.

Lembre-se, um estilo que você deseja usar como uma atividade ou tema de aplicação é

definido em XML exatamente do mesmo jeito que um estilo para uma view. Um estilo,

como o definido acima pode ser aplicado como um estilo para uma única view ou como

um tema para uma atividade ou aplicação inteira. Como aplicar um estilo para uma

visão única ou como um tema de aplicação é discutido mais tarde.

Herança

O atributo parent no elemento <style> permite que você especifique um estilo a partir

do qual o seu estilo deve herdar propriedades. Você pode usar isso para herdar

propriedades de um estilo existente e, em seguida, definir apenas as propriedades que

deseja alterar ou acrescentar. Você pode herdar de estilos que você criou para si mesmo

ou de diferentes estilos que estão construídos na plataforma (Veja Usando estilos e

temas da plataforma abaixo, para obter informações sobre herança de estilos definidos

pela plataforma Android). Por exemplo, você pode herdar a aparência do texto padrão

da plataforma Android e, em seguida, modificá-lo:

Page 192: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 192

<style name="GreenText" parent="@android:style/TextAppearance">

<item name="android:textColor">#00FF00</item>

</style>

Se você quer herdar os estilos que você definiu para si mesmo, você não tem que usar o

atributo parent. Em vez disso, apenas preceda o nome do estilo que você quer herdar ao

nome do seu novo estilo, separados por um período. Por exemplo, para criar um novo

estilo que herda o estilo CodeFont definido anteriormente, mas fazer a cor vermelha,

você pode criar o novo estilo como este:

<style name="CodeFont.Red">

<item name="android:textColor">#FF0000</item>

</style>

Observe que não há atributo parent na tag <style>, mas porque o atributo name começa

com a CodeFont nome do estilo (que é um estilo que você criou), este estilo herda todas

as propriedades de estilo a partir desse estilo. Este estilo, em seguida, substitui a

propriedade android:textColor para tornar o texto vermelho. Você pode fazer referência

a este novo estilo como @style/CodeFont.Red.

Você pode continuar herdando assim tantas vezes quanto quiser, encadeando os nomes

com os períodos. Por exemplo, você pode estender CodeFont.Red ser maior, com:

<style name="CodeFont.Red.Big">

<item name="android:textSize">30sp</item>

</style>

Este herda de ambos os estilos CodeFont e CodeFont.Red, em seguida, adiciona a

propriedade android:textSize.

Nota: Essa técnica de herança por encadeando de nomes só funciona para estilos

definidos por seus próprios recursos. Você não pode herdar estilos internos do Android

desta forma. Para fazer referência a um estilo incorporado, como TextAppearance,

você deve usar o atributo parent.

Propriedades do estilo

Agora que você entende como um estilo é definido, é preciso saber que tipo de

propriedades de estilo definidas pelo <item> estão disponíveis. Você provavelmente

está familiarizado com alguns já, como layout_width e textColor. Claro, há muito mais

propriedades de estilo que você pode usar.

Page 193: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 193

O melhor lugar para encontrar propriedades que se aplicam a um determinado View é a

referência de classe correspondente, que lista todos os atributos XML que são

suportados. Por exemplo, todos os atributos listados na tabela de atributos XML

TextView podem ser usados em uma definição de estilo para um elemento TextView

(ou uma de suas subclasses). Um dos atributos listados na referência é

android:inputType, então onde você normalmente poderia colocar o atributo

android:inputType em um elemento <EditText>, assim:

<EditText

android:inputType="number"

... />

Você pode em vez disso criar um estilo para o elemento EditText que inclua esta

propriedade:

<style name="Numbers">

<item name="android:inputType">number</item>

...

</style>

Portanto, o seu XML para o esquema pode agora aplicar este estilo:

<EditText

style="@style/Numbers"

... />

Este exemplo simples pode parecer dar mais trabalho, mas quando você adiciona mais

propriedades de estilo e fatores na possibilidade de voltar a usar o estilo em vários

lugares, o custo-beneficio pode ser enorme.

Para uma referência de todas as propriedades de estilo disponíveis, consulte a referência

R.attr. Tenha em mente que todos os objetos view não aceitam todos os atributos de

mesmo estilo, então você deve, normalmente, referenciar ao específico View para as

propriedades de estilo suportadas. Entretanto, se você aplicar um estilo a uma exibição

que não suporta todas as propriedades de estilo, o view irá aplicar apenas as

propriedades que são suportadas e simplesmente ignorar os outros.

Algumas propriedades de estilo, no entanto, não são suportadas por qualquer elemento

View e só pode ser aplicado como um tema. Estas propriedades de estilo se aplicam a

toda a janela e não a qualquer tipo de view. Por exemplo, propriedades de estilo para um

tema podem ocultar o título do aplicativo, ocultar a barra de status, ou mudar o fundo da

Page 194: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 194

janela. Estes tipos de propriedades de estilo não pertencem a nenhum objeto View. Para

descobrir essas propriedades de estilo theme-only, veja o R.attr de referência para os

atributos que começam com window. Por exemplo, windowNoTitle e

windowBackground são propriedades de estilo que só são eficazes quando o estilo é

aplicado como um tema para uma atividade ou aplicação. Consulte a próxima seção

para informações sobre como aplicar um estilo como um tema.

Nota: Não se esqueça de prefixo dos nomes das propriedades em cada elemento

<item> com o android: namespace. Por exemplo: <item name="android:inputType">.

Aplicando estilos e temas para a interface do usuário

Há duas maneiras de definir um estilo:

Para uma view individual, adicione o atributo style a um elemento view no

XML para seu layout.

Ou, para uma atividade inteira ou uma aplicação, adicione o atributo

android:theme ao elemento <activity> ou <application> no manifesto do

Android.

Quando você aplica um estilo a uma única View no layout, as propriedades definidas

pelo estilo são aplicadas somente ao View. Se um estilo é aplicado a um ViewGroup, a

criança do elemento View não herdará as propriedades de estilo - apenas o elemento ao

qual se aplicam diretamente o estilo vai aplicar suas propriedades. No entanto, você

pode aplicar um estilo para que se aplique a todos os elementos View, aplicando o estilo

como um tema.

Para aplicar uma definição de estilo como um tema, você deve aplicar o estilo para uma

Activity ou aplicação no manifesto do Android. Quando você fizer isso, todos os View

dentro da atividade ou da aplicação serão aplicáveis a cada propriedade que ele suporta.

Por exemplo, se você aplicar o estilo CodeFont dos exemplos anteriores a uma

atividade, então todos os elementos View que suportam as propriedades de estilo de

texto irá aplicá-los. Qualquer visão que não suporta as propriedades vai ignorá-los. Se o

view suporta apenas algumas das propriedades, então é só aplicar essas propriedades.

Aplicar um estilo a uma view

Veja como definir um estilo para uma exibição no esquema XML:

Page 195: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 195

<TextView

style="@style/CodeFont"

android:text="@string/hello" />

Agora este TextView será denominado como definido pelo estilo chamado CodeFont.

(Veja o exemplo acima, em Definição de estilos).

Nota: O atributo style não usa o android: namespace prefix.

Aplicar um tema a uma atividade ou aplicação

Para definir um tema para todas as atividades de sua aplicação, abra o arquivo

AndroidManifest.xml e edite o tag <application> para incluir o atributo android:theme

com o nome do estilo. Por exemplo:

<application android:theme="@style/CustomTheme">

Se você quer um tema aplicado a apenas uma atividade em seu aplicativo, então,

adicione o atributo android:theme ao tag <activity> um de cada vez.

Assim como o Android oferece outros recursos internos, há muitos temas pré-definidos

que podem ser usados, para evitar escrevê-los sozinho. Por exemplo, você pode usar o

tema Dialog e fazer a sua actividade parecer como uma caixa de diálogo:

<activity android:theme="@android:style/Theme.Dialog">

Ou se você quiser que o fundo seja transparente, usar o tema translúcido:

<activity android:theme="@android:style/Theme.Translucent">

Se você gosta de um tema, mas quer ajustá-lo, basta adicionar o tema como o parent do

seu tema personalizado. Por exemplo, você pode modificar o tradicional light theme

para usar a sua própria cor, como esta:

<color name="custom_theme_color">#b0b0ff</color>

<style name="CustomTheme" parent="android:Theme.Light">

<item name="android:windowBackground">@color/custom_theme_color</item>

<item name="android:colorBackground">@color/custom_theme_color</item>

</style>

(Note que a cor precisa ser fornecida como um recurso separado aqui, porque o atributo

android:windowBackground suporta apenas uma referência a outro recurso, ao contrário

do android:colorBackground, a ele pode não ser dada uma cor literal.)

Agora use CustomTheme em vez de Theme.Light dentro do Manifesto Android:

Page 196: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 196

<activity android:theme="@style/CustomTheme">

Selecione um tema baseado na versão de plataforma

Novas versões do Android têm temas adicionais disponíveis para os aplicativos, e você

pode querer usar estes durante a execução nessas plataformas, enquanto continuam

sendo compatíveis com versões anteriores. Você pode fazer isso através de um tema

personalizado que usa a seleção de recursos para alternar entre os temas pai diferentes,

baseado na versão da plataforma.

Por exemplo, aqui está a declaração de um tema personalizado que é simplesmente o

modelo padrão das plataformas do tema light. Ele vai em um arquivo XML por

res/values (tipicamente res/values/styles.xml ):

<style name="LightThemeSelector" parent="android:Theme.Light">

...

</style>

Para este tema usar o novo tema holográfico quando o aplicativo está rodando o

Android 3.0 (API Nível 11) ou superior, você pode colocar uma declaração alternativa

para o tema em um arquivo XML em res/values-v11, mas fazer do tema mãe o tema

holográfico:

<style name="LightThemeSelector" parent="android:Theme.Holo.Light">

...

</style>

Agora, usar este tema como se fosse qualquer outro, e seu aplicativo passará

automaticamente para o tema holográfico se em execução no Android 3.0 ou superior.

Uma lista de atributos padrão que você pode usar em temas podem ser encontrados em

R.styleable.Theme.

Para obter mais informações sobre o fornecimento de recursos alternativos, como os

temas e layouts, com base na versão de plataforma ou configurações de outro

dispositivo, consulte o documento Fornecimento de Recursos.

Usando estilos e temas da plataforma

A plataforma Android oferece uma grande coleção de estilos e temas que você pode

usar em seus aplicativos. Você pode encontrar uma referência de todos os estilos

disponíveis na classe R.style. Para usar os estilos listados aqui, substituir todos os

Page 197: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 197

sublinhados no nome do estilo, com um período. Por exemplo, você pode aplicar o

Theme_NoTitleBar tema com "@android:style/Theme.NoTitleBar".

O R.style, entretanto, não é bem documentado e não descreve minuciosamente os

estilos, assim, ver o código fonte para estes estilos e temas lhe dará uma compreensão

melhor do que as propriedades de estilo de cada um oferece. Para uma melhor

referência para estilos e temas do Android, consulte os seguintes códigos fonte:

Android Styles (styles.xml)

Android Temas (themes.xml)

Esses arquivos vão te ajudar a aprender através do exemplo. Por exemplo, no código

fonte de temas Android, você encontrará uma declaração de <style

name="Theme.Dialog">. Nesta definição, você verá todas as propriedades que são

usadas para estilo de diálogos que são usadas pelo framework Android.

Para uma referência de atributos de estilo disponíveis que você pode usar para definir

um estilo ou tema (por exemplo, "windowBackground" ou "textAppearance"), ver R.attr

ou a classe View para o qual você está criando um estilo.

Page 198: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 198

Recursos de aplicação

Você deve sempre externar recursos como imagens e seqüências de seu código de

aplicação, para que você possa mantê-las de forma independente. Externalizar seus

recursos também permite que você forneça recursos alternativos que oferecem suporte a

configurações de dispositivos específicos, como línguas ou tamanhos de tela diferentes,

o que se torna cada vez mais importante quanto mais dispositivos com Android tornam-

se disponíveis com configurações diferentes. A fim de proporcionar compatibilidade

com diferentes configurações, você deve organizar os recursos em seu diretório do

projeto res/, usando vários sub-diretórios que agrupam os recursos por tipo e

configuração.

Figura 1. Dois dispositivos diferentes, ambos os usando recursos padrão.

Figura 2. Dois dispositivos diferentes, uma usando recursos alternativos.

Para qualquer tipo de recurso, você pode especificar padrão e vários recursos

alternativos para a sua aplicação:

Os recursos padrão são aqueles que devem ser utilizados independentemente da

configuração do aparelho ou quando não existem recursos alternativos que

correspondam à configuração atual.

Recursos alternativos são aqueles que você já projetou para uso com uma

configuração específica. Para especificar que um grupo de recursos são de uma

configuração específica, acrescente um qualificador de configuração adequada

ao nome do diretório.

Page 199: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 199

Por exemplo, enquanto o seu layout padrão de interface do usuário é salvo no diretório

res/layout/, você pode especificar um layout de interface diferente para ser usado

quando a tela está na orientação paisagem, salvando-o no diretório res/layout-land/.

Android aplica automaticamente os recursos apropriados por correspondência à

configuração atual do dispositivo para os nomes do diretório de recursos.

A figura 1 demonstra como um conjunto de recursos padrão de um aplicativo são

aplicados a dois dispositivos diferentes, quando não há recursos alternativos

disponíveis. A Figura 2 mostra o mesmo aplicativo com um conjunto de recursos

alternativos que se qualificam para uma das configurações do dispositivo, assim, os dois

dispositivos usam recursos diferentes.

A informação acima é apenas uma introdução sobre como trabalhar os recursos do

aplicativo no Android. Os documentos a seguir fornecem um guia completo de como

você pode organizar seus recursos de aplicação, especificar os recursos alternativos,

acessá-los em seu aplicativo, e mais:

Fornecendo recursos

Que tipos de recursos você pode oferecer em seu aplicativo, onde guardá-los, e

como criar recursos alternativos para configurações de dispositivo específico.

Acessando recursos

Como utilizar os recursos que você forneceu, seja por referenciá-los a partir do

seu código de aplicativo ou de outros recursos XML.

Tratando alterações em runtime

Como gerenciar as alterações de configuração que ocorrem quando sua atividade

está em execução.

Localização

Um guia de baixo para cima para localizar seu aplicativo usando recursos

alternativos. Enquanto este é apenas um uso específico de recursos alternativos,

é muito importante para alcançar mais usuários.

Page 200: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 200

Tipos de recursos

Uma referência de vários tipos de recursos que você pode fornecer, descrevendo

seus elementos XML, atributos e sintaxe. Por exemplo, esta referência mostra

como criar um recurso para os menus do aplicativo, os desenhos, animações e

muito mais.

Page 201: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 201

Armazenamento de dados

Android oferece várias opções para você guardar dados da aplicação persistente. A

solução que você escolher depende de suas necessidades específicas, tais como se os

dados devem ser privados da sua aplicação ou acessíveis para outras aplicações (e do

usuário) e quanto espaço seus dados requerem.

Suas opções de armazenamento de dados são os seguintes:

Preferências compartilhadas

Armazenar dados privados primitivos em pares chave-valor.

Armazenamento interno

Armazenar dados privados sobre a memória do dispositivo.

Armazenamento externo

Armazenar dados públicos sobre o armazenamento compartilhado externo.

Bancos de dados SQLite

Armazenar dados estruturados em um banco privado.

Conexão de rede

Armazenar dados na web com seu servidor de rede própria.

Android fornece uma maneira para que você exponha seus dados pessoais, mesmo para

outras aplicações - com um provedor de conteúdo. Um provedor de conteúdo é um

componente opcional que expõe acesso de leitura/gravação à sua aplicação de dados,

sujeito a qualquer restrição que você pretende impor. Para obter mais informações sobre

como usar provedores de conteúdo, consulte a documentação de provedores de

conteúdo.

Utilizando preferências compartilhadas

A classe SharedPreferences fornece um framework geral que permite salvar e recuperar

pares chave-valor persistente de tipos de dados primitivos. Você pode usar

SharedPreferences para salvar os dados primitivos: booleans, floats, inteiros, longos, e

Page 202: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 202

strings. Estes dados vão persistir nas sessões de usuário (mesmo se sua aplicação é

morta).

Para obter um objeto SharedPreferences

para sua aplicação, use um dos dois

métodos:

getSharedPreferences() - Use esta

opção se você precisa de arquivos

de múltiplas preferências

identificados pelo nome, que você

especifica com o parâmetro

primeiro.

getPreferences() - Use esta opção

se você só precisa de um arquivo de

preferências para a sua actividade. Porque este será o único arquivo de

preferências para sua atividade, você não fornece um nome.

Para escrever os valores:

1. Chame edit() para obter uma SharedPreferences.Editor.

2. Adicione valores com métodos como putBoolean() e putString().

3. Empregue os novos valores com commit().

Para ler os valores, use métodos SharedPreferences como getBoolean() e getString().

Aqui está um exemplo que salva uma preferência para o modo silencioso keypress em

uma calculadora:

public class Calc extends Activity {

public static final String PREFS_NAME = "MyPrefsFile";

@Override

protected void onCreate(Bundle state){

super.onCreate(state);

. . .

// Restore preferences

SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);

boolean silent = settings.getBoolean("silentMode", false);

setSilent(silent);

}

Preferências do usuário

Preferências compartilhadas não são

estritamente para o salvar "as preferências do

usuário", como o toque de um usuário

escolheu. Se você está interessado em criar

preferências do usuário para seu aplicativo,

consulte PreferenceActivity , que estabelece

um quadro de atividades para você criar as

preferências do usuário, o qual será

automaticamente persistido (usando as

preferências compartilhadas).

Page 203: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 203

@Override

protected void onStop(){

super.onStop();

// We need an Editor object to make preference changes.

// All objects are from android.context.Context

SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);

SharedPreferences.Editor editor = settings.edit();

editor.putBoolean("silentMode", mSilentMode);

// Commit the edits!

editor.commit();

}

}

Usando o armazenamento interno

Você pode salvar arquivos diretamente na memória interna do dispositivo. Por padrão,

os arquivos salvos no armazenamento interno são privados para sua aplicação e outras

aplicações não podem acessá-los (nem mesmo o usuário). Quando o usuário desinstala o

aplicativo, esses arquivos são removidos.

Para criar e gravar um arquivo privado para o armazenamento interno:

1. Chame openFileOutput() com o nome do arquivo e o modo de funcionamento.

Isso retorna um FileOutputStream.

2. Escreva no arquivo com o write().

3. Feche o fluxo com close().

Por exemplo:

String FILENAME = "hello_file";

String string = "hello world!";

FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);

fos.write(string.getBytes());

fos.close();

MODE_PRIVATE irá criar o arquivo (ou substituir um arquivo de mesmo nome) e

torná-lo privado para sua aplicação. Outras modalidades disponíveis são:

MODE_APPEND, MODE_WORLD_READABLE e MODE_WORLD_WRITEABLE.

Page 204: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 204

Para ler um arquivo de armazenamento interno:

1. Chame openFileInput() e passe o nome do arquivo a ser lido. Isso retorna um

FileInputStream.

2. Leia os bytes do arquivo com a read().

3. Em seguida, feche o fluxo com close().

Dica: Se você quiser salvar um arquivo estático em seu aplicativo em tempo de

compilação, salve o arquivo no diretório de res/raw/ do seu projeto. Você pode abri-lo

com openRawResource(), passando a identificação de recurso R.raw.<filename>. Esse

método retorna um InputStream que você pode usar para ler o arquivo (mas você não

pode escrever no arquivo original).

Salvando os arquivos de cache

Se você gostaria de gravar alguns dados em cache, ao invés de armazená-lo

persistentemente, você deve usar getCacheDir() para abrir um arquivo que representa o

diretório interno onde a sua aplicação deve salvar os arquivos de cache temporário.

Quando o dispositivo está com pouco espaço de armazenamento interno, o Android

pode excluir esses arquivos de cache para recuperar espaço. No entanto, você não deve

confiar no sistema para limpar esses arquivos para você. Você deve sempre manter os

arquivos de cache e manter dentro de um limite razoável de espaço consumido, como

1MB. Quando o usuário desinstala o aplicativo, esses arquivos são removidos.

Outros métodos úteis:

getFilesDir()

Obtém o caminho absoluto para o diretório de arquivos onde os arquivos

internos são salvos.

getDir()

Cria (ou abre um existente) diretório dentro de seu espaço de armazenamento

interno.

deleteFile()

Exclui um arquivo salvo na memória interna.

Page 205: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 205

fileList()

Retorna uma matriz de arquivos atualmente salvos pela sua aplicação.

Usando o armazenamento externo

Cada dispositivo compatível com o Android suporta um "armazenamento externo"

compartilhado que você pode usar para salvar arquivos. Pode ser uma mídia de

armazenamento removível (como um cartão SD) ou uma memória interna (não

removível). Os arquivos salvos no armazenamento externos são de leitura e podem ser

modificados pelo usuário quando permitem armazenamento em massa USB para

transferir arquivos de um computador.

Atenção: os arquivos externos podem desaparecer se o usuário monta o

armazenamento externo em um computador ou remove a mídia, e não há nenhuma

segurança aplicada sobre os arquivos que você salva para o armazenamento externo.

Todas as aplicações podem ler e gravar arquivos colocados no armazenamento externo

e o usuário pode removê-los.

Verificar a disponibilidade dos meios

Antes de fazer qualquer trabalho com o armazenamento externo, você deve sempre

chamar getExternalStorageState() para verificar se os meios de comunicação estão

disponíveis. A mídia pode ser montada a um computador, faltando, somente leitura, ou

em algum outro estado. Por exemplo, aqui está como você pode verificar a

disponibilidade:

boolean mExternalStorageAvailable = false;

boolean mExternalStorageWriteable = false;

String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {

// We can read and write the media

mExternalStorageAvailable = mExternalStorageWriteable = true;

} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {

// We can only read the media

mExternalStorageAvailable = true;

mExternalStorageWriteable = false;

} else {

// Something else is wrong. It may be one of many other states, but all we need

// to know is we can neither read nor write

mExternalStorageAvailable = mExternalStorageWriteable = false;

}

Page 206: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 206

Este exemplo verifica se o armazenamento externo está disponível para ler e escrever. O

método getExternalStorageState() retorna outros estados que você pode querer verificar,

como se a mídia está sendo compartilhada (conectada a um computador), está

totalmente ausente, foi mal removida, etc. Você pode usá-los para notificar o usuário

com mais informações quando o aplicativo precisa de acesso à mídia.

Acessando arquivos em armazenamento externo

Se você estiver usando a API de nível 8 ou superior, use getExternalFilesDir() para abrir

um arquivo que representa o diretório de armazenamento externo onde você deve salvar

seus arquivos. Este método utiliza um parâmetro type que especifica o tipo de

subdiretório que você deseja, como DIRECTORY_MUSIC e

DIRECTORY_RINGTONES (passe null para receber a raiz do diretório do arquivo da

aplicativo). Este método irá criar o diretório apropriado, se necessário. Ao especificar o

tipo de diretório, você garante que a mídia scanner do Android irá categorizar

corretamente seus arquivos no sistema (por exemplo, os ringtones são identificados

como toques, e não música). Se o usuário desinstala o aplicativo, este diretório e todo

seu conteúdo serão apagados.

Se você estiver usando a API de nível 7 ou inferior, use getExternalStorageDirectory()

para abrir um File que representa a raiz de armazenamento externo. Você deve então

escrever os seus dados no seguinte diretório:

/Android/data/<package_name>/files/

O <package_name> é o seu nome de estilo do pacote Java, tal como

"com.example.android.app". Se o dispositivo do usuário está executando API nível 8 ou

superior e desinstalar o aplicativo, este diretório e todo seu conteúdo serão apagados.

Como salvar arquivos que

devem ser compartilhados

Se você deseja salvar os arquivos que

não são específicos para a sua

aplicação e que não devem ser

excluídos quando o aplicativo é

desinstalado, salva-os em um dos

Escondendo seus arquivos a partir da

Media Scanner

Inclua um arquivo vazio chamado .nomedia em seu

diretório de arquivos externos (note o ponto prefixo

ao nome do arquivo). Isto irá prevenir o media

scanner do Android de ler arquivos de mídia e

incluí-los em aplicativos como Galley ou Music.

Page 207: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 207

diretórios públicos de armazenamento externo. Esses diretórios estão na origem do

armazenamento externo, como Music/, Pictures/, Ringtones/ e outros.

Na API nível 8 ou superior, use getExternalStoragePublicDirectory(),passando para ele

o tipo de diretório público que deseja, tais como DIRECTORY_MUSIC,

DIRECTORY_PICTURES, DIRECTORY_RINGTONES ou outros. Este método irá

criar o diretório apropriado, se necessário.

Se você estiver usando a API de nível 7 ou inferior, use getExternalStorageDirectory()

para abrir um File que representa a raiz do armazenamento externo, em seguida, salve

seus arquivos compartilhados em um dos seguintes diretórios:

Music/ - o scanner media classifica todas as mídias encontradas aqui como a

música do usuário.

Podcasts/ - scanner media classifica todas as mídias encontradas aqui como um

podcast.

Ringtones/ - scanner media classifica todas as mídias encontradas aqui como um

ringtone.

Alarms/ - scanner media classifica todas as mídias encontradas aqui como um

som de alarme.

Notifications/ - scanner media classifica todas as mídias encontradas aqui como

um som de notificação.

Pictures/ - todas as fotos (excluindo as tiradas com a câmera).

Movies/ - todos os filmes (excluindo aqueles filmados com a câmera de vídeo).

Download/ - downloads diversos.

Salvando os arquivos de cache

Se você estiver usando a API de nível ou superior, use 8 getExternalCacheDir() para

abrir um File que representa o diretório de armazenamento externo, onde você deve

salvar os arquivos de cache. Se o usuário desinstala o aplicativo, esses arquivos serão

automaticamente excluídos. No entanto, durante a vida do seu aplicativo, você deve

gerenciar esses arquivos de cache e eliminar os que não são necessários, a fim de

preservar o espaço do arquivo.

Page 208: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 208

Se você estiver usando a API de nível 7 ou inferior, use getExternalStorageDirectory()

para abrir um File que representa a raiz do armazenamento externo, em seguida, escreva

o seu cache de dados no seguinte diretório:

/Android/data/<package_name>/cache/

O <package_name> é o seu estilo nome do pacote Java, tal qual

"com.example.android.app".

Utilizando bancos de dados

Android oferece suporte completo para bancos de dados SQLite. Qualquer banco de

dados que você criar serão acessíveis pelo nome de qualquer classe na aplicação, mas

não fora da aplicação.

O método recomendado para criar um novo banco de dados SQLite é criar uma

subclasse de SQLiteOpenHelper e substituir o método onCreate(), no qual você pode

executar um comando SQLite para criar tabelas no banco de dados. Por exemplo:

public class DictionaryOpenHelper extends SQLiteOpenHelper {

private static final int DATABASE_VERSION = 2;

private static final String DICTIONARY_TABLE_NAME = "dictionary";

private static final String DICTIONARY_TABLE_CREATE =

"CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +

KEY_WORD + " TEXT, " +

KEY_DEFINITION + " TEXT);";

DictionaryOpenHelper(Context context) {

super(context, DATABASE_NAME, null, DATABASE_VERSION);

}

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(DICTIONARY_TABLE_CREATE);

}

}

Você pode obter uma instância de sua implementação SQLiteOpenHelper usando o

construtor que você definiu. Para escrever e ler a partir do banco de dados, chame

getWritableDatabase() e getReadableDatabase(), respectivamente. Estes devolvem um

objeto SQLiteDatabase que representa o banco de dados e fornece métodos para

operações de SQLite.

Page 209: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 209

Você pode executar consultas SQLite usando

métodos query() SQLiteDatabase, que aceitam

parâmetros de consulta diversos, tais como a tabela a

ser consultada, a projeção, seleção, colunas,

agrupamento e outros.

Cada consulta SQLite irá retornar um Cursor que

aponta para todos os registros encontrados pela

consulta. O Cursor é sempre o mecanismo com o

qual você pode navegar pelos resultados de uma

consulta de banco de dados e ler linhas e colunas.

Para aplicativos de exemplo que demonstram como

usar banco de dados SQLite no Android, consulte as

aplicações Note Pad e Dicionário pesquisável.

Banco de dados de depuração

O Android SDK inclui uma ferramenta sqlite3 de banco de dados que permite que você

navegue sobre o conteúdo da tabela, execute comandos SQL e execute outras funções

úteis em bancos de dados SQLite.

Usando uma conexão de rede

Você pode usar a rede (quando disponível) para armazenar e recuperar dados sobre os

seus próprios serviços baseados na web. Para fazer operações de rede, use as classes dos

seguintes pacotes:

java.net.*

android.net.*

Android não impõe qualquer

limitação para além dos conceitos-

padrão SQLite. Nós

recomendamos incluir um valor de

campo autoincrementado como

chave que pode ser usado como

uma identificação única para

localizar rapidamente um registro.

Isso não é necessária para dados

privados, mas se você

implementar um provedor de

conteúdo, você deve incluir uma

identificação exclusiva com a

constante BaseColumns._ID.

Page 210: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 210

Artigos

Acessibilidade

Linguagens e recursos

Text-to-Speech TTS: também conhecido como speech synthesis (síntese de fala, em

português) é um recurso disponibilizado a partir de Android 1.6 (API Level 4) que

possibilita ao dispositivo „ler‟ textos em diversas linguagens.

O mecanismo TTS embarcado no Android suporta inglês (britânico ou americano),

francês, alemão, italiano e espanhol e precisa saber qual idioma pronunciar para adequar

a voz, afinal, uma mesma palavra possui pronuncias diferentes dependendo da língua.

Uma checagem da disponibilidade do recurso se faz necessário tendo em vista que,

apesar de todos os dispositivos com Android terem a funcionalidade embarcada, alguns

possuem armazenamento limitado e pode ser que faltem arquivos de recursos

específicos do idioma, sendo assim, o seguinte código verifica a presença dos recursos

TTS.

Intent checkIntent = new Intent();

checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);

startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);

Uma checagem que retorna sucesso é marcada por CHECK_VOICE_DATA_PASS indicando

que o dispositivo está pronto para „falar‟, depois da criação do objeto TextToSpeech.

Em caso negativo, o dispositivo irá utilizar o ACTION_INSTALL_TTS_DATA que

dispara uma ação levando o usuário a fazer a instalação manualmente acessando o

Android Market; A instalação é feita automaticamente após a conclusão do download.

Uma implementação da verificação do resultado da checagem está abaixo:

private TextToSpeech mTts;

protected void onActivityResult(

int requestCode, int resultCode, Intent data) {

if (requestCode == MY_DATA_CHECK_CODE) {

if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {

// success, create the TTS instance

mTts = new TextToSpeech(this, this);

} else {

// missing data, install it

Intent installIntent = new Intent();

installIntent.setAction(

TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);

startActivity(installIntent);

Page 211: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 211

}

} }

No construtor da instância TextToSpeech nós passamos uma referência ao Contexto a

ser usado (neste caso a atividade atual), e um OnInitListener (aqui a nossa atividade

também). Dessa forma é habilitado para que a aplicação seja notificada quando o Text-

To-Speech é totalmente carregado, então podemos começar a configurar-lo e usá-lo.

Linguagens e localidade

No Google I/O 2009 foi mostrado uma utilização do TTS para falar o resultado de uma

tradução de e para uma das línguas disponíveis. Um exemplo de chamada é como o

abaixo:

mTts.setLanguage(Locale.US);

Ou para verificar se uma linguagem está disponível, basta usar o trecho abaixo que

retornará TextToSpeech.LANG_COUNTRY_AVAILABLE para indicar que o idioma

e o país, como descrito pelo parâmetro de localidade, são suportados (e os dados estão

corretamente instalados), como também pode retornar

TextToSpeech.LANG_AVAILABLE indicando que a língua está disponível ou o

oposto TextToSpeech.LANG_MISSING_DATA.

mTts.isLanguageAvailable(Locale.UK))

mTts.isLanguageAvailable(Locale.FRANCE))

mTts.isLanguageAvailable(new Locale("spa", "ESP")))

Obs.: para usar o código Locale.getDefault(), deve-se certificar primeiramente se o

idioma padrão é suportado.

Fazendo o dispositivo ‘falar’

A maneira mais simples de fazer isso é usando o método speak() como:

String myText1 = "Did you sleep well?";

String myText2 = "I hope so, because it's time to wake up.";

mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, null);

mTts.speak(myText2, TextToSpeech.QUEUE_ADD, null);

O mecanismo TTS gerencia uma fila global de todas as entradas para sintetizar, que

também são conhecidos como "expressões". Cada TextToSpeech pode gerir sua própria

fila a fim de controlar o que vai interromper a emissão atual e que é simplesmente uma

fila de espera.

Page 212: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 212

No Android, cada stream de áudio que é reproduzido está associado a um tipo de fluxo,

tal como definido no android.media.AudioManager . Para um despertador falando, o

texto a ser reproduzido pertence ao tipo de fluxo AudioManager.STREAM_ALARM,

para que ele respeite as definições de alarme que o usuário escolheu no dispositivo. O

último parâmetro do método speak() permite que você passe para os parâmetros do

TTS, especificado como pares chave / valor em um HashMap, tal qual:

HashMap<String, String> myHashAlarm = new HashMap();

myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_STREAM,

String.valueOf(AudioManager.STREAM_ALARM));

mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, myHashAlarm);

mTts.speak(myText2, TextToSpeech.QUEUE_ADD, myHashAlarm);

Como as chamadas são assíncronas, pode ser necessário identificar se uma síntese foi

concluída, isso pode ser feito da seguinte forma:

mTts.setOnUtteranceCompletedListener(this);

myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_STREAM,

String.valueOf(AudioManager.STREAM_ALARM));

mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, myHashAlarm);

myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,

"end of wakeup message ID");

// myHashAlarm now contains two optional parameters

mTts.speak(myText2, TextToSpeech.QUEUE_ADD, myHashAlarm);

E a atividade é notificada pelo método, repare que a mensagem foi usada para ser

identificada no método:

public void onUtteranceCompleted(String uttId) {

if (uttId == "end of wakeup message ID") {

playAnnoyingMusic();

}

}

Como o recurso de fala exige bastante processamento, numa situação em que um

determinado texto será lido diversas vezes é mais interessante gravar o áudio para ser

reproduzido posteriormente.

HashMap<String, String> myHashRender = new HashMap();

String wakeUpText = "Are you up yet?";

String destFileName = "/sdcard/myAppCache/wakeUp.wav";

myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, wakeUpText);

mTts.synthesizeToFile(wakuUpText, myHashRender, destFileName);

A funcionalidade de text-to-speech depende de um serviço dedicado compartilhado

entre todos os aplicativos que usam esse recurso. Quando você terminar de usar o TTS,

use a instrução mTts.shutdown() dentro do método onDestroy(), por exemplo.

Page 213: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 213

Interface

Toque

O modo de toque é um estado da hierarquia de vista que depende unicamente da

interação do usuário com o telefone. Por si só, o modo de tocar é algo muito fácil de

entender, pois ele simplesmente indica se a interação do usuário passado foi realizada

com a tela sensível ao toque. Por exemplo, se você estiver usando um dispositivo com

Android, a seleção de um widget com o trackball vai levá-lo sair do modo de tocar, no

entanto, se você toca um botão na tela com seu dedo, você entrará no modo de tocar.

Quando o usuário não estiver em modo de tocar, nós falamos sobre o modo de trackball,

o modo de navegação ou a navegação por teclado, por isso não se surpreenda se você

encontrar esses termos.

Existe apenas uma API diretamente relacionada com o modo de toque,

View.isInTouchMode().

Curiosamente, o modo de tocar é enganosamente simples e as conseqüências de entrar

no modo de tocar é muito maior do que você imagina. Vejamos algumas das razões.

Toque em modo de seleção e foco

Criar um conjunto de ferramentas UI para dispositivos móveis é difícil porque são

vários os mecanismos de interação. Alguns dispositivos oferecem apenas 12 teclas,

algumas têm uma tela sensível ao toque, alguns exigem uma caneta, alguns têm ambos

um ecrã táctil e um teclado. Com base nos recursos de hardware do usuário, ele pode

interagir com seu aplicativo usando mecanismos diferentes, então tivemos que pensar

muito sobre todos os possíveis problemas que possam surgir. Uma questão nos levou a

criar o modo de toque.

Imagine um aplicativo simples, ApiDemos por exemplo, que mostra uma lista de itens

de texto. O usuário pode navegar livremente pela lista usando a trackball, mas também,

em alternativa, deslocar e arremessar a lista usando a tela sensível ao toque. O problema

neste cenário é como lidar com a seleção corretamente quando o usuário manipula a

lista através da tela sensível ao toque.

Neste caso, se o usuário selecionar um item no topo da lista e então arremessa a lista

para o fundo, o que deve acontecer com a seleção? E se ele permanecer no item e rolar

Page 214: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 214

para fora da tela? O que deve acontecer se o usuário decidiu então mover a seleção com

o trackball? Ou pior, o que deve acontecer se o usuário pressiona o trackball para agir

de acordo com o item selecionado, que não é mostrado na tela mais?

Após cuidadosa consideração, decidimos remover por completo a seleção, quando o

usuário manipula a interface do usuário através da tela sensível ao toque.

No modo de toque, não há foco e não há seleção. Qualquer item selecionado em uma

lista de em uma grade fica desmarcada, logo que o usuário entra no modo de tocar. Da

mesma forma, quaisquer widgets ficaram desfocados quando o usuário entra no modo

de tocar. A imagem abaixo ilustra o que acontece quando o usuário toca uma lista

depois de selecionar um item com o trackball.

Para tornar as coisas mais naturais para o usuário, o quadro sabe como ressuscitar a

seleção / foco sempre que o usuário sai do modo de tocar. Por exemplo, se o usuário

fosse usar o trackball novamente, a seleção iria reaparecer no item previamente

selecionado. É por isso que alguns desenvolvedores estão confusos quando se criar uma

exibição personalizada e começar a receber os principais eventos só depois de mover o

trackball uma vez: a sua aplicação está no modo de tocar, e eles precisam usar o

trackball para sair do modo de tocar e ressuscitar o foco.

Page 215: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 215

A relação entre o modo de toque, seleção e foco significa que você não deve confiar na

seleção e/ou foco existir em sua aplicação. Um problema muito comum com o Android

para novos desenvolvedores é contar com ListView.getSelectedItemPosition(). No

modo de toque, este método retornará INVALID_POSITION.

Focando no modo Touch

Em geral, o foco não existe no modo de tocar. No entanto, o foco pode existir no modo

de tocar em uma maneira muito especial chamado focusable. Este modo especial foi

criado para widgets que recebem a entrada de texto, como EditText ou ListView. O

modo focusable é o que permite ao usuário inserir texto dentro de um campo de texto na

tela, sem primeiro selecioná-la com a bola ou o dedo.

Quando um usuário toca a tela, o aplicativo irá entrar no modo de toque se ele não

estava no modo de tocar já. O que acontece durante a transição para o modo de tocar

depende do que o usuário tocou, e que atualmente tem foco. Se o usuário toca um

widget que é focusable no modo de tocar, o widget irá receber o foco. Caso contrário,

qualquer elemento ao qual se dedica atualmente não vai manter o foco a menos que seja

focusable no modo de tocar. Por exemplo, na figura abaixo, quando o usuário toca a

tela, o campo de texto recebe o foco.

Focusable no modo de é uma propriedade que você

pode definir a si mesmo, seja de código ou de XML. No

entanto, você deve usá-lo com parcimônia e só em

situações muito específicas, porque quebra a coerência

com o comportamento normal da interface do Android.

Um jogo é um bom exemplo de uma aplicação que pode

fazer bom uso do focusable na propriedade de modo

sensível ao toque. MapView, se usada em tela cheia

como no Google Maps, é outro bom exemplo de onde

você pode usar focusable no modo de tocar

corretamente.

Abaixo está um exemplo de um widget focusable em modo de tocar. Quando o usuário

bate um AutoCompleteTextView com o dedo, o foco permanece no campo de texto de

entrada:

Page 216: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 216

Novos desenvolvedores para o Android, muitas vezes pensam que focusable no modo

de tocar é a solução que eles precisam para "consertar" o problema do

"desaparecimento" da seleção/foco. Nós encorajamos você a pensar muito antes de

utilizá-lo. Se usada incorretamente, pode fazer seu aplicativo se comportar de maneira

diferente do resto do sistema e simplesmente jogar fora os hábitos do usuário. O quadro

Android contém todas as ferramentas necessárias para lidar com as interações do

usuário sem usar focusable no modo de tocar. Por exemplo, em vez de tentar fazer

ListView sempre manter a sua seleção, basta usar o modo de escolha apropriada, como

mostrado na setChoiceMode(int) .

Gestos

As telas de toque são uma ótima maneira de interagir com aplicações em dispositivos

móveis. Com uma tela de toque, os usuários podem facilmente tocar, arrastar,

arremessar ou deslizar rapidamente e realizar ações em seus aplicativos favoritos. Para

os desenvolvedores de aplicativos o Android faz com que seja fácil reconhecer ações

simples, mas tem sido mais difícil de lidar com gestos complicados, às vezes exigindo

que os desenvolvedores escrevam um monte de código.

É por isso que foi introduzida uma nova API de gestos no Android 1.6. Esta API,

localizada no novo pacote android.gesture , permite armazenar, carregar, desenhar e

reconhecer gestos. Clique para baixar o código fonte dos exemplos

Método de entrada de dados

A partir do Android 1.5, a plataforma Android oferece um Input Method Framework

(FMI) que permite a criação de métodos de entrada na tela, como teclados virtuais. Este

artigo fornece uma visão geral de editores de método de Android de entrada (IME) e o

que um aplicativo precisa fazer para trabalhar bem com eles. O FMI é concebido para

Page 217: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 217

apoiar novas classes de dispositivos Android, como os teclados sem hardware, por isso

é importante que o aplicativo funcione bem com o FMI e oferece uma ótima experiência

para os usuários.

O que é um método de entrada?

O FMI Android foi concebido para suportar uma variedade de IMEs, incluindo o

teclado virtual, reconhecedores de mão-escrita, e tradutores teclado duro. Nosso foco,

no entanto, será em teclados virtuais, já que este é o tipo de método de entrada que

atualmente faz parte da plataforma.

Um usuário normalmente irá acessar o IME atual, tocando em uma exibição de texto

para editar, conforme mostrado na tela inicial:

O teclado virtual é posicionado na parte inferior da tela sobre a janela do aplicativo.

Para organizar o espaço disponível entre a aplicação e o IME, usamos algumas

abordagens, a que é mostrada aqui é chamado de pan e digitalizar, e simplesmente

envolve a rolagem da janela do aplicativo em torno de modo que a visão focada

atualmente é visível. Este é o modo padrão, pois é mais seguro para as aplicações

existentes.

Page 218: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 218

Na maioria das vezes o layout da tela é um resize, onde a janela da aplicação é

redimensionada para ser totalmente visível. Um exemplo é mostrado aqui, quando

escrevo uma mensagem de e-mail:

O tamanho da janela do aplicativo é alterada para que nenhum deles seja escondido pelo

IME, permitindo acesso total ao aplicativo e IME. Isto, obviamente, só funciona para

aplicativos que têm uma área redimensionável que pode ser reduzida para dar espaço

suficiente, mas o espaço vertical neste modo é realmente nada menos do que o que está

disponível na orientação paisagem.

O principal modo final é fullscreen ou modo de extrato. Isso é usado quando o IME é

muito grande para o espaço compartilhar com a aplicação de base. Com o IME padrão,

você só vai encontrar essa situação quando a tela está em uma orientação horizontal,

embora IMEs sejam livres para usá-lo sempre que desejarem. Neste caso, a janela da

aplicação é deixada como está, e simplesmente o IME exibe a tela inteira em cima dela,

como mostrado aqui:

Page 219: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 219

Porque o IME está cobrindo o aplicativo, ele tem a sua área de edição própria, o que

mostra o texto, na verdade contida na petição inicial. Existem também algumas

oportunidades limitadas à aplicação que tem que personalizar as partes do IME para

melhorar a experiência do usuário.

Atributos básicos XML para controlar IMEs

Há uma série de coisas que o sistema faz para tentar ajudar de trabalho existente com as

aplicações IMEs tão bem quanto possível, tais como:

Use pan e scan por padrão, a menos que possa razoavelmente supor que o modo

de redimensionar vai trabalhar pela existência de listas, percorrer as exibições,

etc.

Analisar no TextView vários atributos existentes para adivinhar o tipo de

conteúdo (números, texto simples, senha, etc.) para ajudar o teclado a exibir um

esquema de chave apropriado.

Atribuir algumas ações padrão para o IME fullscreen, como "campo próximo" e

"feito".

Há também algumas coisas simples que você pode fazer na sua aplicação que, muitas

vezes, melhoram significativamente sua experiência de usuário. Exceto quando

mencionado explicitamente, estes irão trabalhar em qualquer versão da plataforma

Android, mesmo os anteriores para o Android 1.5 (uma vez que irá simplesmente

ignorar essas novas opções).

Especificando cada tipo de controle de entrada EditText

A coisa mais importante para um pedido a fazer é usar o novo atributo

android:inputType em cada EditText. O atributo fornece informação muito mais rica

Page 220: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 220

sobre o conteúdo do texto. Este atributo realmente substitui muitos atributos existentes (

android: password , android: singleLine , android: numeric , android: phoneNumber ,

android: capitalize , android: autoText e android: editable). Se você especificar os

atributos mais velhos e os novos android:inputType, o sistema usa android:inputType e

ignora as outras.

O android:inputType atributo tem três partes:

A classe é a interpretação geral de caracteres. As classes atualmente suportadas

são text (plain text), number (número decimal), phone (número de telefone), e

datetime (uma data ou hora).

A variação é um refinamento da classe. No atributo, normalmente você vai

especificar a classe e variante juntamente com a classe como um prefixo. Por

exemplo, textEmailAddress é um campo de texto onde o usuário irá inserir algo

que é um endereço de correio ([email protected]) para a disposição das teclas terá

um @ "caráter" de acesso fácil, e numberSigned é um campo numérico com um

sinal. Se somente a classe é especificado, então você obtém o padrão / variante

genérica.

Sinalizadores adicionais podem ser especificados de abastecimento de

refinamento. Esses sinalizadores são específicos para uma classe. Por exemplo,

alguns sinalizadores para o text de classe são textCapSentences, textAutoCorrect

e textMultiline.

Como exemplo, aqui é o EditText novo para a aplicação do IM ver texto da mensagem:

<EditText android:id="@+id/edtInput"

android:layout_width="0dip"

android:layout_height="wrap_content"

android:layout_weight="1"

android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"

android:imeOptions="actionSend|flagNoEnterAction"

android:maxLines="4"

android:maxLength="2000"

android:hint="@string/compose_hint"/>

Ativar o modo de redimensionamento e funcionalidades outra janela

A segunda coisa mais importante para sua aplicação a fazer é especificar o

comportamento global da sua janela em relação ao método de entrada. O aspecto mais

Page 221: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 221

visível disto é o controle redimensionar VS. pan e scan mode, mas existem outras coisas

que você pode fazer bem para melhorar sua experiência de usuário.

Você normalmente irá controlar esse comportamento através do

android:windowSoftInputMode em cada <activity> em sua AndroidManifest.xml.

Como o tipo de entrada, há um par de peças diferentes de dados que pode ser

especificado aqui, combinando-as entre si:

O modo de ajuste da janela é especificado com adjustResize ou adjustPan . É

altamente recomendado que você especifique sempre um ou outro.

Você ainda pode controlar se o IME será exibido automaticamente quando sua

atividade é exibida e outras situações onde o usuário move a ele. O sistema não

mostrará automaticamente um IME, por padrão, mas em alguns casos pode ser

conveniente para o usuário se uma aplicação permite esse comportamento. Você

pode solicitar este com stateVisible . Há também um número de opções de outro

estado para o controle mais detalhado que você pode encontrar na

documentação.

Um exemplo típico desse campo pode ser visto na atividade de edição do contato, o que

garante que ele é redimensionado e exibe automaticamente o IME para o usuário:

<activity name="EditContactActivity"

android:windowSoftInputMode="stateVisible|adjustResize">

...

</activity>

Controlando os botões de ação

Para a personalização final, vamos olhar para a "ação" de botões no IME. Existem

atualmente dois tipos de ações:

A tecla enter em um teclado virtual é normalmente ligada a uma ação quando

não estiverem operando em um multi-line de edição de texto.

Quando em modo de tela cheia, um IME pode também colocar um botão de ação

adicional à direita do texto que está sendo editado, dando ao usuário o acesso

rápido a uma operação de aplicativos comuns.

Page 222: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 222

Estas opções são controladas com o atributo android:imeOptions em TextView . O valor

que você fornecer aqui pode ser qualquer combinação de:

Uma das ações pré-definidas constantes ( actionGo , actionSearch , actionSend ,

actionNext , actionDone ). Se nenhum desses for especificado, o sistema irá

inferir qualquer actionNext ou actionDone dependendo se há um campo

focusable após este, você pode explicitamente forçar a nenhuma ação com

actionNone.

O flagNoEnterAction informa o IME que a ação não deve estar disponível na

tecla enter, mesmo que o texto em si não é multi-linha. Isso evita ter ações

irrecuperáveis (enviar) que pode ser tocado acidentalmente pelo usuário durante

a digitação.

O flagNoAccessoryAction remove o botão de ação da área de texto, deixando

mais espaço para o texto.

O flagNoExtractUi remove completamente a área de texto, permitindo que o

aplicativo possa ser visto por trás dele.

A anterior mensagem de aplicação MI também fornece um exemplo de um uso

interessante da imeOptions, para especificar a ação de envio, mas não que seja mostrado

a tecla Enter:

android:imeOptions="actionSend|flagNoEnterAction"

APIs para controlar IMEs

Para um controle mais avançado sobre o IME, há uma variedade de novas APIs que

você pode usar. A menos que tenha um cuidado especial (como por meio de reflexão),

utilizando essas APIs fará com que seu aplicativo seja incompatível com as versões

anteriores do Android, e você deve se certificar de que você especifique

android:minSdkVersion="3" em seu manifesto.

A API principal é o novo android.view.inputmethod.InputMethodManager, que você

pode recuperar com Context.getSystemService(). Ele permite que você interaja com o

estado global do método de entrada, como explicitamente esconder ou mostrar a área

atual do IME de entrada.

Page 223: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 223

Há também novas bandeiras controlando a interação do método de entrada, que você

pode controlar através da existente Window.addFlags() e novos

Window.setSoftInputMode(). A classe PopupWindow acrescentou métodos

correspondentes para controlar essas opções em sua janela. Uma coisa em particular a

ter em conta é o novo

WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, que é usado para

controlar se uma janela está em cima ou atrás do IME atual.

A maior parte da interação entre um IME ativos e a aplicação é feita através do

android.view.inputmethod.InputConnection. Esta é a API implementada em uma

aplicação, que um IME chama para realizar as operações de edição apropriada sobre o

pedido. Você não vai precisar se preocupar com isso, pois TextView oferece sua própria

implementação para si mesmo.

Há também um punhado de novas View às APIs, a mais importante delas sendo

onCreateInputConnection() que cria um novo InputConnection de um IME (e preenche

um android.view.inputmethod.EditorInfo com o seu tipo de entrada, as opções IME, e

outros dados), novamente, a maioria dos desenvolvedores não precisarão se preocupar

com isso, pois TextView cuida disso para você.

Criando um método de entrada de dados

Para criar um método de entrada (IME) para inserir texto em campos de texto e outras

exibições, você precisa estender a classe InputMethodService. Essa classe fornece

grande parte da implementação de base para um método de entrada, em termos de

administrar o estado e a visibilidade do método de entrada e se comunicar com a

atividade visível no momento.

O típico ciclo de vida de um InputMethodService:

Page 224: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 224

Page 225: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 225

Campos de um aplicativo de texto podem ter diferentes tipos de entrada especificada

sobre eles, como o texto de forma livre, numérico, URL, endereço de e-mail e busca.

Quando você implementa um novo método de entrada, você precisa estar ciente dos

tipos de entrada diferentes. Os métodos de entrada não são automaticamente comutados

para diferentes tipos de entrada e por isso é necessário para suportar todos os tipos no

IME. No entanto, o IME não é responsável por validar a entrada enviada para o

aplicativo. Essa é a responsabilidade da aplicação.

Por exemplo, o LatinIME equipados com a plataforma Android oferece layouts

diferentes para o texto e entrada de número de telefone:

Preste atenção específica ao enviar o texto para campos de senha. Certifique-se que a

senha não é visível na sua interface do usuário - nem no modo de exibição de entrada ou

o ponto de vista dos candidatos. Além disso, não salvar a senha em qualquer lugar sem

explicitamente informar o utilizador.

A interface do usuário deve ser capaz de escala entre as orientações retrato e paisagem.

No modo IME não fullscreen, deixar espaço suficiente para a aplicação para mostrar o

campo de texto e qualquer contexto associado. De preferência, não mais que metade da

tela deve ser ocupada pelo IME.

Existem duas maneiras de enviar mensagens de texto para o aplicativo. Você pode

enviar individuais eventos-chave ou você pode editar o texto ao redor do cursor no

campo a aplicação do texto.

Para enviar um evento-chave, você pode simplesmente construir objetos KeyEvent e

chamar InputConnection.sendKeyEvent(). Aqui estão alguns exemplos:

InputConnection ic = getCurrentInputConnection();

long eventTime = SystemClock.uptimeMillis();

ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,

Page 226: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 226

KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,

KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));

ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,

KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,

KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));

Ou então use o seguinte comando:

InputMethodService.sendDownUpKeyEvents(keyEventCode);

Ao editar texto em um campo de texto, alguns dos métodos mais úteis na

android.view.inputmethod.InputConnection são:

getTextBeforeCursor()

getTextAfterCursor()

deleteSurroundingText()

commitText()

Ações de desenhos

Drawable Mutations é uma classe para oferecer ao programador “algo que pode ser

desenhado” (Drawable Mutations).

Por exemplo, um BitmapDrawable é usado para exibir imagens, um ShapeDrawable

para desenhar formas e gradientes e assim por diante. Você pode até mesmo combiná-

las para criar renderizações complexas.

Por exemplo, toda vez que você criar um Button, uma nova drawable é carregada a

partir do quadro de recursos ( android.R.drawable.btn_default ). Isto significa que todos

os botões em todos os aplicativos usam uma instância diferente drawable como pano de

fundo. No entanto, todas estas partes drawables possuem um estado comum, o

chamado "estado constante". O conteúdo deste estado varia de acordo com o tipo de

drawable você está usando, mas, geralmente, contém todas as propriedades que podem

ser definidos por um recurso. No caso de um botão, o constante estado contém uma

imagem bitmap. Desta forma, todos os botões em todos os aplicativos compartilham o

mesmo bitmap, o que economiza um monte de memória.

O diagrama abaixo mostra como as entidades são criadas quando você atribuir o

recurso de mesma imagem como fundo de dois pontos de vista diferentes. Como você

Page 227: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 227

pode ver dois drawables são criadas, mas que ambos compartilham o mesmo estado

constante, portanto, a mesma bitmap:

Esse recurso de compartilhamento de estado é de grande valia para evitar o desperdício

de memória, mas pode causar problemas quando você tentar modificar as propriedades

de um drawable. Imagine uma aplicação com uma lista de livros. Cada livro tem uma

estrela ao lado de seu nome, totalmente opaco quando o usuário marca o livro como um

dos favoritos, e translúcido quando o livro não é um favorito. Para conseguir esse

efeito, você provavelmente vai escrever o seguinte código em seu método adaptador de

lista do getView():

Book = ...;

TextView Item_da_lista = ...;

listItem.setText (book.getTitle ());

estrela Drawable = context.getResources () getDrawable (R.drawable.star).;

if (book.isFavorite ()) {

star.setAlpha (255); / opaca /

Else {}

star.setAlpha (70); / translúcido

}

Page 228: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 228

Infelizmente, este pedaço de código gera um resultado um pouco estranho: todas as

drawables têm a mesma opacidade. Esse resultado é explicado pela constante estado.

Mesmo que nós estamos começando uma nova instância drawable para cada item da

lista, o constante estado permanece o mesmo e, no caso de BitmapDrawable, a

opacidade é parte da constante estado. Assim, mudando a opacidade de uma instância

muda drawable a opacidade de todas as outras instâncias.

Android 1.5 e superior oferecem uma maneira muito fácil de resolver esse problema

com a nova mutate() método. Quando você chamar esse método em um drawable, a

constante estado de drawable é duplicada para permitir a você alterar qualquer

propriedade, sem afetar outros drawables. Note-se que os bitmaps são ainda comuns,

mesmo depois de uma mutação drawable. O diagrama abaixo mostra o que acontece

quando você chama mutate() em um drawable:

Page 229: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 229

Agora, o código a ser escrito é parecido com o abaixo.

Drawable star = context.getResources().getDrawable(R.drawable.star);

if (book.isFavorite()) {

star.mutate().setAlpha(255); // opaque

} else {

star. mutate().setAlpha(70); // translucent

}

Assim, um exemplo se uso, como o citado acerca dos livros, fica como a figura abaixo.

Truques de layout: criando layouts eficientes

O Android UI Toolkit oferece diversos gerenciadores de layout que são bastante fáceis

de usar e, na maioria das vezes, você só precisa das características básicas destes

gerenciadores de layout para implementar uma interface de usuário.

Ater-se às características básicas infelizmente não é a forma mais eficiente para criar

interfaces de usuário. Um exemplo comum é o abuso de LinearLayout , o que leva a

uma proliferação de pontos de vista e hierarquia de vista. Cada ponto de vista - ou pior,

cada gerente de layout - que você adicionar à sua aplicação tem um custo: layout de

inicialização, e o desenho se torna mais lento. A passagem de layout pode ser muito

Page 230: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 230

cara principalmente quando você aninhar diversos LinearLayout que utilizam o weight

do parâmetro, que requer que o nodo filho seja medido duas vezes.

Consideremos um exemplo muito simples e comum de um esquema: um item da lista

com um ícone no lado esquerdo, um título em cima e uma descrição opcional abaixo do

título.

Uma ImageView e dois TextView são posicionados em relação uns aos outros, aqui é o

wireframe do layout como capturado pelos HierarchyViewer :

A implementação desta estrutura é simples com LinearLayout. O item em si é uma

LinearLayout horizontal com um ImageView e um LinearLayout vertical, que contém

as duas TextView. Aqui está o código fonte deste esquema:

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

android:layout_width="fill_parent"

android:layout_height="?android:attr/listPreferredItemHeight"

android:padding="6dip">

<ImageView

android:id="@+id/icon"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_marginRight="6dip"

android:src="@drawable/icon" />

<LinearLayout

android:orientation="vertical"

android:layout_width="0dip"

android:layout_weight="1"

android:layout_height="fill_parent">

<TextView

android:layout_width="fill_parent"

android:layout_height="0dip"

Page 231: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 231

android:layout_weight="1"

android:gravity="center_vertical"

android:text="My Application" />

<TextView

android:layout_width="fill_parent"

android:layout_height="0dip"

android:layout_weight="1"

android:singleLine="true"

android:ellipsize="marquee"

android:text="Simple application that shows how to use RelativeLayout" />

</LinearLayout>

</LinearLayout>

Este esquema acima funciona, mas pode ser prejudicial se você instanciá-la para cada

item da lista de um ListView . O mesmo esquema pode ser reescrito utilizando um

único RelativeLayout , salvando assim um ponto de vista, e melhor ainda, um nível na

hierarquia do ponto de vista, por item da lista. A implementação do layout com um

RelativeLayout é simples:

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

android:layout_width="fill_parent"

android:layout_height="?android:attr/listPreferredItemHeight"

android:padding="6dip">

<ImageView

android:id="@+id/icon"

android:layout_width="wrap_content"

android:layout_height="fill_parent"

android:layout_alignParentTop="true"

android:layout_alignParentBottom="true"

android:layout_marginRight="6dip"

android:src="@drawable/icon" />

<TextView

android:id="@+id/secondLine"

android:layout_width="fill_parent"

android:layout_height="26dip"

android:layout_toRightOf="@id/icon"

android:layout_alignParentBottom="true"

android:layout_alignParentRight="true"

Page 232: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 232

android:singleLine="true"

android:ellipsize="marquee"

android:text="Simple application that shows how to use RelativeLayout" />

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_toRightOf="@id/icon"

android:layout_alignParentRight="true"

android:layout_alignParentTop="true"

android:layout_above="@id/secondLine"

android:layout_alignWithParentIfMissing="true"

android:gravity="center_vertical"

android:text="My Application" />

</RelativeLayout>

Esta nova aplicação se comporta exatamente da mesma forma que a implementação

anterior, exceto em um caso: o item da lista que se pretende apresentar tem duas linhas

de texto: o título e uma descrição opcional. Quando uma descrição não está disponível

para um determinado item da lista o aplicativo simplesmente define a visibilidade do

segundo TextView como GONE. Isso funciona perfeitamente com o LinearLayout, mas

não com o RelativeLayout:

Em um RelativeLayout, as vistas são alinhadas com a sua mãe, com o

RelativeLayout em si, ou com outras visões. Por exemplo, nós declaramos que a

descrição está alinhado com a parte inferior da RelativeLayout e que o título é

colocado acima da descrição e ancorado a mãe da top. Com a descrição GONE, o

RelativeLayout não sabe a posição da margem inferior do título. Para resolver esse

problema, você pode usar um parâmetro muito especial chamado

layout_alignWithParentIfMissing.

Este parâmetro boolean simplesmente diz ao RelativeLayout a utilizar os seus próprios

limites como âncoras quando um alvo está faltando. Por exemplo, se você posicionar

uma view à direita de uma GONE e definir alignWithParentIfMissing como true,

Page 233: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 233

RelativeLayout vez vai ancorar o fim de sua borda esquerda. No nosso caso, usando

alignWithParentIfMissing fará RelativeLayout alinhar a parte inferior do título

com si mesmo. O resultado é o seguinte:

O comportamento do nosso layout está perfeito, mesmo quando a descrição é GONE.

Ainda melhor, a hierarquia é mais simples porque não estamos usando pesos

LinearLayout's é também mais eficiente. A diferença entre as duas implementações

torna-se evidente quando se comparam as hierarquias em HierarchyViewer:

Page 234: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 234

Truques de layout: usando ViewStub

Compartilhamento e reutilização de componentes de interface do usuário são muito

fáceis com o Android, graças à tag <include/>. Às vezes é tão fácil criar complexas

construções UI que UI termina com um grande número de pontos de vista, algumas das

quais raramente são utilizados. Felizmente, o Android oferece um widget muito especial

chamado ViewStub , que traz todos os benefícios da <include /> sem poluir a interface

do usuário com views raramente utilizadas.

A ViewStub é uma visão leve. Não tem dimensão, não tira nada e não participa no

layout de qualquer forma. Isso significa que uma ViewStub é muito barata para inflar e

muito barata para se manter em uma hierarquia de vista. A ViewStub pode ser melhor

descrita como um preguiçoso <include />. O esquema referenciado por um ViewStub é

inflado e adicionado à interface do usuário somente quando assim o decidir.

A figura a seguir vem da aplicação Prateleira. O principal objetivo da atividade

mostrado na imagem é apresentar ao usuário uma lista pesquisável dos livros:

Page 235: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 235

A atividade também é usada quando o usuário adiciona ou importa novos livros.

Durante essa operação, a Prateleira mostra bits adicionais de interface do usuário. A

imagem abaixo mostra a barra de progresso e um botão cancelar que aparecerá na parte

inferior da tela durante uma importação:

Como a importação de livros não é uma operação comum, pelo menos quando

comparado à visita a lista de livros, o painel de importação é representado inicialmente

por um ViewStub :

Page 236: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 236

Quando o usuário inicia o processo de importação, o ViewStub é inflado e passa a ter o

conteúdo do arquivo de layout que faz referência:

Page 237: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 237

Para usar um ViewStub , tudo que você precisa é especificar um atributo android:id,

para depois inflar, e um atributo android:layout para fazer referência ao arquivo de

layout para incluir e inflar. Um stub permite que você use um terceiro atributo,

android:inflatedId , que pode ser usado para substituir o id da raiz do arquivo incluído.

Finalmente, os parâmetros de layout especificados no topo serão aplicados para a raiz

do layout incluído. Aqui está um exemplo:

<ViewStub

android:id="@+id/stub_import"

android:inflatedId="@+id/panel_import"

android:layout="@layout/progress_overlay"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom" />

Quando estiver pronto para inflar o stub, basta invocar o método inflate(). Você também

pode simplesmente alterar a visibilidade do stub para VISIBLE ou INVISIBLE e o stub

irá inflar. Note, no entanto que o método inflate() tem a vantagem de retornar a raiz

View ao inflar o layout:

((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);

// or

View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

É muito importante lembrar que após o stub ser inflado, o topo é removido da hierarquia

de vista. Como tal, é necessário manter uma referência de vida longa, por exemplo, em

um campo de instância de classe, a uma ViewStub .

Um ViewStub é um grande compromisso entre a facilidade de programação e de

eficiência. Em vez de encher vistas manualmente e adicioná-los em tempo de execução

de sua hierarquia de vista, basta usar um ViewStub. É barato e fácil. A única

desvantagem de ViewStub é que atualmente não suporta a tag <merge />.

Truques de layout: mesclando layouts

A tag <merge /> foi criada com a finalidade de otimizar layouts Android, reduzindo o

número de níveis em árvores de vista. É mais fácil entender o problema que esta tag

resolve olhando um exemplo. O esquema XML a seguir declara um layout que mostra

uma imagem com o título em cima dela. A estrutura é bastante simples, uma

FrameLayout é usada para empilhar um TextView em cima de um ImageView :

Page 238: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 238

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

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ImageView

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:scaleType="center"

android:src="@drawable/golden_gate" />

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="20dip"

android:layout_gravity="center_horizontal|bottom"

android:padding="12dip"

android:background="#AA000000"

android:textColor="#ffffffff"

android:text="Golden Gate" />

</FrameLayout>

Isso torna o layout bem e nada parece errado com ele:

Page 239: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 239

As coisas ficam mais interessantes quando você inspecionar o resultado com

HierarchyViewer. Se você olhar atentamente para a árvore resultante, você vai notar

que o FrameLayout definida no arquivo XML (destacada em azul abaixo) é o filho

único de outro FrameLayout:

Só fizemos a interface mais complexa, sem qualquer razão. Mas como poderíamos nos

livrar do presente FrameLayout? Afinal de contas, documentos XML requerem uma

marca de raiz e tags em esquemas XML sempre representam instâncias.

É aí que o <merge /> vem a calhar. Quando o LayoutInflater encontra essa marca, ele

ignora-o e adiciona o <merge /> dos nodos ao <merge /> pai. Confuso? Vamos

reescrever o nosso layout XML anterior, substituindo o FrameLayout com <merge />:

Page 240: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 240

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

<ImageView

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:scaleType="center"

android:src="@drawable/golden_gate" />

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="20dip"

android:layout_gravity="center_horizontal|bottom"

android:padding="12dip"

android:background="#AA000000"

android:textColor="#ffffffff"

android:text="Golden Gate" />

</merge>

Com esta nova versão, tanto o TextView quanto o ImageView serão adicionados

diretamente ao nível superior. O resultado será o mesmo, mas visualmente a hierarquia

do ponto de vista é simples:

Page 241: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 241

Obviamente, o uso do <merge /> funciona neste caso porque o nodo pai é sempre um

FrameLayout. Você não pode aplicar este truque se o layout estava usando um

LinearLayout como sua marca raiz, por exemplo. O <merge /> pode ser útil em outras

situações, no entanto. Por exemplo, ele funciona perfeitamente quando combinado com

o <include />. Você também pode usar <merge /> quando você cria um composto de

exibição personalizado. Vamos ver como podemos usar essa tag para criar uma nova

visão chamada OkCancelBar que simplesmente mostra dois botões com rótulos

personalizados.

<merge

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

xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">

<ImageView

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:scaleType="center"

android:src="@drawable/golden_gate" />

<com.example.android.merge.OkCancelBar

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom"

android:paddingTop="8dip"

android:gravity="center_horizontal"

android:background="#AA000000"

okCancelBar:okLabel="Save"

okCancelBar:cancelLabel="Don't save" />

</merge>

Page 242: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 242

O código fonte do OkCancelBar é muito simples, porque os dois botões são definidos

em um arquivo XML externo, carregado com um LayoutInflate. Como você pode ver

no trecho a seguir, o esquema XML R.layout.okcancelbar é inflado com o OkCancelBar

como o pai:

public class OkCancelBar extends LinearLayout {

public OkCancelBar(Context context, AttributeSet attrs) {

super(context, attrs);

setOrientation(HORIZONTAL);

setGravity(Gravity.CENTER);

setWeightSum(1.0f);

LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);

String text = array.getString(R.styleable.OkCancelBar_okLabel);

if (text == null) text = "Ok";

((Button) findViewById(R.id.okcancelbar_ok)).setText(text);

text = array.getString(R.styleable.OkCancelBar_cancelLabel);

if (text == null) text = "Cancel";

((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);

Page 243: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 243

array.recycle();

}

}

Os dois botões são definidos no esquema XML a seguir. Como você pode ver, usamos

o <merge /> para adicionar os dois botões diretamente ao OkCancelBar . Cada botão

está incluído a partir do arquivo XML externo mesmo layout para torná-los mais fáceis

de manter, nós simplesmente substituimos o seu id:

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

<include

layout="@layout/okcancelbar_button"

android:id="@+id/okcancelbar_ok" />

<include

layout="@layout/okcancelbar_button"

android:id="@+id/okcancelbar_cancel" />

</merge>

Nós criamos uma forma flexível e fácil de manter a exibição personalizada que gera

uma hierarquia de vista eficiente:

Page 244: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 244

O <merge /> é extremamente útil e pode fazer maravilhas em seu código. No entanto,

ele sofre de um par de limitações:

<merge /> só pode ser usado como a tag raiz de um esquema XML

Ao inflar um layout começando com uma <merge /> você deve especificar um

pai ViewGroup e você deve definir attachToRoot como true

ListView, uma otimização

ListView é um dos widgets mais amplamente utilizados no Android. É bastante fácil de

usar, muito flexível e incrivelmente poderoso.

Um dos problemas mais comuns com ListView acontece quando você tenta usar um

plano personalizado. Por padrão, como muitos widgets do Android, o ListView tem um

fundo transparente, o que significa que você pode ver através do padrão da janela do

fundo. Além disso, ListView permite as margens de desvanecimento por padrão, como

você pode ver no topo da tela a seguir - o texto do primeiro item desvanece-se

gradualmente para preto. Esta técnica é utilizada em todo o sistema para indicar que o

contêiner pode ser rolado.

Page 245: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 245

O efeito de fade é implementado usando uma combinação de Canvas.saveLayerAlpha()

e Porter-Duff Destination Out blending mode.

Infelizmente, as coisas começam a ficarem feias quando você tenta usar um background

personalizado no ListView ou quando você muda a janela de fundo. A seguir duas

imagens mostram o que acontece em um aplicativo quando você mudar o fundo da

janela. A imagem da esquerda mostra como a lista se parece por padrão e a imagem da

direita mostra o como a lista se parece durante um deslocamento iniciado com um gesto

de tocar:

Este problema de processamento é causada por uma otimização do quadro Android

ativada por padrão em todas as instâncias do ListView. Esta aplicação funciona muito

bem, mas infelizmente é muito caro e pode trazer para baixo o desempenho de desenho,

um pouco como ele necessita para capturar uma parcela da prestação em um bitmap fora

da tela e, em seguida, requer a mistura extra (o que implica readbacks da memória).

ListView‟s são, na maioria das vezes exibidos em uma base sólida, não há nenhuma

razão para enveredar por esse caminho caro. É por isso que nós introduzimos uma

otimização chamada de “pitada de cor cache”. A dica de cor cache é uma cor RGB

definido por padrão com a cor de fundo da janela, que é #191919 no tema escuro do

Page 246: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 246

Android. Quando esta dica é definida, ListView (na verdade, sua classe base View) sabe

que vai recorrer a um fundo sólido e substitui, portanto, a cara renderização

saveLayerAlpha()/Porter-Duff por um gradiente simples. Este gradiente vai desde

totalmente transparente para o valor de cor cache e é exatamente isso que você vê na

imagem acima, com o gradiente escuro na parte inferior da lista. No entanto, isso ainda

não explica por que a lista inteira fica em preto durante um pergaminho.

Como mencionado anteriormente, ListView tem um fundo transparente/translúcido por

padrão, assim como todos os widgets padrão na caixa de ferramentas UI Android. Isto

implica que, quando ListView redesenha seus filhos, tem que misturar as crianças com

janela de fundo. Mais uma vez, isto requer readbacks caros de memória que são

particularmente dolorosos durante um deslocamento ou uma aventura, quando acontece

o desenho dezenas de vezes por segundo.

Para melhorar o desempenho de desenho durante as operações de rolagem, o quadro

Android reutiliza a dica de cor cache.

Para corrigir esse problema, tudo que você tem que fazer é desativar o cache de

otimização de cor, se você usar uma cor de fundo não-contínua, ou definir a dica para o

valor de cor sólido adequado. Você pode fazer isso a partir do código ou, de preferência,

a partir de XML, usando o android:cacheColorHint. Para desabilitar a otimização, basta

usar a cor transparente #00000000. A figura abaixo mostra uma lista com

android:cacheColorHint="#00000000" definido no arquivo de layout XML:

Page 247: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 247

Como você pode ver, o fade funciona perfeitamente contra o fundo personalizado em

madeira. O recurso de cache de sugestão de cor é interessante porque mostra como as

otimizações podem tornar sua vida mais difícil em algumas situações. Neste caso

específico, porém, o benefício do comportamento padrão compensa a maior

complexidade.

Live folders

Live Folders, introduzida no Android 1.5 API (Nível 3), permitem a exibição de

qualquer fonte de dados na tela inicial, sem forçar o usuário a lançar uma aplicação.

Uma live folder é simplesmente uma visão em tempo real de um ContentProvider.

Como tal, uma live folder pode ser usada para exibir todos os contatos do usuário ou

bookmarks, e-mail, listas de reprodução, um feed RSS, e assim por diante. As

possibilidades são infinitas!

A plataforma inclui várias pastas padrão para a exibição de contatos. Por exemplo, a

imagem abaixo mostra o conteúdo das pastas ao vivo que mostra todos os contatos com

um número de telefone:

Page 248: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 248

Se a sincronização de contatos acontece em segundo plano enquanto o usuário está

visitando esta pasta ao vivo, o usuário verá a mudança acontecer em tempo real. Live

folders não são apenas úteis, mas elas também são fáceis de adicionar ao seu aplicativo.

Este artigo mostra como adicionar uma live folder para uma aplicação, como por

exemplo as chamadas Prateleiras. Para entender melhor como trabalham as pastas, você

pode baixar o código fonte da aplicação e modificá-lo seguindo as instruções abaixo.

Para dar ao usuário a opção de criar uma nova pasta para um aplicativo, você primeiro

precisa criar uma nova atividade com a intenção de filtro cuja ação é

android.intent.action.CREATE_LIVE_FOLDER. Para isso, basta abrir

AndroidManifest.xml e adicionar algo semelhante a isto:

<activity

android:name=".activity.BookShelfLiveFolder"

android:label="BookShelf">

<intent-filter>

<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</activity>

O rótulo e o ícone desta atividade são o que o usuário verá na tela inicial quando se

escolhe uma pasta:

Page 249: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 249

Uma vez que você só precisa de uma intenção de filtro, é possível e, por vezes

aconselhável, a reutilização de uma atividade existente. No caso de Prateleiras, vamos

criar uma nova atividade,

org.curiouscreature.android.shelves.activity.BookShelfLiveFolder. O papel desta

atividade é enviar um resultado Intent para Página contendo a descrição da live folder: o

seu nome, ícone, modo de apresentação e conteúdo URI. O URI conteúdo é muito

importante, pois descreve o que ContentProvider será usado para preencher a live

folder. O código da atividade é muito simples, como você pode ver aqui:

public class BookShelfLiveFolder extends Activity {

public static final Uri CONTENT_URI = Uri.parse("content://shelves/live_folders/books");

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

final Intent intent = getIntent();

final String action = intent.getAction();

if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {

setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,

"Books", R.drawable.ic_live_folder));

} else {

setResult(RESULT_CANCELED);

}

finish();

}

private static Intent createLiveFolder(Context context, Uri uri, String name, int icon) {

final Intent intent = new Intent();

intent.setData(uri);

intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);

intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,

Intent.ShortcutIconResource.fromContext(context, icon));

intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,

LiveFolders.DISPLAY_MODE_LIST);

return intent;

}

}

Esta atividade, quando invocada com a ACTION_CREATE_LIVE_FOLDER, retorna

com a intenção de um URI, content://shelves/live_folders/books , e três extras para

descrever a pasta ao vivo. Existem outros extras e constantes que você pode usar e você

deve consultar a documentação do android.provider.LiveFolders para mais detalhes.

Page 250: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 250

Quando a Home recebe esta ação, uma nova live folder é criada no desktop do usuário,

com o nome e o ícone que você forneceu. Então, quando o usuário clica na pasta para

abri-la ao vivo, a Home consulta o provedor de conteúdo referenciado pelo URI

fornecido.

Prestadores de pastas Live devem obedecer a regras específicas de nomeação. O Cursor

retornado pelo método query() deve ter pelo menos duas colunas chamadas

LiveFolders._ID e LiveFolders.NAME . O primeiro é o identificador único de cada item

na live folder e o segundo é o nome do item. Há nomes de coluna que você pode usar

para especificar um ícone, uma descrição, a intenção de associar ao item (acionado

quando o usuário clicar nesse item), etc. Novamente, consulte a documentação do

android.provider.LiveFolders para mais detalhes .

No nosso exemplo, tudo o que precisamos fazer é modificar o provedor existente nas

prateleiras chamado org.curiouscreature.android.shelves.provider.BooksProvider.

Primeiro, precisamos modificar o URI_MATCHER para reconhecer nosso

content://shelves/live_folders/books URI de conteúdo:

private static final int LIVE_FOLDER_BOOKS = 4;

// ...

URI_MATCHER.addURI(AUTHORITY, "live_folders/books", LIVE_FOLDER_BOOKS);

Então, precisamos criar um mapa de nova projeção para o cursor. Um mapa de

projeção pode ser usado para "renomear" colunas. No nosso caso, vamos substituir

BooksStore.Book._ID , BooksStore.Book.TITLE e BooksStore.Book.AUTHORS com

LiveFolders._ID , LiveFolders.TITLE e LiveFolders.DESCRIPTION:

private static final HashMap<string, string=""> LIVE_FOLDER_PROJECTION_MAP;

static {

LIVE_FOLDER_PROJECTION_MAP = new HashMap<string, string="">();

LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders._ID, BooksStore.Book._ID +

" AS " + LiveFolders._ID);

LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.NAME, BooksStore.Book.TITLE +

" AS " + LiveFolders.NAME);

LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.DESCRIPTION,

BooksStore.Book.AUTHORS +

" AS " + LiveFolders.DESCRIPTION);

}

Porque estamos a dar um título e uma descrição para cada linha, Home irá exibir

automaticamente cada item da live folder com duas linhas de texto. Finalmente, vamos

Page 251: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 251

implementar a query(), fornecendo o nosso mapa de projeção para o construtor de

consultas SQL:

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

switch (URI_MATCHER.match(uri)) {

// ...

case LIVE_FOLDER_BOOKS:

qb.setTables("books");

qb.setProjectionMap(LIVE_FOLDER_PROJECTION_MAP);

break;

default:

throw new IllegalArgumentException("Unknown URI " + uri);

}

SQLiteDatabase db = mOpenHelper.getReadableDatabase();

Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,

BooksStore.Book.DEFAULT_SORT_ORDER);

c.setNotificationUri(getContext().getContentResolver(), uri);

return c;

}

Agora você pode compilar e implantar o aplicativo, vá para a tela inicial e tente

adicionar uma live folder. Você pode adicionar uma pasta para os livros na sua tela

inicial e quando você abri-lo, veja a lista de todos os seus livros, com seus títulos e

autores, e bastou algumas linhas de código:

Page 252: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 252

A API de live folders é extremamente simples e depende apenas de intenções e URI. Se

você quiser ver mais exemplos de aplicação das pastas, você pode ler o código-fonte do

aplicativo de contatos e do provedor de Contatos.

Live Wallpapers

Começando com o Android 2.1 (API Nível 7), os utilizadores podem agora desfrutar de

papéis de parede ao vivo - mais ricos, animados, cenários interativos - em suas telas

iniciais. Um papel de parede ao vivo é muito semelhante a uma aplicação Android

normal e tem acesso a todas as facilidades da plataforma: SGL (desenho 2D), OpenGL

(desenho 3D), GPS, acelerômetro, acesso à rede, etc. Os papéis de parede ao vivo,

incluído no Nexus One, demonstram o uso de algumas dessas APIs para criar

experiências divertidas e interessantes. Por exemplo, o papel de parede da grama usa a

localização do telefone para calcular o nascer e o por do sol, a fim de exibir o céu

adequado.

Criar o seu próprio papel de parede ao vivo é fácil, especialmente se você teve

experiência anterior com SurfaceView ou Canvas. Para saber como criar um papel de

parede ao vivo, você deve verificar se o código de exemplo CubeLiveWallpaper .

Em termos de execução, um papel de parede ao vivo é muito similar a um Service. A

única diferença é a adição de um novo método, onCreateEngine(), cujo objetivo é criar

um WallpaperService.Engine. O dispositivo é responsável pela gestão do ciclo de vida e

desenho de um papel de parede. O sistema fornece uma superfície sobre a qual você

pode desenhar, assim como você faria com um SurfaceView. Desenho de um papel de

parede pode ser muito caro por isso você deve otimizar o código, tanto quanto possível

para evitar o uso excessivo de CPU, não só para a vida da bateria, mas também para

evitar a abrandar o resto do sistema. É também por isso que a parte mais importante do

ciclo de vida de um papel de parede é quando se torna visível, como indicado por uma

Page 253: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 253

chamada para onVisibilityChanged(). Quando invisíveis, como quando o usuário inicia

um aplicativo que cobre a tela inicial, o papel de parede tem que parar todas as

atividades.

O dispositivo também pode implementar vários métodos para interagir com o usuário

ou o aplicativo de origem. Por exemplo, para reagir ao toque, basta implementar

onTouchEvent(). Finalmente, os aplicativos podem enviar comandos arbitrários para o

papel de parede ao vivo. Atualmente, apenas o pedido inicial padrão envia comandos

para o onCommand() do papel de parede ao vivo:

1. android.wallpaper.tap: Quando o usuário bate um espaço vazio na área de

trabalho. Este comando é interpretado pelo dispositivo para fazer o papel de

parede reagir à interação do usuário.

2. android.home.drop: Quando o usuário solta um ícone ou um widget no espaço

de trabalho.

Se você está desenvolvendo um papel de parede ao vivo, lembre-se que o recurso só é

suportado no Android 2.1 (API nível 7) e versões superiores da plataforma. Para

garantir que seu pedido só pode ser instalado em dispositivos que suportam wallpapers,

lembre-se de acrescentar o seguinte trecho de código antes de publicar no Android

Market:

<uses-sdk android:minSdkVersion="7" />, que indica à Android Market e à

plataforma que seu aplicativo requer Android 2.1 ou superior.

<uses-feature android:name="android.software.live_wallpaper" />, que informa

à Android Market que seu aplicativo inclui um papel de parede ao vivo. Android

Market usa esse recurso como um filtro, ao apresentar listas de usuários de

aplicações disponíveis. Quando você declarar esse recurso, o Android Market

exibe sua aplicação apenas aos usuários cujas dispositivos suportam wallpapers

ao vivo, enquanto oculta de outros dispositivos sobre os quais não seria capaz de

executar.

Page 254: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 254

Usando webViews

Um pequeno aplicativo chamado WebViewDemo mostra como você pode adicionar

conteúdo da Web em seu aplicativo. Você pode encontrá-la no projeto de aplicativos

para Android. Esta aplicação demonstra como você pode incorporar um WebView em

uma atividade e também como você pode ter comunicação bidirecional entre o

aplicativo e o conteúdo da web.

Um WebView utiliza o mesmo processamento e motor de JavaScript, o navegador, mas

ele é executado sob o controle de sua aplicação. O WebView podem ser em tela cheia

ou você pode misturá-la com outras visões. O WebView pode baixar conteúdo da web,

ou pode vir a partir de arquivos locais armazenados em seu diretório de ativos. O

conteúdo pode até ser gerado dinamicamente pelo código do aplicativo.

Este aplicativo não faz muita coisa: quando você clica sobre o Android, ele levanta o

braço.

Isso poderia, naturalmente, ser facilmente conseguido com um pouco de JavaScript.

Em vez disso, porém, WebViewDemo toma um caminho um pouco mais complicado

para ilustrar duas características muito poderosa de WebView.

Primeiro, o JavaScript em execução dentro do WebView pode se ligar com o código em

sua atividade. Você pode usar isso para fazer suas ações disparar JavaScript como

começar uma nova atividade, ou pode ser usada para buscar dados de um banco de

dados ou ContentProvider . A API para isso é muito simples: basta conectar com o

addJavascriptInterface()em sua WebView. Você passa um objeto cujos métodos você

Page 255: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 255

deseja expor ao JavaScript e o nome a ser usado para fazer chamadas. Você pode ver a

sintaxe exata em WebViewDemo.java. Aqui nós estamos fazendo o nosso objeto

DemoJavascriptInterface disponível em JavaScript para onde ele vai ser chamado de

"window.demo".

Em segundo lugar, sua atividade pode invocar métodos JavaScript. Tudo que você tem a

fazer é chamar o método loadUrl com a chamada de JavaScript apropriada:

mWebView.loadUrl("javascript:wave()");

Nosso WebViewDemo utiliza duas técnicas: quando você clica sobre o Android, que

chama a atividade, que então se vira e chama de volta para o JavaScript. WebViews são

muito poderosos e podem ser uma ferramenta valiosa para ajudar a construir a sua

aplicação - especialmente se você já tem um monte de conteúdo HTML. Quando isso

acontece, usamos exatamente essa abordagem em algumas das aplicações que nós

escrevemos.

Funcionalidades

Caixa de pesquisa

Começando com o Android 1.6, a plataforma inclui suporte para caixa de pesquisa

rápida (CPR ou QSB Quick Search Box), uma poderosa estrutura de pesquisa de todo o

sistema. A caixa de pesquisa rápida permite que os usuários rapidamente e facilmente

encontrem o que procuram, tanto em seus dispositivos quanto na web. Ele sugere

conteúdo no seu dispositivo enquanto você digita como aplicativos, contatos, histórico

do navegador, e música. Também oferece resultados das sugestões de pesquisa na web,

anúncios de empresas locais e outras informações do Google, tais como cotações da

bolsa, previsão do tempo e status de vôo. Tudo isso está disponível logo na tela inicial,

tocando na caixa de pesquisa rápida.

Seus aplicativos podem fornecer sugestões de pesquisa que surgirão a usuários de CPR

junto com outros resultados de pesquisa e sugestões. Isso torna possível para os usuários

acessem o conteúdo do seu aplicativo de fora da sua aplicação, por exemplo, a partir da

tela inicial.

Page 256: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 256

Nota: Os fragmentos de código deste documento estão relacionados a um aplicativo de

exemplo chamado Dicionário pesquisável. O aplicativo está disponível para o Android

1.6 e plataformas posteriores.

Plataforma de lançamentos de versões anteriores para o Android 1.6 já previam um

mecanismo que permite que você exponha pesquisa e sugestões de busca na sua

aplicação, conforme descrito na documentação para SearchManager. Esse mecanismo

não mudou e exige as seguintes coisas em sua AndroidManifest.xml :

1. Em sua <activity>, a intenção do filtro, e uma referência a um searchable.xml

arquivo (descritas abaixo):

<intent-filter>

<action android:name="android.intent.action.SEARCH" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

<meta-data android:name="android.app.searchable"

android:resource="@xml/searchable" />

2. Um provedor de conteúdo que pode oferecer sugestões de pesquisa de acordo

com as URIs e formatos de coluna especificada pelo Sugestões de Pesquisa

seção do docs SearchManager.

<!-- Provides search suggestions for words and their definitions. -->

<provider android:name="DictionaryProvider"

android:authorities="dictionary"

android:syncable="false" />

No searchable.xml, você especifica algumas coisas sobre como você deseja

que o sistema de busca apresente a pesquisa para a sua aplicação, incluindo a

autoridade do provedor de conteúdo que oferece sugestões para o usuário

enquanto digitam. Aqui está um exemplo do searchable.xml de um aplicativo

do Android que fornece sugestões de pesquisa dentro de suas próprias

atividades:

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

android:label="@string/search_label"

android:searchSuggestAuthority="dictionary"

android:searchSuggestIntentAction="android.intent.action.VIEW">

</searchable>

Page 257: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 257

No Android 1.6, que adicionou um novo atributo para os metadados conhecido como:

android:includeInGlobalSearch. Especificando-a como "true" no seu searchable.xml,

você permite que QSB a pegar sua pesquisa provedor de conteúdo e dê sugestão de

inclusão (se o usuário permite que suas sugestões a partir das definições de pesquisa do

sistema).

Você também deve especificar um valor de cadeia para

android:searchSettingsDescription, que descreve aos usuários que tipo de sugestões o

seu aplicativo fornece nas configurações do sistema para a pesquisa.

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

android:label="@string/search_label"

android:searchSettingsDescription="@string/settings_description"

android:includeInGlobalSearch="true"

android:searchSuggestAuthority="dictionary"

android:searchSuggestIntentAction="android.intent.action.VIEW">

</searchable>

O mais importante e primeira coisa a notar é que quando um usuário instala um

aplicativo com um provedor de sugestão que participam no CPR, esta nova aplicação

não será ativado por padrão para o CPR. O usuário pode optar por ativar as fontes de

sugestão especial, as configurações do sistema para a pesquisa (clicando em "Pesquisar"

> "itens pesquisáveis" nas configurações).

Você deve pensar em como lidar com isso em sua aplicação. Talvez mostrar um aviso

de que instrui o usuário a visitar as configurações do sistema e permitir as sugestões do

seu aplicativo.

Quando o usuário habilita o item pesquisado, sugestões do aplicativo terão a chance de

aparecer na QSB. Como sugestões de sua aplicação são escolhidas com mais freqüência,

eles podem se mover para cima na lista.

Page 258: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 258

Um dos nossos objetivos com o CPR é torná-lo mais rápido para que os usuários

acessem as coisas com mais freqüência. Uma maneira que fizemos isso é 'shortcutting'

algumas das sugestões de pesquisa previamente escolhido, então eles serão mostrados

imediatamente quando o usuário começa a digitar, ao invés de esperar para consultar os

fornecedores de conteúdos. Sugestões de sua aplicação podem ser escolhidas como

atalhos quando o usuário clica neles.

Para sugestões de dinâmicas que podem querer alterar o seu conteúdo (ou se tornar

inválido), no futuro, você pode fornecer um 'id atalho‟.

Sistema

Alocação de memória

Escrever eficazes aplicações móveis nem sempre é fácil. Em particular, aplicações

Android dependem de gerenciamento automático de memória manipulado pelo coletor

de lixo Dalvik, que por vezes pode causar problemas de desempenho se você não for

cuidadoso com as alocações de memória.

Em um caminho de código de desempenho sensíveis, tais como o método de layout ou

desenho de uma vista ou o código da lógica de um jogo, qualquer atribuição tem um

preço. Depois de muitas atribuições, o coletor de lixo vai começar e parar o aplicativo

para deixá-lo liberar memória. Na maioria das vezes, as coletas de lixo acontecem

rápidas o suficiente para você não perceber. No entanto, se uma coleção acontece

enquanto você estiver percorrendo uma lista de itens ou enquanto você está tentando

Page 259: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 259

derrotar um inimigo em um jogo, você pode de repente ver uma queda no desempenho/

capacidade de resposta do aplicativo. Não é incomum para uma coleta de lixo levar de

100 a 200 ms. Para comparação, uma animação suave precisa desenhar cada quadro em

16 a 33 ms. Se a animação é subitamente interrompida por 10 quadros, você pode estar

certo que os usuários irão notar.

Na maioria das vezes, a coleta de lixo ocorre por causa de toneladas de objetos

pequenos, de curta duração e alguns coletores de lixo, como catadores de lixo de

gerações, que podem otimizar a coleta desses objetos para que o aplicativo não se

interrompa com muita freqüência. O coletor de lixo Android infelizmente não é capaz

de realizar tais otimizações e na criação de objetos de curta duração em caminhos de

código crítico de desempenho é, portanto, muito caro para sua aplicação.

Para ajudar a evitar freqüentes coletas de lixo, o SDK do Android é embarcado com

uma ferramenta muito útil chamado allocation tracker. Esta ferramenta é parte do

DDMS, que você já deve ter usado para fins de depuração. Para começar a usar o

tracker de atribuição, primeiro você deve lançar a versão autônoma do DDMS, que pode

ser encontrado na tools/ do SDK. A versão do DDMS incluído no Eclipse plugin não

oferece capacidade de usar o tracker de atribuição ainda.

Depois DDMS estar funcionando, basta selecionar o seu processo de candidatura e, em

seguida, clique na guia Atribuição Tracker. Na nova visão, clique em Iniciar

monitoramento e então usar o aplicativo para torná-lo executável para os caminhos de

código que você deseja analisar. Quando estiver pronto, clique em Obter atribuições.

Uma lista de objetos alocados será mostrada no primeiro quadro. Ao clicar em uma

linha você pode ver, na segunda tabela, o rastreamento de pilha que levaram à

atribuição. Não somente você saberá que tipo de objeto foi alocado, mas também em

qual segmento, em que classe, em qual arquivo e em qual linha. A figura abaixo mostra

as atribuições realizadas por prateleiras enquanto estiver rolando um ListView.

Page 260: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 260

O tracker de atribuição ajudará você a identificar questões importantes em seu código.

Por exemplo, um erro comum que tenho visto em muitas aplicações é a criação de uma

novo objeto Paint em cada sorteio. Mover a pintura em um campo de instância é uma

solução simples que ajuda no desempenho de um lote. Eu altamente encorajo-vos a

examinar o código fonte do Android para ver como podemos reduzir dotações em

caminhos de código crítico de desempenho. Você também vai descobrir o Android,

assim, oferecer APIs para ajudar você a reutilizar objetos.

Zipaling oferece uma otimização fácil

O Android SDK inclui uma ferramenta chamada zipalign que otimiza a forma como um

aplicativo é empacotado. Rodando Zipalign em seu aplicativo permite que o Android

possa interagir de forma mais eficiente em tempo de execução e, portanto, tem o

potencial para fazê-lo executar o sistema global mais rápido.

No Android, arquivos de dados armazenados em APK de cada aplicativo são acessados

por vários processos: o instalador lê o manifesto para lidar com as permissões

associadas com o pedido; a aplicação inicial lê recursos para obter o nome do aplicativo

Page 261: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 261

e um ícone; o sistema servidor lê os recursos para uma variedade de razões (por

exemplo, para exibir as notificações que aplicativo), e por último mas não menos

importante, os arquivos de recurso são, obviamente, utilizado pelo próprio aplicativo.

Os códigos de manipulação de recursos no Android podem acessar os recursos de forma

eficiente quando estão alinhados em limites de 4 bytes de memória. Mas, para recursos

que não estão alinhados (ou seja, quando zipalign não tenha sido executado em um

APK), ele tem que cair de volta a expressamente e lê-los - o que é mais lento e consome

memória adicional.

Para um desenvolvedor de aplicativos, este mecanismo de retorno é muito conveniente.

Ele oferece uma grande flexibilidade, permitindo o desenvolvimento de vários métodos

diferentes, incluindo aqueles que não incluem a agregação de recursos como parte de

seu fluxo normal.

Infelizmente, para os usuários a situação é inversa - os recursos de leitura de APKs

desalinhados são lentos e exigem muito da memória. No melhor dos casos, o único

resultado visível é que tanto a aplicação inicial quanto o lançamento de aplicações

desalinhadas são mais lentos do que deveria. No pior dos casos, a instalação de várias

aplicações com recursos de memória não alinhadas aumentam a pressão, causando

assim problemas para o sistema por ter que constantemente iniciar e matar processos. O

usuário acaba com um dispositivo lento, com uma duração de bateria fraca.

Felizmente, é muito fácil para o alinhar recursos em sua aplicação:

Usando ADT:

o A ADT, plugin para o Eclipse (a partir da versão 0.9.3), alinhará

automaticamente os pacotes de aplicativos libertados se a assistente de

exportação é usado para criá-las.

Usando Ant:

o O script de construção Ant (a partir do Android 1.6) pode alinhar os

pacotes de aplicativos. Metas para versões mais antigas da plataforma

Android não são alinhados pelo script de construção Ant e precisam ser

manualmente alinhados.

Page 262: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 262

o A partir dos 1,6 Android SDK, Ant alinha pacotes de sinais

automaticamente, quando está construindo o modo de depuração.

o No modo de lançamento, Ant alinha as embalagens se tiver informação

suficiente para assinar os pacotes, uma vez que o alinhamento deve

acontecer após a assinatura. A fim de poder assinar os pacotes, e,

portanto, para alinhá-los, Ant precisa saber a localização do

armazenamento de chaves e o nome da chave na build.properties. O

nome das propriedades são key.store e key.alias. Se as propriedades

estiverem ausentes, o pacote de lançamento não será assinado e,

portanto, não vai se alinhar também.

Manualmente:

o A fim de alinhar manualmente um pacote, zipalign está no tools/ do

Android SDK 1.6 e posteriores. Você pode usá-lo para alinhar os pacotes

de aplicativos voltados para qualquer versão do Android. Você deve

executá-lo somente após a assinatura do arquivo APK, usando o seguinte

comando:

zipalign -v 4 source.apk destination.apk

Verificando o alinhamento:

o O comando a seguir verifica se um pacote está alinhado:

zipalign -c -v 4 application.apk

Page 263: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 263

Nota

Todo o conteúdo foi retirado de <http://android.com> e correlatos sendo

disponibilizado através da licença Apache 2.0 e traduzido com auxílio de

<http://translate.google.com.br> com posterior revisão.

Fontes

About the Android Open Source Project. Disponível em:

<http://source.android.com/about/index.html>. Acesso em: 06 abril 2011.

Activities. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/activities.html>. Acesso em:

22 março 2011.

Application Resources. Disponível em:

<http://developer.android.com/guide/topics/resources/index.html>. Acesso em: 05 abril

2011.

Applying Styles and Themes. Disponível em:

<http://developer.android.com/guide/topics/ui/themes.html>. Acesso em: 04 abril 2011.

Bound Services. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/bound-services.html>. Acesso

em: 25 março 2011.

Creating Dialogs. Disponível em:

<http://developer.android.com/guide/topics/ui/dialogs.html>. Acesso em: 01 abril 2011.

Creating Menus. Disponível em:

<http://developer.android.com/guide/topics/ui/menus.html>. Acesso em: 30 março

2011.

Creating Status Bar Notifications. Disponível em:

<http://developer.android.com/guide/topics/ui/notifiers/notifications.html>. Acesso em:

04 abril 2011.

Page 264: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 264

Creating Toast Notifications. Disponível em:

<http://developer.android.com/guide/topics/ui/notifiers/toasts.html>. Acesso em: 04

abril 2011.

Data Backup. Disponível em:

<http://developer.android.com/guide/topics/data/backup.html>. Acesso em: 06 abril

2011.

Data Storage. Disponível em:

<http://developer.android.com/guide/topics/data/data-storage.html>. Acesso em: 05

abril 2011.

Declaring Layout. Disponível em:

<http://developer.android.com/guide/topics/ui/declaring-layout.html>. Acesso em: 29

março 2011.

Fragments. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/fragments.html>. Acesso em:

22 março 2011.

Handling UI Events. Disponível em:

<http://developer.android.com/guide/topics/ui/ui-events.html>. Acesso em: 04 abril

2011.

How Android Draws Views. Disponível em:

<http://developer.android.com/guide/topics/ui/how-android-draws.html>. Acesso em:

04 abril 2011.

Loaders. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/loaders.html>. Acesso em: 23

março 2011.

Notifying the User. Disponível em:

<http://developer.android.com/guide/topics/ui/notifiers/index.html>. Acesso em: 04

abril 2011.

Processes and Threads. Disponível em:

Page 265: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 265

<http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html>.

Acesso em: 25 março 2011.

Page 266: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 266

Services. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/services.html>. Acesso em:

25 março 2011.

Tasks and Back Stack. Disponível em:

<http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html>.

Acesso em: 24 março 2011.

User Interface. Disponível em:

<http://developer.android.com/guide/topics/ui/index.html>. Acesso em: 29 março 2011.

Using the Action Bar. Disponível em:

<http://developer.android.com/guide/topics/ui/actionbar.html>. Acesso em: 30 março

2011.

Fontes das imagens

Androids. Disponível em:

<http://www.android.com/media/wallpaper/gif/androids.gif>. Acesso em: 06 abril 2011.

Untitled. Disponível em:

<http://t3.gstatic.com/images?q=tbn:ANd9GcSknkUiTjwIBqS6dGp8k5IR2ihXI1ipyrols

9PHvLL2nq9GLVd0lg&t=1>. Acesso em: 04 abril 2011.

Fontes dos artigos

Accessibility Service. Disponível em:

<http://developer.android.com/resources/samples/AccessibilityService/index.html>.

Acesso em: 18 março 2011.

Drawable Mutations. Disponível em:

<http://developer.android.com/resources/articles/drawable-mutations.html>. Acesso em:

18 março 2011.

Gestures. Disponível em:

<http://developer.android.com/resources/articles/gestures.html>. Acesso em: 18 março

2011.

Page 267: ANDROID Uma Visao Geral

ANDROID, uma visão geral – Anderson Duarte de Amorim 267

Layout Tricks: Creating Efficient Layouts. Disponível em:

<http://developer.android.com/resources/articles/layout-tricks-efficiency.html>. Acesso

em: 21 março 2011.

Layout Tricks: Merging Layouts. Disponível em:

<http://developer.android.com/resources/articles/layout-tricks-merge.html>. Acesso em:

21 março 2011.

Layout Tricks: Using ViewStubs. Disponível em:

<http://developer.android.com/resources/articles/layout-tricks-stubs.html>. Acesso em:

21 março 2011.

Live Folders. Disponível em:

<http http://developer.android.com/resources/articles/live-folders.html>. Acesso em: 21

março 2011.

Live Wallpapers. Disponível em:

<http://developer.android.com/resources/articles/live-wallpapers.html>. Acesso em: 21

março 2011.

ListView Backgrounds: An Optimization. Disponível em:

<http://developer.android.com/resources/articles/listview-backgrounds.html>. Acesso

em: 21 março 2011.

Touch Mode. Disponível em:

<http://developer.android.com/resources/articles/touch-mode.html>. Acesso em: 21

março 2011.

Using Text-to-Speech. Disponível em:

<http://developer.android.com/resources/articles/tts.html>. Acesso em: 18 março 2011.

Zipalign: an Easy Optimization. Disponível em:

<http://developer.android.com/resources/articles/zipalign.html>. Acesso em: 21 março

2011.