sexta-feira, 12 de outubro de 2012

Como navegar entre telas


Olá a todos.

Até este tópico, aprendemos como instalar o Android, criar um projeto e até mesmo criar layouts bem completos. Qual o próximo passo? Até agora, em todos os exemplos vistos, foi utilizada no máximo uma tela por exemplo. O próximo passo a ser dado é aprender a navegar entre mais de uma tela em uma mesma aplicação.

Em Android, cada tela é uma classe Java que estende da classe Activity. Para navegarmos entre mais de uma Activity, devemos utilizar a classe Intent. A melhor tradução para esta palavra é "intenção". Se estamos em uma Activity e queremos ir para outra, temos que chamar uma Intent (ou seja, temos a "intenção" de mudar de uma tela para outra).

Para entender o conceito de Intent, criaremos 3 exemplos bem simples em que basicamente trafegaremos entre várias telas de uma mesma aplicação. No 1º exemplo, criaremos uma transição simples de uma tela para outra. No 2º exemplo, criaremos uma transição de uma tela para outra em que a tela de origem enviará uma variável para a tela de destino. No 3º exemplo, criaremos uma transição de uma tela para outra em que a tela de destino retornará um valor para a tela de origem. Este tópico do blog se dividirá entre esses 3 exemplos.

Transição simples de uma tela de origem para uma tela de destino
Em Android, quando criamos um projeto no Eclipse, é gerado o arquivo AndroidManifest.xml, que é um arquivo de configuração onde relatamos várias informações, como quais telas existem na aplicação, qual é a API mínima e a API máxima do Android que a aplicação poderá rodar, dentre outras. Logo, o primeiro passo a ser dado na criação do nosso exemplo é informar ao AndroidManifest.xml as telas que usaremos em nossa aplicação. Vamos supor que teremos 3 telas em nossa aplicação: Tela1Activity.java, Tela2Activity.java e Tela3Activity.java (é aconselhável, em Android, o nome de uma Activity possuir o posfixo "Activity"). Então, precisamos declará-las no arquivo AndroidManifest.xml, tal qual mostrado abaixo:



Agora que já temos as Activity's declaradas no AndroidManifest.xml, o próximo passo será criar o layout de cada uma dessas telas. De início, criaremos algo bem simples, como uma TextView sendo mostrada no centro da tela. Algo como isso:



Para cada tela, o parâmetro android:text="@string/tela1_title" será adaptado para os devidos nomes (para a Tela2 por exemplo, o parâmetro será android:text="@string/tela2_title", e com a Tela3, analogamente).

Em seguida, precisamos criar as Activity's dessas telas (as classes Java estendendo da classe Activity). Elas basicamente seguirão o modelo mostrado abaixo:



Agora que criamos todas as nossas telas, seus layout's, e as declaramos no AndroidManifest.xml, poderemos enfim lidar com as Intent's, que nos permitirão navegar dentre essas 3 telas.

Para navegarmos entre as telas, precisamos criar um dispositivo de entrada ao usuário, para que ele escolha quando essa transição entre as telas ocorrerá. O melhor dispositivo para esse caso é criar um Button que, quando clicado, mude a tela corrente da aplicação. Para isso, criaremos um Button na Tela1Activity.java e outro Button na tela Tela2Activity.java. A idéia é que a transição ocorra da Tela1 para a Tela2, e da Tela2 para a Tela3.

Modificando o layout da Tela1, teremos o seguinte conteúdo em seu arquivo XML:



E seu layout ficará como mostrado abaixo:



Agora, precisamos mapear o id do Button na nossa Activity (tal qual ensinado no tópico anterior). Modificando a estrutura do código-fonte da tela Tela1Activity.java, teremos o seguinte conteúdo:


Agora, nos restar implementar a chamada à Tela2. Para chamarmos outra tela em Android, precisamos usar uma Intent. A sintaxe de uma chamada à uma classe Intent é mostrada abaixo:


O construtor da classe Intent é: Intent intent = new Intent(packageContext, cls). O argumento "packageContext" é o contexto da Activity atual. O argumento "cls" é a classe da Activity para a qual a Activity atual deseja ir. Na imagem acima, os valores desses argumentos são, respectivamente, "Tela1Activity.this" e "Tela2Activity.class". Ou seja, temos a "intenção" de migrar da Tela1 para a Tela2.

O código-fonte completo da Tela1Activity.java é mostrado abaixo:


Agora, basta adaptarmos esse código-fonte para a Tela2Activity.java, para termos implementada a transição entre a Tela2 e a Tela3. E pronto! Teremos implementada uma aplicação com diversas telas, e com a transição entre elas.

