Começando a Criar Interfaces Gráficas com Android

Page 1

Começando a Criar Interfaces Gráficas com Android • Introdução O estudo para criar este artigo foi feito em várias fontes, mas uma chamou a atenção por teu uma seqüência de exemplos muito bem explicados e de fácil entendimento. O texto se chama “Understanding User Interface in Android”. Neste artigo vamos aprender como criar interfaces gráficas (UI) mais básicas com Android, utilizando o Eclipse como IDE de desenvolvimento. Pretendo criar outros artigos falando sobre componentes mais complexos ou se aprofundando nos apresentados aqui. Para quem está chegando de mundos um pouco mais obscuros em relação à UI, como o Java ME, a diferença é gritante e impressiona. Com Android temos gerenciadores de layout sofisticados, componentes estilizados com efeitos atrativos graficamente, além de uma facilidade no desenvolvimento. Obs: O Java ME ganhou muito poder UI quando ganhou o framework LWUIT, permitindo uso de componentes e gerenciadores de layouts. Mas quem programou com Java ME pré-LWUIT sabe das dificuldades de criar uma tela amigável com Canvas. Existem algumas plataformas, como a da RIM (responsável pelos aparelhos BlackBerry) que redefiniu o Java ME para suas necessidades. Nestes aparelhos, também é possível criar telas atraentes para o usuário. O Android oferece dois modos de criar interfaces gráficas, uma é definindo um arquivo XML que será carregado no startup da aplicação e a renderização da tela é construída em tempo de execução. O outro modo é através da codificação pura. Na maioria dos casos o desenvolvedor usará as duas maneiras, porém, recomenda-se a preferência pelo uso do XML. Como vimos nos outros publicados por mim no Java Móvel, um aplicativo Android terá uma Activity, que é responsável pela interface gráfico da aplicação. Na verdade, ela pode ser imaginada como uma tela do seu software.


Obs: A Activity utiliza uma pilha, chamada de activity stack, o índice que estiver no topo da pilha será a tela exibida para o usuário da aplicação. Para quem programa para BlackBerry OS verá grandes similaridades com a pilha de Screens desta plataforma. Como dissemos anteriormente, existem dois modos de criação de UI no Android, aqui vamos nos deter principalmente no XML. Então, veja o exemplo abaixo: <?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" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/nome" /> </LinearLayout>

O XML acima representa a interface gráfica, contendo um campo de texto em um layout linear, orientado verticalmente. A aplicação deve possuir no mínimo uma classe que herde de Activity e sobrecarregue o método onCreate: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }

Perceba que definidos a interface através de um arquivo XML, usando o método setContentView. O R representa a classe com as constantes que identificam os recursos da nossa aplicação. Ao longo deste artigo vamos ver em detalhes o pouco que fizemos até aqui, vamos a luta.

• View e ViewGroup


Quando pensamos em interface gráfica de um aplicativo Android devemos ter em mente oque significa View e ViewGroup. Ambos representam qualquer componente visual que você visualizar na tela do aparelho. Como estas classes são de suma importância para o entendimento do texto no geral, vou parafrasear o site oficial de desenvolvedores Android: A classe View representa o bloco de construção básico para componentes de interface gráfica. Uma View ocupa uma área retangular na tela e é responsável por desenhar e controlar os eventos. View é a classe básica para widgets, que são usados para criar componenets de UI interativos (botões, caixas de texto, etc.). A subclasse ViewGroup é a classe base para layouts, que são containers invisíveis que contém Views (ou outros ViewGroups) e definem as propriedades desse layout. Veja a Figura 1 para um melhor entendimento.

Figura 1: Views e Views Group.

Para fazer com que o leitor entenda definitivamente à importância dessas duas classes, veja a Figura 2: Na interface existe um vasto conjunto de widgets, desde botões até caixas de seleções. Todos esses componentes estão dentro de um gerenciador de layout, mais precisamente em um LinearLayout.


Figura 2: Exemplo real de Views e Views Group.

• Gerenciadores de Layout Vamos começar a brincadeira com UI no Android com os gerenciadores de layout. Como o próprio nome indica, estas classes orientam o posicionamento dos widgets na tela. Dependendo do layout utilizado, até a altura e largura do componente é alterada. Para quem trabalhou com o swing do Java lembra do BorderLayout, FlowLayout, dentre outros. Até mesmo no Java ME é possível utilizar gerenciador parecidos com os mencionados anteriormente, através do LWUIT. Inicialmente vamos criar uma aplicação Android que alteraremos conforme o gerenciador de layout a ser estudado.

o Criando o projeto Recapitulando dos nossos artigos anteriores. Siga o caminho file>new->Android Project. Configure as seguintes opções e clique em Ok:


• Project Name: LayoutsAndroid. • Contents: Create new project in workspace. • Target Name: Android 2.1. • Application Name: Layouts Android • Package Name: com.estudo.android. • Create Activity: LayoutsAndroid


Figura 3: Configurações do projeto inicial.

O Eclipse já cria o projeto e toda a estrutura de pastas e códigos necessária. Execute este projeto e verá o seguinte:

Figura 3: Projeto inicial emulado.

Dê uma olhada também no arquivo main.xml, que pode ser encontrado no caminho res->layout. Listagem 1: <?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" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>

O nosso exemplo já usa um gerenciador de layout, o LinearLayout, que, por acaso, será nosso primeiro objeto de estudo.


o LinearLayout Este gerenciador organiza seus componentes filhos em uma única coluna ou única linha, dependendo da orientação que o mesmo tiver. Para ficar mais claro, vamos editar o arquivo main.xml citado anteriormente: Listagem 2: <?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" > <EditText android:id="@+id/EditText01" android:layout_width="180px" android:layout_height="wrap_content" /> <EditText android:id="@+id/EditText02" android:layout_width="180px" android:layout_height="wrap_content" /> <Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Enviar" /> </LinearLayout>

Se executarmos a aplicação agora, veja como fica:

Figura 4: Projeto alinhados verticalmente.


Vamos fazer algumas alterações no XML para compreender oque ele faz. Na declaração do layout, que sempre deve ser o nó raiz do documento, temos o LinearLayout. Definimos para este componente três propriedades: android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"

A orientation defina se os componentes se desenrolarão no sentido horizontal ou vertical. Se a tela terá apenas uma linha ou uma coluna, como falado anteriormente. Mudemos essa propriedade para: android:orientation="horizontal"

O resultado podemos ver abaixo:

Também definimos as propriedades de largura (width) e altura (height). Ambos usam fill_parent, que diz o seguinte: use todo o espaço disponível na tela. Vamos mudar novamente o main.xml, mais especificamente as propriedades do LinearLayout.


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

O resultado é:

Perceba que o restante dos componentes foi cortado, por exceder os limites do seu container, o LinearLayout. Na prática, porém, vai ser raro as vezes em que este gerenciador de layout não vai ocupar todo o espaço disponível na tela. Existem alguns atributos específicos para componentes que fazem todo sentido quando aplicados no LinearLayout. Vamos trabalhar com duas propriedades: android:layout_weight e android:layout_gravity. Reescreva o main.xml: LISTAGEM 3: <?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"> <EditText android:id="@+id/EditText01" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="0.3" /> <EditText android:id="@+id/EditText02" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="0.7" /> <Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Enviar" android:layout_gravity="right" /> </LinearLayout>

Depois de executar veremos o seguinte:


Figura 7: Utilizando gravity e weight.

