Quarta sequência de slides do curso de Android da Especializa Treinamentos
http://www.especializa.com.br/curso/android
Assunto desta aula:
- Application (Mais detalhes do AndroidManifest.xml)
- Activity
- Fragment
2. Ementa
1. Introdução ao Android
2. Ambiente de desenvolvimento
3. Conceitos básicos
4. Application, Activities e Fragments
5. Intents e Broadcast Receivers
6. Views e Layout Managers
7. Persistência de dados e Content Providers
8. Shared Preferences
2
3. Ementa
Application, Activities e Fragments
Android Application
Mais sobre o Manifest.xml
Activities
Pilhas, estados e ciclo de vida
Monitoramento de estados e LogCat
Activity subclasses
Fragments
Estratégias de fragments em tablets x smartphones
Ciclo de vida
Fragment subclasses e suporte a APIs antigas
3
4. O que faz uma aplicação?
Tipos de objetos que fazem uma aplicação Android:
Activity
Objetos de classes que herdem de
android.app.Activity
Controladora da interface de cada tela
Activities carregam Views e Fragments
para exibir conteúdo ao cliente
Service
Objetos do tipo android.app.Service
Trabalhadores invisíveis que executam tarefas em background
Atualizam Activities, disparam Notifications e difundem Intents
Suas tarefas em geral são longas ou constantes sem nenhuma
interação direta com o usuário
4
5. O que faz uma aplicação?
Tipos de objetos que fazem uma aplicação Android:
View
Objetos de classes que herdem de
android.view.View
Representa cada componente na tela
Gestores de layout herdam de sua subclasse
abstrata android.view.ViewGroup
Fragment
Controladores de trechos de telas criados
desde os primeiros tablets Android quando
viram que uma Activity só para controlar
essas telas maiores ficaria muito extensa
São carregados por Activities, mas possuem
seu próprio ciclo de vida
5
6. O que faz uma aplicação?
Tipos de objetos que fazem uma aplicação Android:
Intent
Objetos do tipo android.content.Intent
Mecanismo de troca de mensagens interactivities e até mesmo
interaplicações
São extensamente usadas para abrir ou encerrar Activities ou
Services, enviar mensagens em broadcast e muito mais
Broadcast Receiver
Intent listeners
Fazem a aplicação reagir a determinadas Intents publicadas
Constroem uma aplicação orientada a eventos
6
7. O que faz uma aplicação?
Tipos de objetos que fazem uma aplicação Android:
Notification
Possibilitam que sua aplicação alerte o usuário de determinada
ação sem interromper o que ele estiver fazendo no momento
Publicam na barra de notificações, em geral a partir de um
Service ou Broadcast Receiver
Content Provider
Objetos do tipo android.content.ContentProvider
Mecanismo compartilhável de armazenamento de dados que interage
com bancos SQL
Há diversos content providers nativos que nos dão acesso a bases
como lista de contatos e mídias armazenadas
7
8. O que faz uma aplicação?
Tipos de objetos que fazem uma aplicação Android:
Widget
Componentes visuais que rodam direto na home screen
São uma variação especial de Broadcast Receiver, permitindo
interação com o usuário e execução direta
Live Wallpaper
Serviços com interatividade na home screen
Widgets de fundos de tela responsivos
8
9. Mais sobre o Manifest.xml
Outros atributos da tag raiz <manifest>
package - determina o nome único do projeto. É
recomendado que você utilize o padrão do DNS invertido
finalizando com o nome do projeto
android:* - prefixo XML que carrega nós definidos no
namespace http://schemas.android.com/apk/res/android
installLocation - Local default de instalação da aplicação.
Disponível desde a API 8 (Froyo - 2.2)
internalOnly - Só admite que a aplicação seja instalada na
memória interna do dispositivo
preferExternal - Sugere que a aplicação seja instalada no SD
card. Se esta estiver cheia ou indisponível no momento da
instalação, o sistema vai instalar na memória interna
auto - Utiliza as definições padrão do cliente
9
10. Mais sobre o Manifest.xml
Local de instalação da aplicação
Instalar a app no SD card é bom por contar com mais espaço
Mas quando o usuário utilizar o aparelho como USB mass
storage, todas as apps instaladas no SD card serão encerradas
Activities são fechadas
Services são encerrados
Alarmes são cancelados
Widgets são removidos e Live Wallpapers são substituídos pelo
default. Só voltam após o reinício da aplicação home. Em geral,
quando ele reiniciar o aparelho
A app não volta até a intervenção do usuário ou se estiver
inscrita via BroadcastReceiver na Intent do sistema
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
10
11. Mais sobre o Manifest.xml
Apps que compartilham o mesmo usuário
Cada app roda no Android em seu usuário único e seus
recursos não são compartilhados para outros usuários
O atributo sharedUserId pode ser usado para definir
explicitamente uma string com o UID do usuário
Duas apps podem definir o mesmo UID usando este atributo
Têm acesso aos mesmos recursos como bases de dados
Quando uma app chamar uma Activity de outra, esta segunda só
rodará no mesmo processo (compartilhando até o que houver em
memória) se tiver o mesmo sharedUserId
As duas apps devem ter sido assinadas pelo mesmo certificado
O atributo sharedUserString define um rótulo para o usuário
11
12. Mais sobre o Manifest.xml
Tag <uses-configuration>
Filtram sua aplicação para só funcionar se tais configurações
estiverem presentes
Notadamente úteis para games com jogabilidade única ou
avançada
reqTouchScreen - Restringe a determinado tipo de touchscreen
undefined, notouch, stylus (requer touchscreen com suporte a caneta) e
finger
reqKeyboardType - Restringe a deteminado tipo de teclado
undefined, nokeys, qwerty (exige um teclado padrão qwerty - com as
letras q, w, e, r, t e y na primeira linha) e twelvekey (exige um teclado
padrão de simples celulares - números, * e #)
12
13. Mais sobre o Manifest.xml
Tag <uses-configuration>
reqHardKeyboard - Booleano. Exige ou não presença de
teclado físico. Caso preciso de um tipo específico, determine
também reqKeyboardType
reqNavigation - Restringe à presença de determinado
mecanismo de navegação
undefined, nonav, dpad (diretional pad), trackball, wheel
reqFiveWayNav - Booleano. Exige ou não que o dispositivo
tenha recursos de navegação direcional como trackball ou dpad
13
14. Mais sobre o Manifest.xml
Tags <uses-feature>
Indica que sua aplicação só estará disponível se o hardware
tiver determinada característica.
Úteis para quando for imprescindível o uso de algum ou alguns
recursos não disponíveis em todos os aparelhos
O ideal é você nunca precisar citar nada aqui, mas caso
necessite é bom restringir para evitar clientes frustrados
O Google Play filtra a sua aplicação para só aparecer para
quem estiver com hardware compatível com esta tag
As características podem ser exigidas ou apenas
recomendadas, mas todas as que precisarem de permissão
do usuário são, por padrão, consideradas exigidas
Para desligar esse filtro de exigência utilize o atributo
required=“false”
14
15. Mais sobre o Manifest.xml
Tags <uses-feature>
Atributos:
name - especifica qual o recurso que você precisa.
Ex.: android.hardware.bluetooth
Podem ser de hardware:
Audio, bluetooth, camera, location, microphone, nfc, sensor, screen,
telephony, television, touchscreen, usb, wifi
Ou sofware:
live_wallpaper e sip (voip)
Você pode conferir a lista completa aqui:
developer.android.com/guide/topics/manifest/uses-feature-
element.html#features-reference
15
16. Mais sobre o Manifest.xml
Tags <uses-feature>
Atributos:
required - determina se a característica é apenas uma boa
indicação ou se chega a ser uma exigência
glEsVersion - informa se há uma recomendação de versão do
OpenGL ES (para gráficos). O valor é numérico de 32 bits
expresso na base hexa, sendo os 16 primeiros reservados ao
maior número da versão e os outros 16 ao menor número.
glEsVersion=“0x00020001” - para a versão 2.1 (0002 -> 2 e 0001 ->1)
Exemplos:
<uses-feature android:name="android.hardware.camera"
android:required="false"/>
<uses-feature android:glEsVersion="0x00020001"
android:required="true" />
16
17. Mais sobre o Manifest.xml
Tags <uses-permission>
Declara quais permissões a
aplicação vai solicitar do usuário
antes de ser instalada
Basicamente, todas as
operações que implicarem em
custos ou questões de
segurança precisam ser
apresentadas ao cliente
Traz apenas o atributo name e a lista
possível nele é enorme
17
18. Activity
Principal classe para aplicativos que precisam da
interação com o usuário
Declaração mais básica de uma Activity no manifest
Só é
<activity android:label="@string/app_name" necessário .Classe por
android:name=".MainActivity"> causa do package de
</activity> <manifest>
Construção fundamental da classe
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
}
}
18
19. Pilhas de Activities
As activities são organizadas em pilhas (Last in first out)
O estado delas é determinado pela sua posição na pilha
Quando uma activity inicia, ela fica ativa no topo da pilha,
quando o usuário aciona o botão back, a activity logo abaixo na
pilha se torna ativa.
19
20. package br.com.especializa.minhaprimeiraapp;
import android.os.Bundle; Código gerado
import android.app.Activity; pelo Hello World
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
A tarefa mais básica e comum do método onCreate é carregar alguma
View através do método setContentView(). Ele vai instanciar os objetos
Java referentes a cada tag desse layout e definir o que será exibido assim
que Activity chegar no modo Running
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
20
22. Estados de uma Activity
activity iniciada
métodos
estados chamados
métodos
callback
21
23. Estados de uma Activity
activity iniciada
métodos
estados chamados
activity está
ativa
métodos
callback
21
24. Estados de uma Activity
activity iniciada
métodos
estados chamados
activity está
ativa
activity
métodos está visível mas
callback sem foco
21
25. Estados de uma Activity
activity iniciada
métodos
estados chamados
activity está
ativa
activity está na
memória mas não activity
métodos está visível mas
está visível
callback sem foco
21
26. Estados de uma Activity
activity iniciada
métodos
estados chamados
activity está
ativa
activity está na
memória mas não activity
métodos está visível mas
está visível
callback sem foco
activity está
encerrada
21
27. Pilhas de Activities
Para garantir que as activities vão reagir às mudanças
de estado, o Android provê uma série de métodos de
callback manipuladores de eventos
Visão geral de todos os métodos do ciclo de vida
activity activity
onRestoreIntanceState onSaveIntanceState
activity activity
onResume onPause
activity activity
onStart onStop
activity activity
oncreate onDestroy
activity
onRestart
Activity está ativa
Activity está visível
Tempo de vida completo da Activity
22
28. Métodos de ciclo de vida
onCreate(Bundle)
Método principal onde
normalmente as coisas
são inicializadas
Recebe um objeto
bundle que empacota
dados do estado anterior
da Activity
onStart()
Método chamado
imediatamente antes da
Activity ficar visível
Seguido de onResume()
ou onStop()
23
29. Métodos de ciclo de vida
onResume()
Chamado imediatamente
antes da activity estar
pronta para interagir com
o usuário
É sempre seguido de
onPause()
onPause()
Método chamado no
momento em que o
sistema vai resumir outra
Activity
24
30. Métodos de ciclo de vida
onStop()
Chamado quando a
activity não vai ficar mais
visível
Seguido de onRestart()
se outra activity entrou
na frente e saiu
Seguido de onDestroy()
se a activity for ser
encerrada
onRestart()
Chamado quando a
activity for voltar a ficar
visível, antes do onStart()
25
31. Métodos de ciclo de vida
onDestroy()
Chamado quando a
activity for encerrar
O método isFinishing()
retorna true aqui se a
activity vai encerrar por
que o usuário fechou ou
a aplicação chamou o
método finish()
Retorna false se o
sistema resolveu encerrar
a activity para liberar
espaço em memória
26
32. Logs do aplicativo
A classe android.util.Log é útil para enviar informações
no desenrolar do aplicativo
Toda saída de sistema no Android (stdout e stderr) são
direcionados para /dev/null, que significa que o bom e velho
System.out.println() não vai funcionar
O LogCat captura essas saídas de log e exibe as mensagens
em cores diferentes:
Código Nível Cor
Log.v("categoria", "texto"); Verbose preto
Log.d("categoria", "texto"); Debug azul
Log.i("categoria", "texto"); Info verde
Log.w("categoria", "texto"); Warning laranja
Log.e("categoria", "texto"); Error vermelho
27
33. package br.com.especializa.minhaprimeiraapp;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
public class EstadosActivity extends Activity {
! @Override
! protected void onCreate(Bundle savedInstanceState) {
! ! super.onCreate(savedInstanceState);
! ! setContentView(R.layout.activity_estados); Exemplo de Logs para
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onCreate");
monitoramento de
! }
estados
! @Override
! protected void onStart() {
! ! super.onStart();
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onStart");
! }
! @Override
! protected void onResume() {
! ! super.onResume();
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onResume");
! }
! @Override
! protected void onRestart() {
! ! super.onRestart();
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onRestart");
! }
! // já deu pra entender né? Crie logs em todos os métodos citados anteriormente
}
28
35. LogCat maximizado - DDMS
Assim que iniciamos o
Encerramos a aplicação
aplicativo
29
36. LogCat maximizado - DDMS
Desligamos o visor
Assim que iniciamos o
(entrou a tela inicial),
Encerramos a aplicação
religamos e só então
aplicativo
encerramos a app
29
37. Salvando estado da Activity
Mesmo quando uma activity é pausada ou parada,
seu estado é mantido em memória
No entanto, o sistema pode destruir a activity para
liberar memória, mesmo sem avisar ao usuário
O sistema deverá ser capaz de recriar a activity de modo que
retome seu estado original se o usuário quiser retornar a ela
Existem métodos callback para lidar com as operação de
registro de estado
onSaveInstanceState - Chamado quando o sistema vai destruir
a activity, mas sabe que precisa restaurar depois
Quando o usuário clica em back, ele não é chamado
onRestoreInstanceState - Logo antes do usuário solicitar
novamente a activity destruída
30
39. Salvando estado da activity
Objeto Bundle
Objeto que empacota um java.util.Map
Delega todos os seus métodos (putInt(), putString(), getInt(),
getString() ...)
Passado aos métodos onSaveInstanceState(),
onRestoreInstanceState() e mesmo o onCreate()
Através dele podemos salvar dados em onSaveInstanceState() e
recuperar esses dados em onRestoreInstanceState()
32
40. !
// Novos métodos em EstadosActivity
/**
! * Método chamado quando o sistema resolver destruir
! * a activity atual em situações onde precisar recuperá-la de volta
! */
! @Override
! protected void onSaveInstanceState(Bundle outState) {
! ! outState.putLong("momento", System.currentTimeMillis());
! ! super.onSaveInstanceState(outState);
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onSaveInstanceState");
! }
!
! /**
! * Método chamado quando o sistema recuperar uma activity
! * destruída após chamada ao onSaveInstanceState
! */
! @Override
! protected void onRestoreInstanceState(Bundle savedInstanceState) {
! ! // TODO Auto-generated method stub
! ! super.onRestoreInstanceState(savedInstanceState);
! ! Log.i("LOG DO APLICATIVO", "Passou pelo onRestoreInstanceState " +
! ! ! ! ! ! ! ! ! (System.currentTimeMillis() -
! ! ! ! ! ! ! ! ! savedInstanceState.getLong("momento")) +
! ! ! ! ! ! ! ! ! "ms depois do onSaveInstanceState");
! }
33
41. Salvando estado da activity
Iniciamos a
aplicação, o
onRestoreInstanceState
não foi chamado
34
42. Salvando estado da activity
O visor
Iniciamos a
esmaeceu após um
aplicação, o
tempo.
onRestoreInstanceState
onSaveInstanceState
não foi chamado
foi chamado
34
43. Salvando estado da activity
Desbloqueamos a tela
O visor
Iniciamos a
reexibindo aapós um
esmaeceu aplicação.
aplicação, o
onRestoreInstanceState ainda
tempo.
onRestoreInstanceState a
não foi chamado porque
onSaveInstanceState
activityfoi chamado
não não havia sido
foi chamado
destruída
34
44. Salvando estado da activity
Clicamos no back
Desbloqueamos a tela
O visor
Iniciamosaaaplicacão.
para encerra
reexibindo aapós um
esmaeceu aplicação.
Como a activityo
aplicação, não ainda
onRestoreInstanceState é mais
tempo.
onRestoreInstanceState a
necessária,
não foi chamado porque
onSaveInstanceState
activityfoi chamado
não não havia sido
onSaveInstanceInstate
foi chamado
não foi chamado
destruída
34
45. Salvando estado da activity
Iniciamos a
aplicação e rotacionamos visor noa tela
Desbloqueamos back
Clicamos
O
Iniciamosaaaplicacão.
para encerra
o aparelho. Esse éreexibindo aapós um
um caso aplicação.
esmaeceu
em que o sistema Como a activityo
aplicação, não ainda
onRestoreInstanceState é mais
sempre tempo.
onRestoreInstanceState a
não foi necessária,
destrói e reconstrói achamado porque
onSaveInstanceState
activity activityfoi chamado
não não havia sido
onSaveInstanceInstate
foi chamado
não foi chamado
destruída
34
46. Salvando estado da activity
Curiosidade. Iniciamos a
Quando a tela esmaece, e rotacionamos visor noa tela
aplicação Desbloqueamos back
Clicamos
O
Iniciamosaaaplicacão.
para encerra
a orientação da tela volta ao Esse éreexibindo aapós um
o aparelho. um caso aplicação.
esmaeceu
estado inicial. Confiraque o sistema Como a activityo
em os aplicação, não ainda
onRestoreInstanceState é mais
sempre tempo.
onRestoreInstanceState a
não foi necessária,
métodos sendodestrói e reconstrói achamado porque
onSaveInstanceState
chamados activity activityfoi chamado
não não havia sido
onSaveInstanceInstate
foi chamado
não foi chamado
destruída
Voltaremos a este
assunto na aula 6 ao
falarmos mais sobre
interfaces
34
47. Activity subclasses
Exemplos de subclasses:
ListActivity - Activity que carrega
ListViews para exibir coleções
ExpandableListActivity - Activity
que carrega ExpandableListViews
TabActivity - Apresenta views em
abas. Foi depreciada na API 13, em
função dos fragments
Aplicativos compilados com o extra Android Support Library
poderão contar com FragmentActivity
Trataremos deste assunto em Fragments
Aplicativos compilados com a GoogleAPIs terão MapActivity
Esse assunto será visto só em outro curso
35
48. ListActivity
Classe que empacota um ListView sem precisarmos
escrever nenhum arquivo de layout
Exibe uma lista vertical de itens, quando não couberem
na tela, carrega uma barra de rolagem
É uma Activity com alguns métodos para gerenciamento de
listas
Para preencher a lista, passamos um objeto de uma
classe que herde de ListAdapter
Vamos trabalhar agora com ArrayAdapter e em assuntos
posteriores com SimpleCursorAdapter
36
49. Activity subclasses
Como não vamos criar uma
view, vamos criar a Activity
de outro jeito
No Manifest, vá na aba
Application e clique em add
Mande criar uma Activity
Em Attributes for Activity, clique
em name para mandar gerar
uma nova classe e preencha os
demais atributos. É bem
intuitivo.
37
50. package br.com.especializa.minhaprimeiraapp;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
/**
* Devemos herdar de ListActivity
*/
public class MinhaListaActivity extends ListActivity {
! @Override
! public void onCreate(Bundle savedInstanceState) {
! super.onCreate(savedInstanceState);
! // Valores que vão preencher a lista
! String[] instrumentos = {"violão", "guitarra", "bateria", "gaita",
! ! ! ! ! ! ! "microfone", "cajon", "bandolim", "congas",
! ! ! ! ! ! ! "violino", "piano", "contra-baixo", "panela"};
! // ArrayAdapter que vai saber fornecer cada item ao ListView
! // context - this (toda Activity é um contexto)
! // android.R.layout.simple_item_1 - componente visual de cada linha
! // objetos - instrumentos - coleção de dados
! ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
! ! ! ! ! ! ! ! android.R.layout.simple_list_item_1,
! ! ! ! ! ! ! ! instrumentos);
! // Método que define o adapter do ListView
! setListAdapter(adapter);
! }
}
38
51. package br.com.especializa.minhaprimeiraapp;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
/**
* Devemos herdar de ListActivity
*/
public class MinhaListaActivity extends ListActivity {
! @Override
! public void onCreate(Bundle savedInstanceState) {
! super.onCreate(savedInstanceState);
! // Valores que vão preencher a lista
! String[] instrumentos = {"violão", "guitarra", "bateria", "gaita",
! ! ! ! Existe uma classe R padrão
! ! ! "microfone", "cajon", "bandolim", "congas",
! ! ! !do Android.! referencia todos
! ! Ela "violino", "piano", "contra-baixo", "panela"};
os componentes que podemos
! // ArrayAdapter que vai saber fornecer cada item ao ListView
trabalhar
! // context - this (toda Activity é um contexto)
! // android.R.layout.simple_item_1 - componente visual de cada linha
! // objetos - instrumentos - coleção de dados
! ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
! ! ! ! ! ! ! ! android.R.layout.simple_list_item_1,
! ! ! ! ! ! ! ! instrumentos);
! // Método que define o adapter do ListView
! setListAdapter(adapter);
! }
}
38
52. package br.com.especializa.minhaprimeiraapp;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
/**
* Devemos herdar de ListActivity
*/
public class MinhaListaActivity extends ListActivity {
! @Override
! public void onCreate(Bundle savedInstanceState) {
! super.onCreate(savedInstanceState);
! // Valores que vão preencher a lista
! String[] instrumentos = {"violão", "guitarra", "bateria", "gaita",
! ! ! ! Existe uma classe R padrão
! ! ! "microfone", "cajon", "bandolim", "congas",
! ! ! !do Android.! referencia todos
! ! Ela "violino", "piano", "contra-baixo", "panela"};
os componentes que podemos
! // ArrayAdapter que vai saber fornecer cada item ao ListView
trabalhar
! // context - this (toda Activity é um contexto)
! // android.R.layout.simple_item_1 - componente visual de cada linha
! // objetos - instrumentos - coleção de dados
! ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
! ! ! ! ! ! ! ! android.R.layout.simple_list_item_1,
! ! ! ! ! ! ! ! instrumentos);
! // Método que define o adapter do ListView
! setListAdapter(adapter);
! }
}
38
53. ListAdapters
Como vimos, servem para realizar o bind entre uma
view e uma coleção de valores
ArrayAdapter
Carrega coleções arrays ou Lists em telas ListView
CursorAdapter
Listas maiores ou mais complexas precisam de um adapter
mais inteligente do que o ArrayAdapter
CursorAdapters unem cursores (objetos que gerenciam
eficientemente coleções em buffers) e listviews
Voltaremos a esse assunto quando fizermos consultas a bancos
de dados
39
54. Fragments
São porções de tela por baixo de uma Activity
É possível combinar mais de um Fragment numa tela
Disponível desde a API 11 (Honeycomb)
É possível decidir mostrar várias fragments em telas maiores
(tablets) e continar usando navegação por activities (handsets)
Disponível para APIs mais antigas (a partir da 4) através
da Android Support Library
No SDK Manager, vá aos extras e baixe esse pacote
Seus próximos projetos já vão ser gerados com a inserção da
dependência android-support-v4.jar
40
58. Exemplo de Fragments
1.Crie uma nova Activity
1.1.Defina o nome FragmentsActivity.java. Por enquanto não
vamos mudar o conteúdo padrão dela
1.2.Renomeie a view gerada para fragment_dois_lados.xml
2.Crie mais duas views
2.1.File > New > Other, depois Android > Android XML Layout File
2.2.Nomes: fragment_esquerda.xml e fragment_direita.xml
3.Crie duas classes Java no pacote src
3.1.FragmentEsquerda.java e FragmentDireita.java
3.2.Elas devem herdar de android.support.v4.app.Fragment
44
59. View principal do
fragment_dois_lados.xml exemplo. Carrega um
layout em linha com
dois elementos
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
! <fragment
! ! android:name="br.com.especializa.minhaprimeiraapp.FragmentEsquerda"
! ! android:id="@+id/fragmentEsquerda"
! ! android:layout_weight="1"
! ! android:layout_width="0px"
! ! android:layout_height="match_parent" />
! <fragment
! ! android:name="br.com.especializa.minhaprimeiraapp.FragmentDireita"
! ! android:id="@+id/fragmentDireita"
! ! android:layout_weight="1"
! ! android:layout_width="0px" Tags
! ! android:layout_height="match_parent" /> fragment indicam
</LinearLayout>
no atributo name o
nome da classe
controladora
45
60. fragment_esquerda.xml
Seu conteúdo é irrelevante
no momento
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
! xmlns:android="http://schemas.android.com/apk/res/android"
! android:orientation="vertical"
! android:layout_width="fill_parent"
! android:layout_height="fill_parent" Mudamos a cor
! android:background="#00FF00"> do background só pra
! <TextView ficar destacar mais o
! ! android:layout_width="fill_parent" exemplo
! ! android:layout_height="wrap_content"
! ! android:text="Fragmento da esquerda"
! ! android:textColor="#000000"
! ! android:textSize="25sp" />
</LinearLayout>
fragment_direita.xml
Pode ter um conteúdo
semelhante. Só altere a
cor do fundo
46
61. package br.com.especializa.minhaprimeiraapp;
import android.os.Bundle; Vamos herdar de
import android.support.v4.app.Fragment; Fragment da support
import android.view.LayoutInflater;
API
import android.view.View;
import android.view.ViewGroup;
public class FragmentEsquerda extends Fragment {
! @Override
! public View onCreateView(LayoutInflater inflater, ViewGroup container,
! ! ! Bundle savedInstanceState) {
! ! // LayoutInflater é um objeto com a capacidade de ler um arquivo XML,
! ! // criar os objetos Java referentes a cada componente escrito lá
! ! // e exibi-lo em algum lugar.
! ! // Até então nós só havíamos conseguido isso com o setContentView()
! ! // do onCreate da Activity.
! ! // Que por sua vez carrega sempre na tela como um todo
! ! return inflater.inflate(R.layout.fragment_esquerda, container, false);
! }
}
No exemplo,
Método callback do Chamada que FragmentDireita.java
momento em que a view carrega a “sub” view deve ter a mesma
principal for criada referenciada em R composição
47
62. Como tudo aconteceu?
FragmentsActivity.java
setContentView(R.layout.fragment_dois_lados)
Carregou a view principal
fragment_dois_lados.xml
Tags <fragment>
Cada tag carregou uma classe Fragment
FragmentEsquerda.java FragmentDireita.java
inflater.inflate() inflater.inflate()
Carregou fragment_esquerda.xml Carregou fragment_direita.xml
fragment_esquerda.xml fragment_direita.xml
Simples arquivo de layout Simples arquivo de layout
48
63. O que ganhamos com isso?
Telas maiores exibem
mais informações
Imagine
A navegação não uma única Activity
descarrega a tela para controlar todos os
anterior seus eventos
Cada Fragment controla a sua porção de tela
49
66. Fragments
Podemos detectar de alguma forma que a tela é
grande e só então exibir dois fragments
Se a tela for menor, exibiremos apenas um fragment
Este fragment irá abrir outra Activity com o segundo fragment
dentro
1. Crie um novo arquivo XML de layout
1.1.Chame-o de fragment_um_lado.xml
2. Altere a Activity principal para escolher entre ele ou o de dois
lados
52
67. Um ou dois fragments
FragmentsActivity.java
OU
setContentView
fragment_um_lado.xml fragment_dois_lados.xml
FragmentEsquerda.java FragmentDireita.java
fragment_esquerda.xml fragment_direita.xml
53
68. fragment_um_lado.xml
Apenas um fragment
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
! android:name="br.com.especializa.minhaprimeiraapp.FragmentEsquerda"
! android:id="@+id/fragmentEsquerda"
! android:layout_width="match_parent"
! android:layout_height="match_parent" />
54
69. 1. @Override
2. protected void onCreate(Bundle savedInstanceState) {
3. super.onCreate(savedInstanceState);
4. boolean doisPaineis = false;
5.
6. // Verificamos se a tela é padrão XLARGE
7. if ((getResources().getConfiguration().screenLayout
8. & Configuration.SCREENLAYOUT_SIZE_XLARGE)
9. ! == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
10.
11. ! Toast.makeText(this, "XLARGE", Toast.LENGTH_LONG).show();
12. ! doisPaineis = true;
13.
14. // Verificamos se a tela é padrão LARGE
15. } else if ((getResources().getConfiguration().screenLayout &
16. Configuration.SCREENLAYOUT_SIZE_LARGE)
17. ! == Configuration.SCREENLAYOUT_SIZE_LARGE) {
18.
19. ! Toast.makeText(this, "LARGE", Toast.LENGTH_LONG).show();
20. ! doisPaineis = true;
21.
22. }
23.
24. if (doisPaineis) {
25. setContentView(R.layout.fragment_dois_lados);
26. } else {
27. setContentView(R.layout.fragment_um_lado);
28. }
29. }
55
70. Fragments
Nas linhas 7 e 15
getResources() retornou o objeto Resources a partir do qual
se obtém dados dos recursos do dispositivo
getConfiguration() retornou dados da configuração corrente
screenLayout trouxe uma máscara de bits sobre o tamanho
da tela
Perguntamos se a máscara de bits casa com o padrão
XLARGE ou LARGE
Nesses casos, e só nesses casos, doisPaineis virou true
E chamamos fragments_dois_lados
56
71. Fragments
Como vamos exibir o layout fragment_direita.xml?
Ele deverá vir a partir de uma nova Activity
Esta nova Activity não vai controlar a view do fragment
Essa tarefa já ficou a cargo da classe DireitaFragment.java
1. Vamos criar um botão em fragment_esquerda.xml
2. Implementar o evento de clique no EsquerdaFragment.java
2.1. Será feito no novo método callback onActivityCreate()
3. Criar a nova Activity chamada DireitaFragmentActivity.java
57
74. Novo método em getActivity() recupera a
FragmentEsquerda.java Activity associada ao
Fragment
@Override
public void onActivityCreated(Bundle savedInstanceState) {
! super.onActivityCreated(savedInstanceState);
!
! Button b = (Button) getActivity().findViewById(R.id.btnChamarDireita);
! b.setOnClickListener(new View.OnClickListener() {
! !
! ! @Override
! ! public void onClick(View v) {
! ! ! if (getActivity().findViewById(R.id.fragmentDireita) == null) {
! ! ! ! Intent i = new Intent(getActivity(), FragmentDireitaActivity.class);
! ! ! ! getActivity().startActivity(i);
! ! ! }
! ! }
! });
!
} A chamada à próxima
Activity precisa da criação de
Intents. Assunto da próxima
aula
60
75. package br.com.especializa.minhaprimeiraapp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
public class FragmentDireitaActivity extends FragmentActivity {
! @Override
! protected void onCreate(Bundle savedInstanceState) {
! ! super.onCreate(savedInstanceState); Novo objeto em
! ! Fragment fragment = new FragmentDireita(); FragmentDireita que vai
controlar a view. O papel desta
! ! getSupportFragmentManager().beginTransaction() Activity é apenas criá-lo
! ! .add(android.R.id.content, fragment).commit();
! !
! }
}
- getSupportFragmentManager() retorna o objeto FragmentManager.
- Seu método beginTransation() é quem adiciona, remove ou substitui
fragments ao layout da Activity atual
- android.R.id.content referencia a raiz de elementos da View
- commit() realiza a operação
61
76. O ciclo de vida do Fragment
é parecido e vinculado ao de
uma Activity
Se a Activity parar, seus
fragments também param. Se for
destruída, eles também serão
onAttach(Activity)
Chamado assim que o fragment
for anexado a uma Activity.
Ela é passada como parâmetro
onCreate(Bundle)
Semelhante ao da Activity. É
chamado para fazer suas
inicializações
62
77. onCreateView(LayoutInflater,
ViewGroup, Bundle)
O método mais usado em um
Fragment representa o momento
exato em que ele cria sua
interface com o usuário
Recebe o inflater que vai
carregar a view, o ViewGroup
que servirá de container e o
mesmo bundle do onCreate
onActivityCreated(Bundle)
Chamado logo depois da Activity
ser criada
Como Fragments não herdam
de Context, precisam sempre
chamar a Activity para conseguir
realizar algumas operações de
conteúdo
Lembra do Toast, por exemplo?
A partir daqui getActivity() não
retorna nulo
63
78. onStart()
Chamado logo quando o
Fragment estiver visível
onResume()
Chamado logo quando ele
assumir interatividade com o
usuário
onPause()
Chamado assim que ele perder
a interatividade
onStop()
Chamado assim que a Activity
não estiver mais visível
64
79. onDestroyView(LayoutInflater,
ViewGroup, Bundle)
Método parecido com o
onCreateView()
Roda no momento em que a
view carregada nele for destruída
onDestroy()
Roda assim que o fragment não
tiver mais serventia
onDetach()
Roda quando não estiver mais
anexado a uma Activity
65
80. Fragment subclasses
Há duas versões de Fragments
Elas possuem estruturas hierárquicas diferentes
android.app.Fragment
Classe adicionada na API 11 (honeycomb) justamente no
momento da ascensão dos tablets
Herdada por DialogFragment, ListFragment, PreferenceFragment
e WebViewFragment
android.support.v4.app.Fragment
Classe da API Android Suppor V4, que é o extra que porta os
fragments lá pra trás na API 4 (Donut - 1.6)
Herdada apenas por DialogFragment e ListFragment
sendo as respectivas classes criadas neste mesmo pacote
Não são as mesmas do pacote principal
66
81. Suporte a APIs antigas
Apps com Fragments compatíveis com APIs antigas:
Os fragments devem ser android.support.v4.app.Fragment
As activities que anexam fragments devem herdar de
android.support.v4.app.FragmentActivity
Nelas, o objeto FragmentManager deve ser o do pacote
android.support.v4.app, chamado exclusivamente por
getSupportFragmentManager(), um método com função
semelhante ao getFragmentManager()
ListFragments e DialogFragments só podem ser do mesmo
pacote da API de suporte
APIs v7 e v13
São supersets da v4 e trazem mais coisas, mas requerem
minSdkVersion 7 e 13 respectivamente
67
83. Esta foi a Activity
principal
<!-- Trecho no AndroidManifest.xml referente
ao MasterDetailFlow que criamos -->
<activity
android:name="br.com.especializa.minhaprimeiraapp.ProdutoListActivity"
android:label="@string/title_produto_list" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> A segunda Activity
<category android:name="android.intent.category.LAUNCHER" /> para quando a tela for
</intent-filter> menor
</activity>
<activity
android:name="br.com.especializa.minhaprimeiraapp.ProdutoDetailActivity"
android:label="@string/title_produto_detail"
android:parentActivityName=".ProdutoListActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ProdutoListActivity" /> Atributo criado no Jelly
</activity> Bean 4.1 (API 16)
Versão anterior à da
API 16 para a mesma
idéia
69
84. Navegação Up
A API 11 trouxe também as ActionBars e com elas, o
chamado Up button
As declarações do AndroidManifest definiram a Activity
a ser chamada quando este botão for acionado
<meta name="android.support.PARENT_ACTIVITY">
Para APIs anteriores à 16
android:parentActivityName=""
Para versões posteriores
70
85. Navegação Up e Back
Up button disponível
em telas internas
Back button de
sempre
71
86. package br.com.especializa.minhaprimeiraapp;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class ProdutoListActivity extends FragmentActivity implements
Interface
! ! ProdutoListFragment.Callbacks { criada pelo próprio
exemplo
! private boolean mTwoPane;
! @Override
! protected void onCreate(Bundle savedInstanceState) {
! ! super.onCreate(savedInstanceState);
! ! // Layout que só carrega um único Fragment
! ! // Vai ser trocado por outro usando um técnica bem legal
! ! setContentView(R.layout.activity_produto_list);
! ! // De cara, parace que nunca vai entrar nesse if
! ! if (findViewById(R.id.produto_detail_container) != null) {
! ! ! // Se entrar, essa variável vira true
! ! ! mTwoPane = true;
O estilo
do ícone vai
! ! ! ((ProdutoListFragment) getSupportFragmentManager() permanecer
! ! ! ! ! .findFragmentById(R.id.produto_list)) clicado
! ! ! ! ! .setActivateOnItemClick(true);
! ! }
! }
! // continua ...
72
87. !
// Método que a interface criada exigiu que fosse implementado
// O fragment controlador da lista vai invocá-lo de volta (Callback)
! @Override
! public void onItemSelected(String id) {
! ! if (mTwoPane) {
! ! ! // Em tela dupla (tablets), vamos criar um Bundle
// para armazenar o id do produto clicado
! ! ! Bundle arguments = new Bundle();
! ! ! arguments.putString(ProdutoDetailFragment.ARG_ITEM_ID, id);
! ! ! ProdutoDetailFragment fragment = new ProdutoDetailFragment();
! ! ! fragment.setArguments(arguments); // Método que registra o bundle no Fragment
// Substituindo o fragment de detalhes que estiver carregado por outro atualizado
! ! ! getSupportFragmentManager().beginTransaction()
! ! ! ! ! .replace(R.id.produto_detail_container, fragment).commit();
! ! } else {
! ! ! // Em telas simples, haverá uma chamada à nova Activity que vai
! ! ! // receber no pacote o id do produto clicado
// ... calma que ainda veremos Intents
! ! ! Intent detailIntent = new Intent(this, ProdutoDetailActivity.class);
! ! ! detailIntent.putExtra(ProdutoDetailFragment.ARG_ITEM_ID, id);
! ! ! startActivity(detailIntent);
! ! }
! }
}
73
88. activity_produto_list.xml
Apenas um fragment
gerado
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/produto_list"
android:name="br.com.especializa.minhaprimeiraapp.ProdutoListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:context=".ProdutoListActivity"
tools:layout="@android:layout/list_content" />
Tablets, que possuem
telas grandes e APIs >= 11,
vão sobrescrever a referência de
<resources> R ao layout de um fragment
<item name="activity_produto_list" type="layout"> pelo de dois
@layout/activity_produto_twopane
</item>
</resources>
refs.xml em values-large Fez sentido o if
(nomenclatura antiga) e (mTwoPane) da
values-sw600dp (nova) Activity?
74
89. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
android:showDividers="middle"
tools:context=".ProdutoListActivity" >
<fragment
android:id="@+id/produto_list"
android:name="br.com.especializa.minhaprimeiraapp.ProdutoListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@android:layout/list_content" />
activity_produto_twopane.xml
<FrameLayout
android:id="@+id/produto_detail_container" carregando um fragment
android:layout_width="0dp" produto_list e um outro componente
android:layout_height="match_parent" produto_detail_container
android:layout_weight="3" />
</LinearLayout>
75
90. ListFragment da
API de suporte
public class ProdutoListFragment extends ListFragment {
! private static final String STATE_ACTIVATED_POSITION = "activated_position";
! private Callbacks mCallbacks = sDummyCallbacks;
! private int mActivatedPosition = ListView.INVALID_POSITION;
Representa
! /** posição inválida do
! * Interface interna que a Activity principal implementou toque no item
! */
! public interface Callbacks {
! ! /**
! ! * Método implementado pela Activity principal
! ! */
! ! public void onItemSelected(String id);
! }
! @Override
Lembra do
! public void onCreate(Bundle savedInstanceState) {
! ! super.onCreate(savedInstanceState); ArrayAdapter?
! ! // TODO: replace with a real list adapter.
! ! setListAdapter(new ArrayAdapter<DummyContent.DummyItem>(getActivity(),
! ! ! ! android.R.layout.simple_list_item_activated_1,
! ! ! ! android.R.id.text1, DummyContent.ITEMS));
! }
// continua ...
76
91. Assim que a
view for criada. Ela é
recriada em
rotações
! @Override
! public void onViewCreated(View view, Bundle savedInstanceState) {
! ! super.onViewCreated(view, savedInstanceState);
! ! // Recuperando um possível valor de item selecionado
! ! if (savedInstanceState != null
! ! ! ! && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
! ! ! setActivatedPosition(savedInstanceState
! ! ! ! ! .getInt(STATE_ACTIVATED_POSITION));
! ! }
! }
Frescurinha
! @Override particular do código
! public void onAttach(Activity activity) { gerado
! ! super.onAttach(activity);
! ! // Activities containing this fragment must implement its callbacks.
! ! if (!(activity instanceof Callbacks)) {
! ! ! throw new IllegalStateException(
! ! ! ! ! "Activity must implement fragment's callbacks.");
! ! }
! ! mCallbacks = (Callbacks) activity;
! }
// Continua ...
77
92. ! @Override Quando o item for
! public void onDetach() {
! ! super.onDetach(); selecionado, chama o
! ! mCallbacks = sDummyCallbacks; callback da activity
! }
! @Override
! public void onListItemClick(ListView listView, View view, int position,
! ! ! long id) {
! ! super.onListItemClick(listView, view, position, id);
! ! mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
! }
! @Override
! public void onSaveInstanceState(Bundle outState) {
! ! super.onSaveInstanceState(outState);
! ! if (mActivatedPosition != ListView.INVALID_POSITION) {
! ! ! // Serialize and persist the activated item position.
! ! ! outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
! ! }
! }
// Continua ...
Salvando o item
selecionado
78
93. ! public void setActivateOnItemClick(boolean activateOnItemClick) {
! ! // When setting CHOICE_MODE_SINGLE, ListView will automatically
! ! // give items the 'activated' state when touched.
! ! getListView().setChoiceMode( Redefinição
! ! ! ! activateOnItemClick ? ListView.CHOICE_MODE_SINGLE
! ! ! ! ! ! : ListView.CHOICE_MODE_NONE); inusitada de métodos
! } sobre a marcação de
itens selecionados
! private void setActivatedPosition(int position) {
! ! if (position == ListView.INVALID_POSITION) {
! ! ! getListView().setItemChecked(mActivatedPosition, false);
! ! } else {
! ! ! getListView().setItemChecked(position, true);
! ! }
! ! mActivatedPosition = position;
! }
}
79
94. /**
* Activity de detalhes para hansets
*/
public class ProdutoDetailActivity extends FragmentActivity {
! @Override Carregou a view
! protected void onCreate(Bundle savedInstanceState) {
! ! super.onCreate(savedInstanceState); porque não houve um
! ! setContentView(R.layout.activity_produto_detail); fragment da direita no
exemplo
! ! // Exibindo o Up button
! ! getActionBar().setDisplayHomeAsUpEnabled(true);
! ! if (savedInstanceState == null) {
! ! ! // Criando o fragment da direita
! ! ! Bundle arguments = new Bundle();
! ! ! arguments.putString(ProdutoDetailFragment.ARG_ITEM_ID, getIntent()
! ! ! ! ! .getStringExtra(ProdutoDetailFragment.ARG_ITEM_ID));
! ! ! ProdutoDetailFragment fragment = new ProdutoDetailFragment();
! ! ! fragment.setArguments(arguments);
! ! ! getSupportFragmentManager().beginTransaction()
! ! ! ! ! .add(R.id.produto_detail_container, fragment).commit();
! ! }
! }
! @Override
! public boolean onOptionsItemSelected(MenuItem item) { Callback do evento
! ! switch (item.getItemId()) { de clique no botão
! ! case android.R.id.home: options do Android
! ! ! NavUtils.navigateUpTo(this, new Intent(this,
! ! ! ! ! ProdutoListActivity.class));
! ! ! return true;
! ! }
! ! return super.onOptionsItemSelected(item);
! }
}
80