Escolar Documentos
Profissional Documentos
Cultura Documentos
ANDROID, Umavisãogeral 1.0 PDF
ANDROID, Umavisãogeral 1.0 PDF
2011
Conteúdo
Introdução ....................................................................................................................... 10
Atividades ....................................................................................................................... 11
Fragmentos ..................................................................................................................... 27
DialogFragment ...................................................................................................... 30
ListFragment ........................................................................................................... 31
PreferenceFragment ................................................................................................ 31
Loaders ........................................................................................................................... 43
Reiniciando o Loader.............................................................................................. 46
Exemplo ...................................................................................................................... 50
Serviços .......................................................................................................................... 66
O básico ...................................................................................................................... 67
O básico ...................................................................................................................... 84
Vinculação a um serviço............................................................................................. 93
Processos .................................................................................................................... 98
ID .......................................................................................................................... 119
Acessibilidade........................................................................................................... 210
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.
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
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.
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
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.
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.
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:
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
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:
@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);
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.
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.
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".)
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.
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 figura 1 ilustra esses laços e os caminhos de uma atividade que podem levar a esses
estados.
Killable
Método Descrição Seguinte
depois?
Chamado quando a atividade
é criada pela primeira vez.
Isto é onde você deve fazer
tudo do seu conjunto estático
onCreate()
normal - criar pontos de vista, Não onStart()
vincular dados em listas, e
assim por diante. Sempre
seguido por onStart() .
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.
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).
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.
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ê
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.
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:
3. Então, se uma atividade não é mais visível na tela, a sua onStop() é executada.
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
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.
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
Criando um fragmento
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.
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.
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.
PreferenceFragment
Exibe uma hierarquia de objetos Preference como uma lista, semelhante à
PreferenceActivity. Isso é útil quando se cria um "settings" para sua aplicação.
Por exemplo, aqui está uma subclasse de Fragment que carrega um layout a partir da
example_fragment.xml:
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:
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:
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.
Depois que você fizer as alterações com FragmentTransaction , você deve chamar
commit() para que as alterações tenham efeito.
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().
Por exemplo, aqui está como você pode substituir um fragmento a outro e preservar o
estado anterior da pilha 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().
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.
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().
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:
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.
Classe/Interface Descrição
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.
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.
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:
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.
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.
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.
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:
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.
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 .
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.
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ó
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.
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
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
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
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.
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:
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
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.
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.
FLAG_ACTIVITY_NEW_TASK
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.
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.
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.
Há alguns atributos de atividade que você pode usar para modificar esse
comportamento:
alwaysRetainTaskState
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.
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".
Iniciado
Ligado
O básico
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()
onCreate()
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.
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).
Para declarar seu serviço, adicione um elemento <service> como um filho do elemento
<application>. Por exemplo:
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.
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.
deve parar quando seu trabalho é feito versões do Android superiores a 2.0,
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.
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.
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).
/**
* 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) {
}
@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).
@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();
@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);
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
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).
START_NOT_STICKY
START_STICKY
START_REDELIVER_INTENT
Por exemplo, uma atividade pode iniciar o serviço de exemplo na seção anterior
(HelloSevice) com a intenção explícita com startService():
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.
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.
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().
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, 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
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.
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).
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.
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.
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
Um serviço vinculado
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.
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().
@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
}
}
Ao implementar esses métodos, você pode controlar dois loops aninhados do ciclo de
vida do serviço:
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.
O básico
Um serviço ligado é uma
implementação da classe Vinculação a um serviço iniciado
Service que permite que Conforme discutido no capítulo sobre Serviço, você pode
outros aplicativos se liguem e criar um serviço que seja iniciado e vinculado. Ou seja, o
interajam com ele. Para serviço pode ser iniciado chamando startService(), que
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.
Usando AIDL
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.
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
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:
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@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) {
/** 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();
}
}
@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().
O Handler é usado para criar uma Messenger (que é uma referência para o
Handler).
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.
/**
* Handler of incoming messages from clients.
/**
* 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();
}
}
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:
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
@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;
}
}
}
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.
1. Implementar ServiceConnection .
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;
}
Com este ServiceConnection, o cliente pode ligar para um serviço, passando este para
bindService(). Por exemplo:
Notas adicionais
Aqui estão algumas anotações importantes sobre a ligação a um serviço:
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).
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.
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.
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.
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.
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.
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
5. Processo vazio
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.
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.
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:
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)
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.
/** 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);
}
}
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:
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.
Para executar o IPC, o aplicativo deve ligar para um serviço, utilizando bindService().
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.
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.
A passagem de medidas utiliza duas classes para Para dar início a um esquema, chama-
comunicar dimensões. A classe se requestLayout(). Este método
View.MeasureSpec é usada por views para normalmente é chamado por uma view
contar aos pais como eles querem ser medidos e sobre si mesma quando se acredita que
é possível não caber mais dentro de
posicionados. A classe base LayoutParams
seus limites atuais.
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).
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:
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:
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
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,
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.
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.
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 :
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 é:
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.
<Button android:id="@+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/my_button_text"/>
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.
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.
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:
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.
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().
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().
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.
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.
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:
<item>
<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.
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
android:title
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.game_menu, menu);
return true;
}
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.
@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);
}
}
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.
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.
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.)
@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);
}
@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);
}
}
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.
Grupos de Menu
Um grupo de menu é uma coleção de itens de menu que compartilham certas
características.
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.
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:
single
all
none
@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.
Para adicionar itens de menu baseado em atividades disponíveis que aceitam uma
intenção:
Se não houver aplicativos instalados que satisfazem a intenção, então não há itens de
menu que sejam adicionados.
Por exemplo:
@Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
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.
Leia mais sobre como escrever filtros de intenção em Intents and Intents Filters.
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.
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.
<activity android:theme="@android:style/Theme.Holo.NoActionBar">
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:
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.
Figura 2. Uma barra de ação com dois itens de ação e menu de estouro.
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:
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.
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.
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.
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.
@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);
}
}
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
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);
}
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.
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().
@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
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.
@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
}
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);
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.
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.
Aqui está uma lista rápida de medidas para permitir a navegação drop-down:
Nota: Você deve executar isto durante a sua atividade do método onCreate().
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
@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;
}
};
@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;
}
}
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()
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
android:actionBarTabBarStyle
Estilo para a barra que aparece abaixo das abas na barra de ação.
android:actionBarTabTextStyle
android:actionDropDownStyle
android:actionButtonStyle
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:
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.
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
TimePickerDialog
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.
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.
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().
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.
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.
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
Para criar um AlertDialog com botões lado a lado, como a mostrada na imagem à
direita, use o método set...Button():
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ê.
Para criar um AlertDialog com uma lista de itens selecionáveis como o mostrado à
esquerda, use o método setItems():
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():
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:
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.
ProgressDialog progressDialog;
progressDialog = new ProgressDialog(mContext);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading...");
progressDialog.setCancelable(false);
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;
@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);
}
}
};
ProgressThread(Handler h) {
mHandler = h;
}
<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>
dialog.setContentView(R.layout.custom_dialog);
dialog.setTitle("Custom Dialog");
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.
AlertDialog.Builder builder;
AlertDialog alertDialog;
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.
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.
onClick()
onLongClick()
onFocusChange()
onKey()
onTouch()
onCreateContextMenu()
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
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:
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ê:
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().
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:
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:
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
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.
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().
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().
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:
Cada uma dessas tarefas de notificação podem ser conseguidas usando uma técnica
diferente:
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.
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:
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.
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.
<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"
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.
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
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().
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager)
getSystemService(ns);
2. Instanciar a notificação:
mNotificationManager.notify(HELLO_ID, notification);
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().
Uma mensagem expandida e título para o modo expandido (a menos que você
defina uma exibição personalizada ampliada)
Um som de alerta
// 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.
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
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.
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");
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.
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 definir o seu padrão de vibração própria, passe uma matriz de valores longos para o
campo vibrar:
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.
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.
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"
"FLAG_INSISTENT"
Adicione isto ao campo de sinalizadores para repetir o áudio até que o usuário
responda.
"FLAG_ONGOING_EVENT"
"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
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>
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.
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.
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" />
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/.
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
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:
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>
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.
<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.
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
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">.
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.
Agora este TextView será denominado como definido pelo estilo chamado CodeFont.
(Veja o exemplo acima, em Definição de estilos).
<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">
<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.)
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:
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.
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.
Para qualquer tipo de recurso, você pode especificar padrão e vários recursos
alternativos para a sua aplicação:
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.
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.
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.
Preferências compartilhadas
Armazenamento interno
Armazenamento externo
Conexão de rede
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.
Aqui está um exemplo que salva uma preferência para o modo silencioso keypress em
uma calculadora:
@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);
}
Por exemplo:
getFilesDir()
getDir()
deleteFile()
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;
}
/Android/data/<package_name>/files/
aplicação e que não devem ser ao nome do arquivo). Isto irá prevenir o media
/Android/data/<package_name>/cache/
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:
DictionaryOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DICTIONARY_TABLE_CREATE);
}
}
consulta. O Cursor é sempre o mecanismo com o Isso não é necessária para dados
qual você pode navegar pelos resultados de uma privados, mas se você
implementar um provedor de
consulta de banco de dados e ler linhas e colunas.
conteúdo, você deve incluir uma
Para aplicativos de exemplo que demonstram como identificação exclusiva com a
usar banco de dados SQLite no Android, consulte as constante BaseColumns._ID.
java.net.*
android.net.*
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.
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")))
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.
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:
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
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.
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.
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
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 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:
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.
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).
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"/>
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>
Para a personalização final, vamos olhar para a "ação" de botões no IME. Existem
atualmente dois tipos de ações:
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.
android:imeOptions="actionSend|flagNoEnterAction"
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.
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.
InputConnection ic = getCurrentInputConnection();
long eventTime = SystemClock.uptimeMillis();
ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
InputMethodService.sendDownUpKeyEvents(keyEventCode);
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, 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ê
Book = ...;
TextView Item_da_lista = ...;
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:
Assim, um exemplo se uso, como o citado acerca dos livros, fica como a figura abaixo.
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
Uma ImageView e dois TextView são posicionados em relação uns aos outros, aqui é o
wireframe do layout como capturado pelos HierarchyViewer :
<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"
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"
<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>
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.
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 :
<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 .
<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"
</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 />:
<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"
</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:
<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>
text = array.getString(R.styleable.OkCancelBar_cancelLabel);
if (text == null) text = "Cancel";
((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
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:
<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
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.
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:
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
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:
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:
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:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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;
}
}
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:
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
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:
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 .
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:
Este aplicativo não faz muita coisa: quando você clica sobre o Android, ele levanta o
braço.
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.
<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" />
<!-- Provides search suggestions for words and their definitions. -->
<provider android:name="DictionaryProvider"
android:authorities="dictionary"
android:syncable="false" />
<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>
<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>
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.
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.
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.
Usando ADT:
Usando Ant:
Manualmente:
Verificando o alinhamento:
Fontes
About the Android Open Source Project. Disponível em:
<http://source.android.com/about/index.html>. Acesso em: 06 abril 2011.