A propriedade gravity funciona como uma função de alinhamento em relação ao container, como usamos right no botão, veja que ele se encontra na extremidade direita da tela. O weight repassa para os componentes o espaço não utilizado na tela. Há, não se preocupem com os componentes, eles serão tratados daqui a pouco. Volte para a figura 4 e perceba que o espaço “vazio” desapareceu, sendo atribuído para as duas caixas de textos que definiram a propriedade weight. Perceba também, que a segunda caixa de texto recebeu 70% do espaço não utilizado, por isso ficou maior que a primeira caixa. Bem, encerramos por aqui a discussão sobre a LinearLayout. Há, não se preocupe com os widgets (componentes). Logo veremos detalhadamente cada um.

o AbsoluteLayout Este gerenciador define exatamente a posição (coordenada x/y) onde cada componente deve ficar. Este layout é menos flexível e também exige um maior trabalho para manutenção. Por exemplo, quando o aparelho muda a orientação, quem deve redefinir a posição dos componentes é o programador, via código.


Mas chega de conversa, redefina o main.xml para isso: LISTAGEM 4: <?xml version="1.0" encoding="utf-8"?> <AbsoluteLayout android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <EditText android:id="@+id/EditText01" android:layout_width="190px" android:layout_height="wrap_content" android:layout_x="12px" android:layout_y="12px" /> <EditText android:id="@+id/EditText02" android:layout_width="190px" android:layout_height="wrap_content" android:layout_x="12px" android:layout_y="60px" /> <Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Enviar" android:layout_x="12px" android:layout_y="161px" /> </AbsoluteLayout>

Quando executamos a nossa aplicação fica com a seguinte cara:

Figura 8: Utilizando AbsoluteLayout.

Simples né? Mas como o leitor já deve ter percebido, na é um gerenciador de layout muito aconselhado.


o TableLayout Este gerenciador pode ser o mais auto-descritivo de todos. Para surpresa geral, ele organiza seus componentes filhos em linhas e colunas. As linhas da tabela são representadas pela classe TableRow. As colunas são criadas conforme a inserção de componentes em uma mesma linha. Não é permitido bordas em linhas, colunas e células. Reescreva o main.xml: LISTAGEM 5: <?xml version="1.0" encoding="utf-8"?> <TableLayout android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <TableRow> <TextView android:text="Nome" android:layout_width="100px" /> <EditText android:id="@+id/EditText01" android:layout_width="200px" /> </TableRow> <TableRow> <TextView android:text="Endereço"/> <EditText android:id="@+id/EditText02" /> </TableRow> <TableRow> <TextView /> <Button android:id="@+id/Button01" android:text="Enviar" /> </TableRow> </TableLayout>

Para gerar uma interface como a Figura 9:

Figura 8: Utilizando TableLayout.


Vários pontos devem ser detalhados aqui. Primeiramente, veja que definimos a largura dos componentes somente para a primeira linha. Isso porque a coluna define sua largura como igual a largura do maior componente horizontal. Assim, se redefinirmos a última caixa de texto e o botão para: <TableRow> <TextView android:text="Endereço"/> <EditText android:id="@+id/EditText02" android:layout_width="100px"/> </TableRow> <TableRow> <TextView /> <Button android:id="@+id/Button01" android:text="Enviar" android:layout_width="50px" /> </TableRow>

O resultado da interface vai ser o mesmo. Tanto o campo de texto de endereço como o botão tem largura menor que a primeira caixa de texto. Na última linha colocamos um campo de texto vazio: <TextView />

Isso deve ser feito para que não ocorra o seguinte efeito:

Figura 8: Utilizando TableLayout com espaço a mais.

o RelativeLayout O RelativeLayout trabalha da seguinte forma. Cada componente filho deve indicar sua posição em relação a outro componente.


Vamos começar nossos estudos com este main.xml. LISTAGEM 6: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout android:id="@+id/RLayout" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <TextView android:id="@+id/lblComments" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Idéias" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" /> <EditText android:id="@+id/txtComments" android:layout_width="fill_parent" android:layout_height="170px" android:textSize="18sp" android:layout_alignLeft="@+id/lblComments" android:layout_below="@+id/lblComments" android:layout_centerHorizontal="true" /> <Button android:id="@+id/btnSave" android:layout_width="125px" android:layout_height="wrap_content" android:text="Salvar" android:layout_below="@+id/txtComments" android:layout_alignRight="@+id/txtComments" /> <Button android:id="@+id/btnCancel" android:layout_width="124px" android:layout_height="wrap_content" android:text="Lixeira" android:layout_below="@+id/txtComments" android:layout_alignLeft="@+id/txtComments" /> </RelativeLayout>

O primeiro componente, um texto estático, está orientado em relação ao sei pai, ou seja, ao próprio layout. Então, definimos que ele estará a esquerda e ao topo da tela. android:layout_alignParentTop="true" android:layout_alignParentLeft="true"

Antes de prosseguir, veja como fica esta interface em trabalho.


Figura 9: Utilizando RelativeLayout.

A caixa de texto tem sua localização definida com: android:layout_alignLeft="@+id/lblComments" android:layout_below="@+id/lblComments"

O layout_alignLeft diz que o componente estará alinhado a esquerda do componente com o id lblCmments, que neste caso, é o texto estático “Idéias”. Também, usamos o layout_below para dizer que este componente estará logo abaixo do mesmo componente de texto lblComments. O botão de lixeira define sua localização com; android:layout_below="@+id/txtComments" android:layout_alignLeft="@+id/txtComments"

Exatamente da forma como definimos nossa caixa de texto, exceto pela mudança do componente referenciado. Agora indicamos o txtComments como ponto de referência. Finalmente, temos o botão salvar: android:layout_below="@+id/txtComments" android:layout_alignRight="@+id/txtComments"

A única mudança é de alignLeft para alignRight.


O desenvolver tem acesso as seguinte propriedades para definir o posicionamento dos componentes no RelativeLayout: * layout_above * layout_alignBaseline * layout_alignBottom * layout_alignLeft * layout_alignParentBottom * layout_alignParentLleft * layout_alignParentRight * layout_alignParentTop * layout_alignRight * layout_alignTop * layout_Below * layout_centerHorizontal * layout_centerInParent * layout_centerVertical * layout_leftOf * layout_rightOf * layout_true

o FrameLayout O FrameLayout reserva um espaço na tela que deve ser utilizado por uma View. Se mais de uma View for colocada nesta área, haverá sobreposição de componentes, com o último que foi inserido aparecendo por primeiro. Vamos a codificação. Altere main.xml: LISTAGEM 7: <?xml version="1.0" encoding="utf-8"?> <AbsoluteLayout android:id="@+id/AbsoluteLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout android:layout_x="88dip"


android:id="@+id/FrameLayout01" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_y="104dip"> <TimePicker android:id="@+id/TimePicker01" android:layout_width="wrap_content" android:layout_height="wrap_content"> </TimePicker> </FrameLayout> </AbsoluteLayout>

Neste XML, apenas o TmePicker pode parecer estranho, mas ele é somente um widget para escolher uma hora do dia. Veja visualmente como fica a execução da aplicação com este XML:

Figura 10: Utilizando FrameLayout.

Estamos usando o FrameLayout dentro de um AbsoluteLayout, logo, devemos especificar sua posição em coordenadas x, y. O FrameLayout possui apenas uma widget, o TimePicker. Até aqui parece que não veremos nada de diferente nesse layout. Mas vamos tentar adicionar mais um componente no XML: <FrameLayout android:layout_x="88dip"


android:id="@+id/FrameLayout01" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_y="104dip"> <TimePicker android:id="@+id/TimePicker01" android:layout_width="wrap_content" android:layout_height="wrap_content"> </TimePicker> <Button android:text="Botão" android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </FrameLayout>