Seguindo este tópico, iremos ver como implementar uma transição entre duas telas em que a tela de origem enviará dados para a tela de destino.

Transição de uma tela de origem para uma tela de destino com envio de informações entre elas
Na transição acima, utilizamos o método startActivity() para realizar a transição entre telas. Porém, foi uma transição simples, sem envio de informações. Desta vez, enviaremos informações de uma tela para a outra. Para realizar este tipo de tarefa, ainda utilizaremos o método startActivity(). Porém, modificaremos um pouco sua estrutura, tal qual mostrado abaixo:


A imagem acima é o conteúdo atualizado do evento onClick(). O que mudou aqui é que incluímos um Bundle na Intent que chama a próxima tela. Em Android, Bundle é a classe que armazena o que será enviado de uma tela para outra. Esta classe possui vários métodos. Na imagem acima, podemos notar o método putString(). Este método recebe a "chave" da string que passaremos para a próxima tela e o seu valor. A chave é um valor (sempre em String) que utilizaremos para obter, na tela de destino, os dados passados de uma tela para a outra.

Agora que transmitimos dados da tela de origem para a tela de destino, precisamos captar esses dados na tela de destino (neste caso, na Tela2Activity.java). Isto é feito através do método getIntent().getExtras(), que retorna um Bundle. Abaixo é mostrado o código-fonte de um método customizado que realiza essa leitura.


Algumas observações precisam ser feitas a respeito da imagem acima:

  • Temos que verificar se os extras são nulos ou não. Isso é vital, pois podemos chamar uma tela a partir de diferentes telas. Por exemplo, tanto a Tela1 como a Tela2 podem chamar a Tela3. E se a Tela1 enviar dados para a Tela3, os extras da imagem acima não serão nulos. Mas se a Tela2 não enviar dados para a Tela3, os extras da imagem acima serão nulos.
  • mTextView é um atributo da Tela2Activity.java, que nada mais é do que a TextView desta tela. Como estamos recebendo dados da tela anterior, para verificarmos se estes dados chegaram da Tela1 para a Tela2, atualizaremos esta TextView.

O código-fonte completo desta tela é mostrado abaixo:



E pronto! Já sabemos como transmitir dados de uma tela para outra. Seguindo este tópico, iremos ver como implementar uma transição entre duas telas em que a tela de destino retornará um valor para a tela de origem.

Transição de uma tela de origem para uma tela de destino, com envio de uma informações da tela de destino para a tela de origem
Nas transições acima, utilizamos o método startActivity(). Desta vez, utilizaremos outro método: startActivityForResult(). Com este método, a Activity de destino consegue retornar informações à Activity de origem. Isso é bem útil em alguns casos.

Por exemplo, suponha que temos uma Tela1, que pode chamar tanto uma Tela2 como uma Tela3  Neste caso, se a Tela1 for uma tela de cadastro de clientes, a Tela2 for uma tela de deleção de clientes, e a TelaC for uma tela de matrícula de clientes, ao abrirmos as telas 2 ou 3 e realizarmos alguma operação, seremos redirecionados à Tela1. Mas nesta tela, precisamos saber se a deleção/matrícula foi feita com sucesso ou não. Neste caso, precisamos enviar alguma informação de retorno à Tela1, a partir do término de execução das telas 2 e 3. Isso é feito com o método startActivityForResult().

Abaixo será mostrado um exemplo mais básico desta funcionalidade. A idéia básica é que uma Activity seja chamada na Tela1, e que a Activity filha chamada retorne uma informação à Tela1. A Tela1 poderá chamar tanto a Tela2 como a Tela3. Esta informação será captada pela Tela1 através do evento: protected void onActivityResult(int codigo, int resultado, Intent intent). Vamos ao nosso exemplo.

Abaixo é mostrado os layout XML das nossas telas.






E o código-fonte da Tela1Activity.java:


E agora, o código-fonte da Tela2Activity.java. A Tela3 será análoga à esta tela.



Agora, vamos executar este exemplo, para fixar bem tudo que foi falado acima. Clicando no botão "Ir para Tela 2", eis o que acontece:



Agora estamos na Tela2. Clicando no botão "Voltar à tela anterior", eis o que acontece:



Basicamente, o que aconteceu foi que a Tela1 recebeu retornou de sua Activity filha. Desta forma, conseguimos identificar qual das telas foi chamada pela Tela1.

E pronto! Já sabemos como transmitir dados de uma tela para outra de todas as maneiras possíveis. Mas antes de fecharmos este tópico, iremos falar de uma propriedade de layout muito útil em Android.