Agora iniciamos a aplicação e veremos isto:

Perceba que o botão sobrepôs o TimePicker. Isso acontece porque o FrameLayout mostra apenas uma View.

o ScrollView Agora fiquei em dúvida entre qual dos layout é o mais autodescritivo: TableLayout ou ScrollView. Este último, mostra apenas uma


ViewGroup que pode exceder os limites da tela física do aparelho. Geralmente o ViewGroup utilizado é um LinearLayout. Falta pouco, vamos editar pela última vez o main.xml neste tópico de layouts. <?xml version="1.0" encoding="utf-8"?> <ScrollView android:id="@+id/widget54" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android" > <LinearLayout android:layout_width="310px" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:text="Brasil" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="França" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Argentina" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Uruguai" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="México" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Paraguai" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Chile" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Colômbia" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Alemanha" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Estados Unidos" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Inglaterra" android:layout_width="110px" android:layout_height="wrap_content" /> <Button android:text="Escócia" android:layout_width="110px" android:layout_height="wrap_content" /> </LinearLayout> </ScrollView>


Este layout não tem mistério, o leitor já deve até estar visualizando mentalmente como ficará a execução do aplicação desta vez. Veja na Figura abaixo:

Veja a barra no lado direito. Fácil esse scroll view hein.


• Views Depois que aprendemos sobre os gerenciadores de layout, representados pelos ViewsGroups, está na hora de aprender sobre s os componentes, representados pelas classes que herdam de View. Para melhor compreensão, vamos dividir este tópico em cinco áreas: • Views básicas: para criar componentes básicos, como caixa de textos e botões; • Pickers Views: componentes especiais que permitem o usuário selecionar de uma certa fonte; • Views de listas: componentes que mostram uma lista de informações; • Views para imagens: componentes especializados para tratamento com imagens. Vocês irão se surpreender com estes componentes; • Menus; • Extras. Antes de iniciarmos este instigante tópico de estudo, vamos criar um novo projeto no Eclipse, chamado ViewsAndroid. Como o leitor já sabe os passos, só vou deixar a imagem com as configurações:


Agora vamos partir para a ação.

o BasicViews - Início As views básicas não precisam de muita explicação, o leitor já deve ter usado elas intensamente em qualquer software. E, se o leitor for programador, então já deve ter criado milhares destes componentes:


• TextView • EditText • Button • ImageButton • CheckBox • ToggleButton • RadioButton • RadioGroup Vamos editar nosso main.xml do projeto recém criado. Reescreva-o com as seguintes informações: <?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" > <TextView android:id="@+id/txtView" android:text="Cadastro" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnAbrir" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Abrir" /> <ImageButton android:id="@+id/btnImg1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/icon" /> <EditText android:id="@+id/txtName" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <CheckBox android:id="@+id/chkAutosave" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Autosave" /> <CheckBox android:id="@+id/star" style="?android:attr/starStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <RadioGroup android:id="@+id/rdbSexo"


android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <RadioButton android:id="@+id/rdbM" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Masculino" /> <RadioButton android:id="@+id/F" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Feminino" /> </RadioGroup> <ToggleButton android:id="@+id/toggle1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>

Rode este projeto e verás o seguinte resultado:

Figura 14: Exemplo de Views.

O primeiro componente é um TextView, dispensando maiores apresentações. O segundo componente é um Button, também é muito auto-


explicativo. O terceiro componente é um ImageButton, mesclando uma imagem ao botão tradicional. Aqui podemos perceber um ponto interessante na forma de programar do Android. Sempre devemos definir a fonte da imagem, no nosso exemplo utilizamos: android:src="@drawable/icon"

O leito deve estar lembrado que na classe Activity usamos a seguinte linha de código: setContentView(R.layout.main);

Ou seja, ambos referem-se aos recursos localizados na pasta res. A diferença é que no primeiro caso acessamos estes recursos através do XML, sendo assim devemos definir um ‘@’, a localização do recurso (drawable, layout ou values) e o nome. No último caso acessamos o recurso através do código Java. Sendo assim, você indica a classe R e, o caminho referente ao recurso desejado. Seguindo com o exemplo da Figura 14. O quarto componente é um campo de texto, representado pela classe EditView. O quinto componente é um simples CheckBox. O sexto componente também é um CheckBox, mas com uma especificidade. Redefinimos seu estilo, veja: style="?android:attr/starStyle"

O sétimo e oitavo componente são instâncias de RadioButton, dentro de um ButtonGroup. Finalmente, o último componente é um ToggleButton, um botão estilizado com dois estados: on e off. Também podemos mudar os textos do ToggleButton. Adicione mais duas propriedades ao último componente citado: android:textOn="Sim" android:textOff="Não"

Agora o componente apresenta textos definidos pelo programador, e não aqueles padrões, veja:


Outro componente que possui atributos importantes para serem citados aqui, é o EditText. Em alguns casos, o programador adiciona um campo de texto onde o usuário informa sua senha, sendo assim, o texto deve ser substituído por asteriscos. Para atingir este objetivo, existe uma propriedade que pode ser adicionada ao EditText: <EditText android:id="@+id/txtName" android:layout_width="fill_parent" android:layout_height="wrap_content" android:password = "true" />

Veja oque acontece:

o BasicViews – Tratamento de Eventos Até agora só mostramos alguns componente na tela, mas não inserimos nenhum tratamento de eventos. E como podemos fazer isso? No início deste artigo falamos rapidamente que a interface gráfica de um aplicativo pode ser construída de duas maneiras: diretamente com o XML ou codificando cada componente. Por exemplo, já usamos o TextView no XML, porém, existe uma classe TextView que pode ser instanciada e tratada diretamente via código.


Mesmo quando optamos por criar a interface via XML, que é mais indicada inclusive pelo site de desenvolvedores do Android, vamos usar a codificação pura em Java para tratar dos eventos. Vamos alterar a classe ViewsAndroid, localizada na pasta com.estudo.android, em src: Listagem 11: 1: package com.estudo.android; 2: 3: import android.app.Activity; 4: import android.os.Bundle; 5: import android.view.View; 6: import android.widget.CheckBox; 7: import android.widget.Toast; 8; 9: public class ViewsAndroid extends Activity { 10: /** Called when the activity is first created. */ 11: @Override 12: public void onCreate(Bundle savedInstanceState) { 13: super.onCreate(savedInstanceState); 14: setContentView(R.layout.main); 15: 16: CheckBox checkBox=(CheckBox) findViewById(R.id.chkAutosave); 17: checkBox.setOnClickListener(new View.OnClickListener() 18: { 19: public void onClick(View v) { 20: Toast.makeText(getBaseContext(), "Salvar "+ (((CheckBox)v).isChecked()?"Aut.":"Man."), Toast.LENGTH_SHORT).show(); 21: } 22: }); 23: 24: } 25:}

Não sei se vocês perceberam que em todos widgets (componentes) criados até aqui, sempre definimos uma propriedade android:id. Essa identificação é usada para recuperar este componente no código Java através do método findViewById(), linha 16 da listagem de código 16 . Com posse da instância de CheckBox podemos configurar um listener para o evento de clique, usando o método setOnClickListener e passando por parâmetro uma instância de OnClickListener. Na Listagem de código isso é feito na linha 17. Perceba que instanciamos a classe passada para o método da mesma linha. Quando definimos o método OnClickLisener devemos redefinir o comportamento do método onClick. Este, por sua vez, programa oque acontecerá quando o comportamento receber o evento de clique.


Veja nas Figuras abaixo oque programos para o momento em que o botão for selecionado como checado e não-checado. Inicialmente o CheckBox está desativado, então, quando ele for selecionado mostramos “Salvar Aut.”, caso inverso mostramos “Salvar Man.”.

Figura 15: CheckBox ativado.

Figura 16: CheckBox desativado.

Esse texto que aparece na tela é um Toast. Veja a linha de código 20 da Listagem 11. Usamos o método estático makeToast() que recebe os seguintes parâmetros: um contexto, a mensagem e um inteiro que define a duração. No temo usamos a constante LENGHT_SHORT. Para os componentes Button e ToogleButton também podemos usar o OnClickListener. Para o RadioButton vamos utilizar o OnCheckedChangeListener. Vamos fazer algumas alterações no exemplo anterior, assim, o ChangeLisener será compreendida perfeitamente. A idéia é associar uma imagem com o campo de escolha do sexo. Sendo assim, a primeira mudança é no main.xml, adicionando mais uma propriedade ao botão de escolha do sexo masculino. <RadioButton android:id="@+id/rdbM" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Masculino" android:checked="true" />

Com isso, a opção de “Masculino” vai vir marcada como padrão. O primeiro passo é criar um widget ImageView na nossa tela. Inicialmente ela mostrará uma imagem do sexo masculino:


</RadioGroup> <ImageView android:id="@+id/imgSexo" android:src = "@drawable/menino" android:layout_width="wrap_content" android:layout_height="wrap_content" />

Ao rodar este aplicativo veremos o seguinte:

Figura 17: RadioButton masculino selecionado.

As imagens que aparecerão são da internet, você por qualquer imagem na sua pasta res/drawable e chama-las de menino e menina. Se o usuário escolher “Feminino” ainda não acontece nada. Eu disse ainda, porque vamos mudar isso agora. Veja a Listagem de código 12: Listagem 12: 1:public void onCreate(Bundle savedInstanceState) { 2: ... 3: RadioGroup radioGroup = (RadioGroup) findViewById(R.id.rdbSexo); 4: radioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() 5: { 6: public void onCheckedChanged(RadioGroup group, int checkedId) { 7: RadioButton rdb = (RadioButton) findViewById(checkedId); 8: ImageView img = (ImageView) findViewById(R.id.imgSexo); 9: if (rdb.getText().equals("Feminino")){ 10: img.setImageResource(R.drawable.menina); 11: } else { 12: img.setImageResource(R.drawable.menino); 13: } 14: } 15: }); 16:}

Na linha 4 adicionamos o OnCheckedChangeListener ao componente RadioGroup. Assim, quando um dos RadioButton´s for selecionado o método onCheckedChanged(), na linha 6, será notificado. Os parâmetros que este método recebe são: o RadioGroup que recebeu a


iteração e o identificador único do RadioButton que foi marcado como checado. Na linha 7 recuperamos o objeto RadioButton através do id recebido como parâmetro. Na linha 8 recuperamos o objeto ImageView da forma já conhecida, ou seja, referenciando o componente através das constantes da classe R. Na linha 9 testamos se o texto do RadioButton selecionado é igual a “Feminino”. Caso afirmativo, devemos configurar a fonte do ImageView para a imagem da menininha. E é isso que fizemos na linha 11. Caso o teste booleano resulte em false, a imagem mostrada deve ser do menino (como já foi mostrado na Figura 17). Agora podemos executar o aplicativo novamente e marcar a caixa de seleção “Feminino”. Veremos algo semelhante ao mostrado na Figura 18:

Figura 18: RadioButton masculino selecionado.

o BasicViews – Barra de Progresso A barra de progresso é um componente muito comum em qualquer interface de usuário. Seus exemplos de uso podem ser: acompanhar quantos bytes foram transmitidos quando envia seu relatório para seu chefe, acompanhar um processamento um pouco mais pesado e não pensar que a aplicação deu erro e está travada, dentre outros. Vamos criar uma nova tela para vermos o comportamento desse widget. Crie um novo arquivo .xml em res/layout, chamado barradeprogresso.xml. Veja como na Figura 19:


Figura 19: Criando o barradeprogresso.xml.

Edite o barradeprogresso.xml com o código da Listagem 13: Listagem 13: <?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" > <ProgressBar android:id="@+id/progressbar" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnEnviar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Enviar" /> </LinearLayout>


Mude também a classe ViewsAndroid (Listagem 14). Listagem 14: package com.estudo.android; import android.app.Activity; import android.os.Bundle; public class ViewsAndroid extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.barradeprogresso); } }

Depois de executar o aplicativo temos:

Essa é a forma mais básica da barra de progresso do Android, dê uma olhada nas propriedades e verá que não acrescentamos nada de novo ao que já vínhamos utilizando. O modo que estamos usando este componente é padrozinado, seu ciclo é indeterminado. Útil para operações que não temos idéia de quando serão finalizadas. No código acima a barra de progresso (que mais parece um círculo de progresso) vai ficar na tela infinitamente. Vamos adicionar o código da Listagem 15 no onCreate: Listagem 15: 1:progressBar = (ProgressBar) findViewById(R.id.progressbar); 2: 3:new Thread(new Runnable() { 4: public void run() { 5: while (progressStatus < 10) { 6: progressStatus++;// = doSomeWork(); 7: try {


9: Thread.sleep(500); 10: } catch (InterruptedException e) { 11: e.printStackTrace(); 12: } 13: } 14: 15: progressBar.setVisibility(8); 16: } 17:}).start();

A Listagem 15 recupera o objeto PorgressBar logo na primeira linha. Posteriormente, adicionamos uma rotina com código Java puro, que apenas roda uma Thread enquanto a variável progressStatus for menor que 10. Depois disse ele muda a visibilidade do ProgressBar, para 8. Execute o aplicativo, espere alguns segundos e...

Como assim? Que erro é esse? Substitua a linha 15 pelo conjunto de linhas de código abaixo: handler.post(new Runnable() { public void run() { progressBar.setVisibility(8); } });


O erro aconteceu porque dentro de uma Thread, não é possível lançar outra linha de execução. O Handler permite que isso acontece trabalhando diretamente com o sistema operacional. Depois da correção você pode reiniciar a aplicação e esperar alguns segundos que o ProgressBar desaparecerá da tela.

Mas ainda ficou uma questão em aberto. Reveja a linha de código: progressBar.setVisibility(8);

Afinal, oque significa o parâmetro 8. O ProgressBar pode ter três estados possível, identificados por números inteiros: • 0 – visível • 4 – invisível • 8 – finalizado Por exemplo, se mudarmos a linha do setVisibly para: progressBar.setVisibility(4);

Depois do mesmo intervalo de tempo veríamos:


Mas ainda não acabamos a discussão sobre o ProgressBar. Outra propriedade interessante é a possibilidade de mudar o estilo do widget. O primeiro passo é redefinir o widget em nosso xml: <ProgressBar android:id="@+id/progressbar" android:layout_width="wrap_content" android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal" android:max="50" />

Fizemos duas coisas. O atributo style define o estilo do componente. O android:max define o valor máximo da barra de progresso (que agora sim terá um formato de barra de progresso). Na classe ViewsAndroid redefina o bloco de código dentro do while para: while (progressStatus < 50) { progressStatus++; handler.post(new Runnable() { public void run() { progressBar.setProgress(progressStatus); } }); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }

Perceba que implementamos o método setProgress dentro do laço, ou seja, a cada iteração a barra de progresso aumenta sua área completada. Depois chegar aos 50 (seu valor máximo) ela ainda deverá sumir, portanto, o restando do código permanece igual. Execute novamente o aplicativo e recebera:


o BasicViews – AutoCompleteTextView O AutoCompleteTextView é filho de TextView, sendo assim, ele traz um campo para inserção de texto. Seu diferencial é a possibilidade de definir um conjunto de Strings que funcionam como auto complemento do texto que está sendo digitado pelo usuário. Reescreva o barradeprogresso.xml com o código da Listagem 16: Listagem 16: <?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" > <AutoCompleteTextView android:id="@+id/txtEstadios" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>

Até este momento apenas criamos o campo de texto. Se olharmos o aplicativo nesse momento a interface conterá apenas uma caixa de exto normal, sem complemento de código nenhum. Agora vamos editar também o ViewsAndroid com o texto da Listagem 17: Listagem 17: package com.estudo.android; import import import import

android.app.Activity; android.os.Bundle; android.widget.ArrayAdapter; android.widget.AutoCompleteTextView;

public class ViewsAndroid extends Activity { 1: 2: String[] estadios = { "Soccer City", "Ellis Park", "Moses Mabhida", "Peter Mokaba", "Mbombela Stadium", "Loftus Versfeld", "Royal Bafokeng", "Free State", "Green Point", "Nelson Mandela Bay" };

3: 4: 5: 6:

/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.barradeprogresso);


7: ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout. simple_dropdown_item_1line, estadios); 8: AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.txtEstadios); 9: textView.setThreshold(3); 10: textView.setAdapter(adapter); 11: } 12:}

Vamos examinar o código a partir da linha 8. Neste ponto criamos a instãncia de AutoCompleteTextView. Na linha 9 usamos o método setThreshold para definir que depois da terceira letra o complemento já pode trabalhar e, finalmente, na linha 10 adicionamos o ArrayAdapter ao componente, este, definirá os textos que podem ser usados no complemento. O ArrayAdapter, por sua vez, é criado na linha 7. O construtor usado recebe um contexto, que pode ser this. Recebe também um segundo parâmetro inteiro, que define seu comportamento. O último parâmetro recebe um vetor de Objects com as Strings para complemento. Agora podemos executar a aplicação novamente.

Perceba que depois que digito a terceira letra, o ‘e’,ele me traz as opções cadastradas no ArrayAdapter.

o PickerViews Estetipo de widget permite que o usuário selecione uma data ou hora de um componente estilizado e atraente visualmente. Vamos criar uma nova interface que pede o nome do usuário, a data e hora do nascimento e calcula quantos dias faltam para tira a carteira de


habilitação, ou, quantos dias faltam para a aposentadoria (pensando em 65 anos). Se o usuário tiver mais que 65 anos, mostramos quantas copas o usuário já assistiu. Crie um novo arquivo XML chamado datahora.xml: Listagem 18: <?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" > <EditText android:id="@+id/txtNome" android:layout_width="200px" android:layout_height="wrap_content" /> <TimePicker android:id="@+id/pckHora" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <DatePicker android:id="@+id/pckData" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/btnCalcular" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Calcular" /> </LinearLayout>

No XML da Listagem 18 os únicos dois componentes que se destacam por serem novos são o DatePicker e o TimePicker. As propriedades de ambos já são bem conhecidas do leitor. Também altere a classe ViewsAndroid usando o código da Listagem 19: Listagem 19: package com.estudo.android; import java.util.Calendar; import java.util.Date; import import import import import import import import

android.app.Activity; android.os.Bundle; android.view.View; android.widget.Button; android.widget.DatePicker; android.widget.EditText; android.widget.TimePicker; android.widget.Toast;

public class ViewsAndroid extends Activity { private static final String CATEGORIA = "data e hora";


/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.datahora); } }

Ao executarmos este aplicativo veremos:

Figura 26: Aplicativo usando DatePicker e TimePicker.

Nossa aplicação ainda não fará nada se clicarmos no botão “Calcular”. Para conseguir isso vamos adicionar o trecho de código da Listagem 20 depois da linha do setContentView no método onCreate. 1:Button botao = (Button) findViewById(R.id.btnCalcular); 2;botao.setOnClickListener(new View.OnClickListener(){ 3: public void onClick(View v){ 4: TimePicker hora = (TimePicker) findViewById(R.id.pckHora); 5: DatePicker data = (DatePicker) findViewById(R.id.pckData); 6: EditText edtNome = (EditText) findViewById(R.id.txtNome); 7; 8: Calendar calNasc = Calendar.getInstance(); 9: calNasc.set(Calendar.YEAR, data.getYear()); 10: calNasc.set(Calendar.MONTH, data.getMonth()); 11: calNasc.set(Calendar.DAY_OF_MONTH, data.getDayOfMonth()); 12: calNasc.set(Calendar.HOUR_OF_DAY, hora.getCurrentHour()); 13: calNasc.set(Calendar.MINUTE, hora.getCurrentMinute()); 14: 15: Calendar calHoje = Calendar.getInstance(); 16: calHoje.setTime(new Date());


17: 18: int tempo = calHoje.compareTo(calNasc); 19: 20: if (tempo > 0){ 21: calculaData(calNasc, calHoje, edtNome.getText().toString()); 22: } 23: } 24:});

Nas duas primeiras linhas configuramos o listener para o componente Button. Na linha 3 criamos o método que vai responder por todas iterações. Nas linhas 4, 5 e 6 recuperamos os objetos para o TimePicker, DatePicker e EditText. Na linha 8 criamos a instância de Calendar que armazena a data de nascimento do usuário. Na seqüência de linhas de 9 até 11, configuro os valores de ano, mês e data do calendário, utilizando os métodos da classe DatePicker para recuperar os valores configurados pelo usuário. As linhas 12 e 13 configuram a hora do dia e o minuto do nascimento do usuário, utilizando métodos da classe TimePicker. Perceba que não tratamos o tempo, apenas incluímos estas linhas de código como aprendizado, porém, pode ficar como um tema de casa para o leitor. Na linha 15 criamos uma nova instância de Calendar. Na linha 16 configuramos a data atual como valor para o objeto criado na linha anterior. Na linha 18 utilizamos o método compareTo da classe Calendar para pegar um número inteiro com a diferença entre as duas datas comparadas. Se o valor for maior que zero (teste realizado na linha 20), significa que o usuário informou uma data do passado, caso contrário, a lógica do programa não precisa ser concluída porque ninguém nasce no futuro, ainda. O leitor deve ter percebido que paramos no método calculaData, onde passamos três parâmetros: data de nascimento do usuário, data atual e nome do usuário. Veja na Listagem 21 o código do método calculaData: Listagem 21: public void calculaData(Calendar nascimento, Calendar agora, String nome){ int ano = agora.get(Calendar.YEAR); int mes = agora.get(Calendar.MONTH); int dia = agora.get(Calendar.DAY_OF_MONTH); int anoNasc = nascimento.get(Calendar.YEAR); int mesNasc = nascimento.get(Calendar.MONTH);


int diaNasc = nascimento.get(Calendar.DAY_OF_MONTH); int idade = ano - anoNasc; if(mes < mesNasc) { idade--; } else if (mes == mesNasc) { if(dia < diaNasc) { idade--; } } String mensagem = ""; if (idade < 18) mensagem = nome+ ", faltam "+(18 - idade)+" anos para tirar a carteira!"; else if (idade >= 18 && idade < 65) mensagem = "Calma "+nome+", faltam "+(65 - idade)+" anos para você se aposentar"; else mensagem = nome+ ", o senhor já assistiu "+(idade/4)+" copas"; Toast.makeText(getBaseContext(), mensagem, Toast.LENGTH_SHORT).show(); }