Quando um botão é clicado, o ideal seria o usuário ter um feedback do que aconteceu. Ou seja, quando o botão for clicado, ficar de uma cor. Quando for solto, ficar de outra. Desta forma, o usuário saberá se o evento de clique do botão foi acionado ou não. Vamos implementar isso em nosso exemplo.

Inicialmente, iremos implementar o arquivo XML que "anima" o botão. Vamos chamá-lo de "comportamento_botao.xml". Seu código-fonte será o seguinte:



Ou seja, quando o botão for clicado, um drawable será chamado. Quando ele não for clicado, outro drawable será chamado. Agora, precisamos entender o conteúdo dos eventos citados no arquivo acima (comportamento_pressionado.xml e comportamento_normal.xml). Abaixo é mostrado o código-fonte dos respectivos arquivos citados ao lado:





Esses arquivos possui alguns parâmetros:

  • gradient  Este parâmetro escolhe a cor que o botão terá. Ele pode ter 3 cores diferentes, germinadas. No caso do nosso exemplo, teremos um gradiente de cores idênticas (as mesmas para cada opção de gradiente) em cada drawable.
  • corners  Este parâmetro cria contornos suaves no botão. No nosso caso, teremos os cantos do botão levemente arredondados em um tamanho de 3dp.
  • stroke  Parâmetro que traça uma borda em nosso botão. Aqui, teremos uma borda preta de 1px de tamanho.

OBS.: Existem outros parâmetros além dos 3 acima. Mas eles não serão vistos neste momento.

Agora, precisamos ativar essa animação nos botões de nosso exemplo. Para fazer isto, basta incluir o parâmetro android:background="@drawable/comportamento_botao" em cada um dos botões de nosso aplicativo. Fazendo isso, teremos botões muito mais elegantes, e bem intuitivos ao usuário.

-----

E aqui terminamos mais um tópico do blog.

Caso alguém tenha uma dúvida, crítica ou sugestão, sinta-se à vontade.