O código da Listagem 21 não tem segredo, é apenas código Java. Primeiramente calculamos a idade, conforme esse resultado configuramos a mensagem do aviso. Finalmente, utilizamos o Toast (já discutido nesse texto), para apresentar a mensagem ao usuário. Veja o resultado de um dos casos:


Figura 27: Caso de uso do aplicativo usando DatePicker e TimePicker.

Não utilizamos muito o TimePicker, mas o aplicativo já mostrou uma idéia do uso deste componente. Assim como os outros widgets do Android, este é bem fácil de usa. O programador também pode usar os pickers um uma janela de diálogo. Vamos fazer algumas alterações, primeiro, no datahora.xml: <?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" > <TextView android:id="@+id/txtAlarme" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Alarme não configurado"/> <Button android:id="@+id/btnConfigurar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Configurar" /> </LinearLayout>

Realmente acho que não preciso explicar o XML. A nossa aplicação vai mostrar uma caixa de texto e um botão. O objetivo é que ao interagir com o botão, o usuário receba uma caixa de diálogo com um seletor de hora para configurar seu despertador. Vamos alterar o conteúdo da classe ViewsAndroid. Veja a Listagem 23: Listagem 23: 1: private static final int TIME_DIALOG_ID = 0; 2: private TextView lblAlarme; 3: 4: /** Called when the activity is first created. */ 5: @Override 6: public void onCreate(Bundle savedInstanceState) { 7: super.onCreate(savedInstanceState); 8: setContentView(R.layout.datahora); 9: lblAlarme = (TextView) findViewById(R.id.txtAlarme); 10: Button btn = (Button) findViewById(R.id.btnConfigurar); 11: btn.setOnClickListener(new View.OnClickListener(){ 12: public void onClick(View v){ 13: showDialog(TIME_DIALOG_ID); 14: }


15: 16: 17:

}); }

18: @Override 19: protected Dialog onCreateDialog(int id) 20: { 21: switch (id) { 22: case TIME_DIALOG_ID: 23: TimePickerDialog td = new TimePickerDialog( 24: this, mTimeSetListener, 12, 00, false); 25: 26: return td; 27: } 28: return null; 29: } 30: 31: private TimePickerDialog.OnTimeSetListener mTimeSetListener = 32: new TimePickerDialog.OnTimeSetListener() 33: { 34: public void onTimeSet(TimePicker view, int hourOfDay, int minuteOfHour) 35: { 36: lblAlarme.setText("Alarme configurado para " + hourOfDay + ":" + minuteOfHour); 37: } 38: };

Na linha 9 recuperamos o objeto TextView, porque vamos editar seu texto posteriormente. Na linha 10 recuperamos o objeto Button. Na linha 11 adicionamos um OnClickListener para o botão recém criado. Quando o usuário clicar neste componente vamos abrir uma caixa de diálogo. Para q ue o diálogo mostrado não seja o padrão do Android, devemos sobrescrever o método onCreateDialog. Também por isso, passamos o TIME_DILAOG_ID como parâmetro, para termos certeza de interceptar somente nosso diálogo. Com isso, mensagens vindas diretamente do Android continuarão a serem mostradas. Dentro do onCreatDialog verificamos se estamos recebendo a mensagem do nosso próprio código. Caso afirmativo, instanciamos um novo objeto TimePickerDialog (linha 23). Seu construtor recebe um Context, um OnTimeSetListener, uma hora e um minuto inicial e, por fim, um valor booleano dentificando se o componente trabalhará com 24 horas ou, com horas AM e PM. Perceba que o OnTimeSetListener foi criado no mesmo código, na linha 31, devemos obrigatoriamente implementar o método onTimeSet (linha 34). Este último método é chamado sempre que a hora é configurada no TimePickerDialog. Quando isso acontecer apenas mudamos o texto do TextView. Veja em três passos a aplicação em funcionamento:


Figura 28: Tela inicial da aplicação.

Figura 29: Diálogo aberto após clicar no botão.

Figura 30: Depois de clicar em “Set” mudamos o texto do TetView.

o ListViews O ListView é um componente para mostrar uma grande lista de dados com scroll. Este componente não precisa de muita explicação, porém, tem algumas diferenças significativas com os exemplos que vimos até aqui. Para explorar ao máximo este widget vamos criar uma lista com todos os países da copa do mundo de 2010. Ao clicarmos em um dos nomes mostramos a posição da seleção no ranking da FIFA. Primeiramente vamos criar um novo XML, listview.xml. Veja seu conteúdo na Listagem 24: Listagem 24: <?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" > <ListView android:id="@+id/android:list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>

Ainda não vimos nenhuma novidade, mas veja como ficará nossa classe ViewsAndroid na Listagem 25:


Listagem 25: 1:public class ViewsAndroid extends ListActivity { 2: 3: String[] paises = { 4: "Africa do Sul", 5: ... 6: "Uruguai" }; 7: 8: int posicoes[] = new int[]{ 9: 90, ..., 18 }; 10: 11: public void onCreate(Bundle savedInstanceState) 12: { 13: super.onCreate(savedInstanceState); 14: setContentView(R.layout.listview); 15: 16: setListAdapter(new ArrayAdapter<String>(this, 17: android.R.layout.simple_list_item_1, paises)); 18: } 19: 20: public void onListItemClick(ListView parent, View v, int position, long id) { 21: Toast.makeText(this, paises[position]+" está na "+posicoes[position]+"° posição", Toast.LENGTH_SHORT).show(); 22: } 23:}

A primeira grande diferença já encontramos na linha 1, perceba que não estendemos mais a classe de Activity, mas sim de ListActivity. O interessante deste componente, é que ele já utiliza internamente uma ListView. Na linha 3 criamos um vetor de String com todos os nomes dos países participantes da copa do mundo. Como não iríamos colocar os 32 países aí, usamos as reticências. Na linha 8 é a vez de criar o vetor de inteiros com as posições das seleções no ranking da FIFA. O ListActivity precisa que a classe configure seu ListAdapter, que pode ser entendido como um adaptador que faz a ponte entre o vetor de itens com a lista propriamente dita. Na Listagem estamos fazendo isso na linha 16. Um de seus construtores (usados na nossa codificação) recebe um Context, um inteiro que define o layout da lista e um vetor com os itens da lista. Finalmente, implementamos o método onListItemChecked para tratar das interações do usuário com a lista. Com o parâmetro position definimos o país e sua colocação e apresentamos em um Toast. Veja na Figura abaixo o comportamento do aplicativo em execução:


Figura 31: Lista de países com interação no item Brasil.

Nossa lista está configurado para escolha única, padrão do componente. Mas poderíamos alterar isso, trabalhando com a sua propriedade android:choiceMode, que permite o uso de três constantes: none, singleChoice e multipleChoice.

o SpinnerViews Este componente também objetiva mostrar uma grande quantidade de dados em uma lista. Porém, ele mostra uma lista no estilo popup, lembrando o ChoiceGroup estilo POPUP NO Java ME. Vamos trabalhar no mesmo exemplo criado acima mas mudando o componente. Para começar, crie um arquivo chamado spinner.xml e edite-o conforme a Listagem 26: Listagem 26: <?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" > <Spinner


android:id="@+id/spinner1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>

A classe ViewsAndroid também sofrerá algumas mudanças. A primeira é na declaração da classe, que fica assim: public class ViewsAndroid extends Activity {

No onCreate também teremos alterações. Veja a Listagem 27: Listagem 27: 1: super.onCreate(savedInstanceState); 2: setContentView(R.layout.spinner); 3: 4: s1 = (Spinner) findViewById(R.id.spinner1); 5: ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, paises); 6: 7: s1.setAdapter(adapter); 8: s1.setOnItemSelectedListener(new OnItemSelectedListener() { 9: public void onItemSelected(AdapterView<?> arg0, 10: View arg1, int arg2, long arg3) { 11: int index = s1.getSelectedItemPosition(); 12: Toast.makeText(getBaseContext(), paises[index]+" ocupa a "+posicoes[index]+"° no ranking da FIFA", Toast.LENGTH_SHORT).show(); 13: } 14: 15: public void onNothingSelected(AdapterView<?> arg0) {} 16:});

Na linha 4 recuperamos o objeto Spinner. Este componente também trabalha com um adaptador, criado na linha 5, e configurado para o Spinner na linha 7. Na linha 8 adicionamos o OnItemSelectedListener, e implementamos seus dois métodos obrigatórios. No onItemSelected recuperamos a posição do item selecionado no Spinner (linha 11) e depois mostramos a mesma mensagem mostrada no exemplo anterior. Veja como ficou este aplicativo:

Figura 32: Tela inicial do aplicativo com Spinner.


Figura 33: Spinner mostrando seus itens.

Figura 34: Tela depois de selecionarmos a Argentina no Spinner.

Também podemos mudar a forma como a lista é mostrada ao usuário. Altere a linha 5 da lista 27 para:


ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, paises);

Veja o resultado:

Figura 35: Spinner com layout modificado.

o Gallery e ImageView A partir desse momento vamos ver componente que nos ajudam a apresentar imagens ao usuário. Se o leitor deste texto estava começando se apaixonar pelo Android, vai pedir a mão dele em casamento ao final deste tópico. Como este tópico difere dos demais, vamos criar um novo aplicativo, chamado ImagesInAndroid:


Figura 36: Criação do projeto ImagesInAndroid.

Crie um arquivo XML chamado passo1.xml. Veja seu conteúdo na Listagem 28: Listagem 28: <?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" > <Gallery android:id="@+id/gallery1" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>


Também precisamos alterar a classe ImagesInAndroid. Como as mudanças são várias, vamos por partes. Primeiramente veja o básico que já aprendemos sobre Android: package com.estudos.android; import … public class ImagesInAndroid extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.passo1); } }

Nenhuma novidade. O passo seguinte é separar algumas imagens e joga-las dentro da patas res.drawable. Veja como fica a estrutura de diretório na Figura 37:

Figura 37: Arquitetura atualizada com as imagens.

Feito isso, cria-se um variável com um vetor de instâncias de Integer. Veja abaixo como fica. Perceba que estamos referenciando a nossa classe de recursos, R.


Integer[] imageIDs = { R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5, R.drawable.pic6 };

Agora, vamos adicionar um evento de clique nas imagens do Gallery. O código mostrado abaixo está dentro do método onCreate: Gallery gallery = (Gallery) findViewById(R.id.gallery1); gallery.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { Toast.makeText(getBaseContext(), "Selecionou" + (position + 1), Toast.LENGTH_SHORT).show(); } });

Nesse código também não encontramos nada de novo em relação ao que já vimos até aqui. Agora execute este arquivo e:

Figura 38: Usando Gallery.

Cadê as imagens? As imagens não apareceram porque o Gallery também precisa de um adaptador, semelhante ao que ocorre com o ListView e o Spinner, vistos a pouco. Esse adaptador precisa ser criado, diferentemente do Spinner por exemplo, que já tem uma implementação da BaseAdapter para ela (SpinnerAdapter), ou do ListView, que possui o ListAdapter. Sendo assim, vamos criar uma classe interna chamada ImageAdapter, veja na Listagem 32 seu código: Listagem 33: 1:public class ImagesInAndroid extends Activity 2:{ 3: …


4: 5: public class ImageAdapter extends BaseAdapter { 6: private Context context; 7: private int itemBackground; 8: 9: public ImageAdapter(Context c) { 10: context = c; 11: } 12: 13: public int getCount() { 14: return imageIDs.length; 15: } 16: 17: public Object getItem(int position) { 18: return position; 19: } 20: 21: public long getItemId(int position) { 22: return position; 23: } 24: 25: public View getView(int position, View convertView, ViewGroup parent) { } 26: } 27:}

Veja que a classe ImageAdapter está dentro de ImagesInAndroid. Ela herda diretamente de BaseAdapter, e, como a linguagem Java ensina, existem alguns métodos que devem ser implementados ao herdar esta classe, sendo eles: • getCount (linha 13): retorna o número de componentes. No nosso caso retornamos o número de elementos no vetor imageIDs, que contém as imgens que serão mostradas no Gallery. • getItem e getItemId (linhas 17 e 21 respectivamente): retornam o objeto e o identificador do elemento em uma determinada posição. • getView (linha 25): retorna um objeto que herda de View que será mostrado em uma determinada posição do Gallery. Vimos que o getView retorna o objeto que será mostrado na galeria. Aqui estamos trabalhando com ImageView, mas é importante sabe que o método pode retornar qualquer classe que herda de View. Por exemplo, poderíamos retornar instâncias de TextView. Veja como ficaria o método getView:

public View getView(int position, View convertView, ViewGroup parent) { TextView tv = new TextView(context); tv.setWidth(100);


}

tv.setText("Teste"); return tv;

E o resultado é:

Figura 39: Usando Gallery com TextView.

Claro que este exemplo não tem nenhuma utilidade prática, mas serve como prova de conceito. Voltando aquilo que realmente importa neste tópico, que é Gallery com ImageView, devemos retornar uma instância desta última classe no lugar do TextView no método getView. Vamos editar novamente o método: public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView = new ImageView(context); imageView.setImageResource(imageIDs[position]); return imageView; }

E o resultado:

Figura 40: Usando Gallery com ImageView.


As imagens ainda estão com o seu tamanho natural, também é possível perceber que elas tem um sombreamento mais claro nas extremidades laterais, isso acontece porque é o estilo padrão do componente. Vamos configurar algumas propriedades do ImageView para melhorar o Gallery. Adicione mais estas duas linhas de código no corpo do getView: imageView.setScaleType(ImageView.ScaleType.FIT_XY); imageView.setLayoutParams(new Gallery.LayoutParams(150, 120));

Estamos definindo o tamanho do componente na segunda linha e, na primeira, configuramos o tipo de escala. Como resultado, a imagem vai ocupar toda a área reservada pra ela (150 por 120 pixeis). Veja como fica na Figura 41:

Figura 41: Usando Gallery com ImageView e parâmetros de layout.

Para finalizar com toque de ouro, vamos usar um estilo para melhorar a apresentação das fotos. public View getView(int position, View convertView, ViewGroup parent) { ... ... ... imageView.setBackgroundResource( android.R.drawable.alert_light_frame) ; return imageView; }


E o resultado disso:

Figura 42: Usando Gallery com ImageView e estilo.

Mas acho que nossa aplicação ainda não está legal, no momento quando clicamos em uma foto apenas mostramos na tela qual imagem foi clicada:

Figura 43: Resultado inicial de interação com imagens.

Mas acho que podemos fazer melhor que isso. Vamos mostrar a imagem selecionada na parte central da tela, em um componente ImageView. Para conseguir nosso objetivo edite o passo1.xml e adicione o seguinte trecho de código logo depois do Gallery.