33 comentários:

  1. Amigo, muito boa sua iniciativa de ensinar a galera as transições. Eu gostaria de saber como poderia fazer uma transição dessas, mas com imagens, como se fossem esses jogos para android com as imagens dos criadores do jogo que fazem a transição sem precisar tocar na tela até chegar no menu principal. vlw.

    ResponderExcluir
    Respostas
    1. Olá.
      Desculpe-me pela demora. Acho que o Gmail não me avisou sobre sua postagem. Só hoje a vi.
      Para fazer essa transição, você pode usar o componente ImageButton. Você está familiarizado com este componente?

      Excluir
  2. Olá Rodrigo, vendo seu tópico e foi ótimo agora que estou iniciando na programação de aplicativos para Android. Só gostaria de sanar uma duvida, se ao invés de criar novas paginas eu quise-se apenas a transição delas, no momento em que eu clica-se no botão a pagina antiga se fecha-se. É possível?
    Obrigado e ótimo trabalho!

    ResponderExcluir
  3. Dalvane, obrigado pelos elogios!
    Sobre sua dúvida, se a página (tela) já existir, basta que você utilize o método "startActivity(Intent intent);" dentro do evento "onClick()" do botão, e também que você utilize o método "finish()" dentro do evento "onDestroy()" da Activity que foi fechada.
    Isso garantirá o funcionamento que você deseja.
    Abraço!

    ResponderExcluir
  4. E pra retornar a Activity anterior? Tipo Fui da tela1 pra tela2 e agora quero retornar a tela1? aqui ele pega o evento de onClick e só vai pra próxima, ja experimentei cria um parametro onClick com o nome voltar mas mesmo assim ele pega o onClick que passa pra prómia tela...

    ResponderExcluir
  5. Olá Alexandre.
    Neste caso você precisa criar um evento de clique na Tela2, e dentro dele declarar o método "finish()", tal qual expliquei ao Dalvane. Assim ele "matará" a Activity2 e retornará à última Activity acessada, que neste caso é a Activity1.
    Caso você não consiga, me diga.

    ResponderExcluir
  6. Oi Rodrigo , fiz exatamento como vc indicou mas quando vou passar da primeira para a segunda tela aparece um erro dizendo "Infelzimente o programa parou."
    tem ideia do que pode estar acontecendo?

    ResponderExcluir
  7. Você poderia me mandar o código fonte via e-mail?

    ResponderExcluir
  8. rodrigo,
    estou com o mesmo erro já tentei varias formas de fazer
    tento colocar o map V2 também da erro e diz que o programa parou
    não sei se você ouviu falar diz que nao tem o openGL 2.0
    estou tentando montar um app mais estou apanhando muito
    e dormindo pouco rsrsr te add no facebook
    espero que possa me ajudar
    estou entrando agora nesse mundo e tenho que
    parabenizar pessoas como você que esta disposto a ajudar

    ResponderExcluir
  9. Opa Mariana.
    Me diga, como é esse programa que você está criando? Poderia me dar maiores detalhes para eu ajudá-la?

    ResponderExcluir
  10. Bom dia Rodrigo,
    primeiro seria as telas rsrsrs
    eu nao consigo interligar elas.

    seria assim...

    tela 1 liga com tela 2 e 3
    tela 2 liga com 1 e 3
    tela 3 liga com 4 e 5 e 6 e 7 e 8 e 9
    tela 4 liga com 3
    tela 5 liga com 3
    tela 6 liga com 3
    tela 7 liga com 3
    tela 8 liga com 3
    tela 9 liga com 1

    é um programa bem interessante, e se der certo você poderia trabalhar comigo
    isso seria apenas o começo, mas podemos começar por ai rsrsrs

    Desde já agradeço

    ResponderExcluir
  11. Olá Mariana.
    Vamos por partes.
    Você não consegue criar um botão na Tela1 que conecte-o à Tela2 e à Tela3?
    Mesmo seguindo meu tutorial, na parte referente à imagem abaixo?
    http://3.bp.blogspot.com/-N4Qv0vYpyJI/UHhsEzlQ7gI/AAAAAAAAAfs/l-SyA_zesAQ/s1600/05.png

    ResponderExcluir
  12. Olá Rodrigo,
    então eu consigo conectar a primeira tela com até
    duas telas ou seja tela 1 com tela 2 e 3
    , mas nao consigo fazer voltar diz que o programa parou
    e fecha ai nao consigo mais fazer exemplo da tela 2 para 3 ou simplesmente retornar a tela 1,
    nao sei o que faço errado. estou tentando parar um activity e iniciar outro um por cada tela, mas ainda nao estou conseguindo.
    mas enfim é isso eu nao consigo resumindo tudo rsrs

    se vc tiver um código semelhante com o que eu preciso e puder me mandar rsrsr

    ResponderExcluir
  13. public class MainActivity extends Activity {

    Button btnovatela, btirtela2;

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

    btnovatela = (Button) findViewById(R.id.btnovatela);
    btnovatela.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    chamanovatela();

    }
    });
    btirtela2 = (Button) findViewById(R.id.btirtela2);
    btirtela2.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    chamatela2();

    }
    });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;


    }
    public void chamanovatela(){
    setContentView(R.layout.novatela);

    }
    public void chamatela2(){
    setContentView(R.layout.tela2);
    }
    }

    quando vou fazer a terceira parte igual a essas da erro

    ResponderExcluir
  14. Rodrigo,
    Conseguiii

    Segui seu exemplo e funcionou perfeitamente
    muito obrigado mesmo
    logo segue mais duvidas rsrsrsr

    muito obrigado mesmo

    ResponderExcluir
  15. Que erro que estava acontecendo?
    Precisando é só perguntar!

    ResponderExcluir
  16. Boa noite Rodrigo,
    tudo bem ?

    eu consegui fazer a navegacao entre telas, mais consigo apenas um comando por tela,
    você sabe me dizer qual comando uso para mais de um?

    exemplo eu fiz 3 telas e fiz a 1 ir para 2 e a 2 ir para a 3 e a 3 ir para 1.

    mas preciso fazer a 1 ir para 2 e para 3 e quando faco o mesmo comando na tela ele nao vai,

    public void chamanovatela(){
    setContentView(R.layout.novatela);

    com esse comando vai mais ai ele nao obedece mais o os demais,

    você sabe algum que eu possa por em conjunto?
    para acionar um segundo botao na mesma tela?

    ResponderExcluir
  17. Você quer que o aplicativo vá da Tela1 para a Tela2 e a Tela3, ao mesmo tempo? Ou que da Tela1 tenhamos opções para ir tanto para a Tela2 quanto para a Tela3?

    ResponderExcluir
  18. Este comentário foi removido por um administrador do blog.

    ResponderExcluir
  19. Oi, Rodrigo. Muito bom tutorial. Parabéns.

    E no caso, por exemplo, de querer retornar uma String de uma Acivity filha? No seu exemplo, a tela1 apenas informa (através de Toast) qual tela foi chamada. Tem como manusear os Bundles/Intents para que eles retornem uma String?

    No meu caso, chamo um FileChooser por uma Activity e preciso que o FileChooser retorne o caminho encontrado para a Activity. Consigo pegar o caminho e imprimi-lo (através de Toast) no FileChooser, mas não consigo pegar esse caminho de volta (uma String) na atividade que chama o FileChooser? Fui claro?

    Obrigado novamente!

    ResponderExcluir
  20. Gabriel, basta passar um Bundle na chamada referenciada em "Transição de uma tela de origem para uma tela de destino, com envio de uma informações da tela de destino para a tela de origem".

    ResponderExcluir
  21. Desculpa, não entendi. Você quer dizer em startActivityforResult? Porque esse método só aceita Intent como parâmetro.

    Estou fazendo (o FileChooser é chamado pelo menu):

    case R.id.importar:
    Intent intent1 = new Intent(MainActivity.this, FileChooser.class);
    Bundle extras = new Bundle();
    extras.putString("path", "vazio");
    intent1.putExtras(extras);
    startActivityForResult(intent1, code);
    return true;

    Isso para a chamada.

    E depois segue:

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.i("MainActivity", "Entrou na funcao " + "codigo: " + requestCode + "code: " + code);
    if (requestCode == code) {
    Bundle extras = new Bundle();
    extras = data.getExtras();
    Log.i("MainAct", "Retornou");
    Toast.makeText(this, "Caminho: " + extras.getString("path"), Toast.LENGTH_LONG).show();
    }


    Mas esse Toast não é mostrado.

    ResponderExcluir
  22. Aqui:
    http://2.bp.blogspot.com/-j0mY5QynnRI/UHhtQOIoP_I/AAAAAAAAAhE/0z_WNEDaH-0/s1600/16.png

    Na função "setaResultadoActivity".

    ResponderExcluir
  23. Oi Rodrigo, estou começando com Android agora e achei muito interessante seu texto, mas fiquei com uma dúvida, no exemplo que você deu só passa uma variável para a segunda tela. Como faço para passar várias variáveis? no meu caso tenho uma tela onde o usuário digita alguns números e quando ele clica no botão tabela, abre outra tela que faz uma tabela com os dados digitados. Como faço para passar mais de um valor para outra tela?

    ResponderExcluir
  24. Basta colocar mais de uma linha, tal quais a dos tipos abaixo:
    "extras.putString(, )"
    ou
    "extras.putBoolean(, )"

    ResponderExcluir
  25. Mano, sabes como fazer para a próxima tela, entrar com uma animação, tipo surgindo da esquerda, ou da direita?

    ResponderExcluir
  26. Sei sim. Este será o tema do próximo post que farei aqui o Blog.

    ResponderExcluir
  27. Muito bom seu post, parabéns por compartilhar esse conteúdo.
    Apenas um problema que está ocorrendo com o meu app, quando eu saio do menu (tela1) e vou para tela2, eu consigo criar um botão de voltar e funciona tranquilo, mas quando eu uso o botão de voltar do aparelho a aplicação fecha no lugar de voltar ao menu. Qual seria a solução? desde já agradeço.

    ResponderExcluir
  28. Olá Rodrigo.
    Estranho este comportamento. A lógica parece estar certa. Para eu opinar melhor, só vendo o código-fonte da sua aplicação.

    ResponderExcluir
  29. Boa noite rodrigo, achei muito interessante seu post. Estou com um problema na minha aplicação, que quando passo da tela1 para a tela2 por exemplo, a tela1 que possuía um objeto serializavel com valores setados, ao retornar, ele esta nulo. Gostaria de saber como proceder neste caso. desde já agradeço

    ResponderExcluir
  30. Olá ivantec.
    Este link destrincha o que você procura.
    http://stackoverflow.com/questions/10107442/android-how-put-parcelable-object-to-intent-and-use-getparcelable-method-of-bu

    ResponderExcluir
  31. Olá Rodrigo, estou com uma duvida.
    Tenho 3 telas, ao clicar na 1 eu passo um texto para a segunda, ao clicar na 2 passo o texto da 1 e da segunda para a 3 que irá exibi-los, estou utilizando o intent.putExtras porém só consigo passar o texto da 1 para a 2, como faço para receber esse texto da 1 na 2 e só passar para 3, tendo em vista que só a 3 que vai exibir os textos?

    ResponderExcluir
  32. Olá Ricardo.
    Não entendi bem seu exemplo. Teria como você me explicar melhor? Ou então mandar o código-fonte da sua aplicação?

    ResponderExcluir