<ImageView android:id="@+id/image1" android:layout_width="320px" android:layout_height="250px" />

Também altere o tratamento da interação do usuário com o Gallery, veja: gallery.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { ImageView imageView = (ImageView) findViewById(R.id.image1); imageView.setImageResource(imageIDs[position]); } });

O resultado está na Figura 44:

Figura 44: Interação com imagens configurando imagens no centro.

Lembram-se que antes usamos o tipo de escala para definir o comportamento da galeria de imagens na parte superior da tela, também podemos usar agora na visualização da imagem. Adicione o seguinte atributo ao ImageView no passo1.xml:


android:scaleType="fitXY" />

Agora a imagem vai preencher toda a região reservada ao ImageView. Veja na Figura 45:

Figura 45: ImageView escalado para preencher região central da tela.

o ImageSwitcher A troca de imagens que implementamos no exemplo anterior com ImageView e Gallery também pode ser conseguida com este componente , e com uma vantagem, o ImageSwitcher permite configurar alguns efeitos 3D na troca das imagens. Este exemplo será construído encima do anterior. Sendo assim, edite o arquivo passo1.xml conforme a Listagem 39: Listagem 39: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ff000000" > <ImageSwitcher android:id="@+id/switcher1"


android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" /> <Gallery android:id="@+id/gallery1" android:layout_width="fill_parent" android:layout_height="wrap_content"/> </RelativeLayout>

Também é preciso alterar a classe ImagesInAndroid. Siga a Listagem 40: Listagem 40: 1:public class ImagesInAndroid extends Activity implements ViewFactory { 2: private ImageSwitcher imageSwitcher; 3: 4: Integer[] imageIDs = {}; 5: 6: @Override 7; public void onCreate(Bundle savedInstanceState) 8: { 9: super.onCreate(savedInstanceState); 10: setContentView(R.layout.passo1); 11: 12: imageSwitcher = (ImageSwitcher) findViewById(R.id.switcher1); 13: imageSwitcher.setFactory(this); 14: 15: imageSwitcher.setInAnimation( AnimationUtils.loadAnimation( this, android.R.anim.fade_in)); 16: imageSwitcher.setOutAnimation( AnimationUtils.loadAnimation( this, android.R.anim.fade_out)); 27: 18: Gallery gallery = (Gallery) findViewById(R.id.gallery1); 19: ... 20: gallery.setOnItemClickListener(new OnItemClickListener() { 21: public void onItemClick(AdapterView parent, 22: View v, int position, long id) { 23: imageSwitcher.setImageResource(imageIDs[position]); 24: } 25: }); 26: } 27: 28: public class ImageAdapter extends BaseAdapter 29: {} 30: 31: @Override 32: public View makeView() { 33: ImageView imageView = new ImageView(this); 34: imageView.setBackgroundColor(0xFF000000); 35: imageView.setScaleType(ImageView.ScaleType.FIT_XY); 36: imageView.setLayoutParams(new 37: ImageSwitcher.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 38: return imageView; 39: } 40:}


Vamos falar somente das alterações feitas na classe anterior. Perceba que na linha 12 recuperamos o componente ImageSwitcher. Na linha 13 usamos o método setFactory para definir a fábrica usada para criar as duas imagens que serão usadas para o componente fazer o flip. Este método recebe como parâmetro uma classe que implemente a interface ViewFactory, implementada na mesma classe ImagesInAndroid, veja a linha 1. Nas linhas 15 e3 16 configuramos os eventos que a imagem terá no momento de entrada e de saída da tela do usuário. Também mudamos a tarefa a realizar quando o usuário clicar em uma das imagens do Gallery. Neste momento indicamos ao ImageSwitcher qual imagem ele deve apresentar na tela, ele próprio trata da renderização dos efeitos configurados anteriormente. Por fim, vá até a linha 32 e veja que implementamos o método makeView, obrigatório quando implementamos a ViewFactory. Estemétodo criará a nova View que será adicionada no switcher. Aqui também poderíamos passar qualquer componente que herde de View, como fizemos com o TextView no exemplo do tópico anterior (Gallery e ImageView). Ao executar a aplicamos veremos:

Figura 46: Uso do ImageSwitcher.


Aparentemente não percebemos nenhuma diferença, a não ser pelo fato da imagem ocupar a tela inteira. Porém, interaja com o Gallery para ver os efeitos do ImageSwitcher.

o GridView Para finalizar este artigo/tutorial/ vãos mostrar o GridView, que, como o próprio nome indica permite que mostramos imagens em uma grade de fotos. O passo1.xml fica da seguinte maneira: <?xml version="1.0" encoding="utf-8"?> <GridView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gridview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:numColumns="auto_fit" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:columnWidth="90dp" android:stretchMode="columnWidth" android:gravity="center" />

No próprio XML configuramos algumas propriedades que são próprias de qualquer grade, não só Android, como por exemplo: android:layout_width, android:layout_height, android:numColumns, android:verticalSpacing, android:horizontalSpacing, android:columnWidth. O ImagesInAndroid sofre mudanças, mas o leitor irá tirar de letra este código a esta altura do campeonato. Veja a Listagem de código 42: Listagem 42: public class ImagesInAndroid extends Activity { //---the images to display--Integer[] imageIDs = { R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5, R.drawable.pic6


}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.passo1); GridView gridView = (GridView) findViewById(R.id.gridview); gridView.setAdapter(new ImageAdapter(this)); gridView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { Toast.makeText(getBaseContext(), "pic" + (position + 1) + " selected", Toast.LENGTH_SHORT).show(); } }); } public class ImageAdapter extends BaseAdapter { private Context context; private int itemBackground; public ImageAdapter(Context c) { context = c; } public int getCount() { return imageIDs.length; } public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView == null) { imageView = new ImageView(context); imageView.setLayoutParams(new GridView.LayoutParams(85, 85)); imageView.setScaleType( ImageView.ScaleType.CENTER_CRO P); } else { imageView = (ImageView) convertView; } imageView.setImageResource(imageIDs[position]); return imageView; }


} }

Execute a aplicação:

Obs: as fotos são uma homenagem a uma pessoa mais que especial na minha vida, minha noiva e futura esposa. Sheila, te amo. Ah, ela liberou o direito de imagem, está tudo certo.

o CONCLUSÃO Primeiramente gostaria de agradecer mais uma vez ao autor do texto “Understanding User Interface in Android”, que foi de onde parti meu estudo em interfaces de usuário com Android. Além de outras fontes que pesquisei. No texto foi possível perceber claramente a facilidade que existe na criação das UIs de nossos aplicativos com a mistura entre XML e codificação Java. Os componentes do Android são fáceis de usar e muito bonitos, deixando um programador Java ME como eu de queixo caído, principalmente nas View que tratam de imagens. Se o tempo permitir pretendo continuar com a série de artigos aqui no Java Móvel, comentários e sugestões são sempre bem vindos.


o SOBRE MIM Meu nome é Ricardo da Silva Ogliari, sou graduado em Ciência da Computação pela Universidade de Passo Fundo. Atualmente também estou cursando uma pós-graduação em web, estratégias de inovação e tecnologia, no Senac SP. Trabalho com mobile a 6 anos, escrevo artigos para algumas revistas nacionais especializadas. Sou criador e mantenedor do http://www.mobilidadetudo.com e sou um dos membros do www.javamovel.com. Palestrei em eventos nacionais e internacionais, como o FISL e o JustJava, além de ter dezenas de artigos meus espalhados pelo mundão da internet.


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.