quarta-feira, 25 de abril de 2012

Programar para Android - 08


Botões que lançam outras atividades

Começamos por criar um novo projeto Try06 e retirar a string hello world, para ficar com um projeto vazio, mas já com a atividade já criada.

Desta vez vamos fazer algo um pouco mais longo e vamos ter o primeiro contacto com intents, permissões, obter dados do GPS e do Wifi e editar o AndroidManifest.xml.

Vamos criar uma atividade com 6 botões que lançam atividades ou realizam outras ações, algumas mais simples outras mais complexas.

Começamos por criar seis strings no strings.xml com nomes app1 a app6 e com o conteúdo: Google, Calculadora, Notificação, Vibrar, GPS, Wifi. Os botões irão:

  • lançar o endereço http://www.google.pt;
  • lançar a calculadora;
  • mostrar uma notificação no écrã;
  • vibrar o telemóvel;
  • obter informação sobre o GPS;
  • obter informação sobre a ligação Wifi.

No main.xml vamos criar o layout. Adicionamos três LinearLayout horizontais dentro do LinearLayout vertical. Dentro de cada LinearLayout horizontal criamos dois botões, ficando assim com seis botões. Usando o botão "Distribute Weights Evenly", podemos fazer que os botões ocupem todo o écrã de forma equilibrada. O aspeto fica assim:
O xml do layout fica assim:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp1"
            android:text="@string/app1" />

        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp2"
            android:text="@string/app2" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <Button
            android:id="@+id/button3"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp3"
            android:text="@string/app3" />

        <Button
            android:id="@+id/button4"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp4"
            android:text="@string/app4" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <Button
            android:id="@+id/button5"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp5"
            android:text="@string/app5" />

        <Button
            android:id="@+id/button6"
            android:layout_width="0dp"
            android:layout_height="fill_parent"
            android:layout_weight="1"
            android:onClick="runApp6"
            android:text="@string/app6" />
    </LinearLayout>

</LinearLayout>


Já não vamos precisar de mexer mais no strings.xml nem no main.xml.
Vamos agora ver o AndroidManifest.xml. Tal como o strings.xml e o main.xml, o Eclipse mostra uma interface gráfica, mas vamos editar diretamente o XML.
Este ficheiro contém informação sobre a aplicação que é usada, por exemplo, no Android Market (agora Play) para listar e identificar as aplicações.
Muitas das tags são óbvias. Desta vez vamos apenas acrescentar algumas linhas de texto, entre o <uses-sdk ...> e o <application ...>.
As linhas a acrescentar são estas:


    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    
    <uses-feature android:name="android.hardware.wifi" />
    <uses-feature android:name="android.hardware.location" />
    <uses-feature android:name="android.hardware.location.network" />
    <uses-feature android:name="android.hardware.location.gps" />


Os uses-permission listam as permissões que a aplicação precisa de ter para poder funcionar, neste caso precisa de poder ligar a vibração (VIBRATE), aceder aos dados de localização por GPS e pela rede (ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION) e aceder a informação da ligação sem fios (ACCESS_WIFI_STATE).
Esta informação também é mostrada ao utilizador no momento em que instala a aplicação e o utilizador tem de dar estas permissões à aplicação.
As linhas de uses-feature informam que a aplicação só irá funcionar em dispositivos que tenham o hardware indicado (neste caso wifi e GPS). Esta informação é usada no market para filtrar as aplicações que não são compatíveis com o dispositivo do utilizador.

Finalmente vamos ao código e graças à separação que o Android faz ao colocar o layout e permissões em ficheiros XML, só temos de nos preocupar mesmo com o código.
O método onCreate() que já existe não precisa ser mexido, apenas precisamos criar seis métodos novos, um para cada botão, com os nomes runApp1() a runApp6(). Estes foram os nomes definidos no main.xml.

O primeiro botão lança um browser para aceder ao endereço http://www.google.pt. Isso consegue-se com este código:


public void runApp1(View v) {
Intent i = new Intent(Intent.ACTION_VIEW, 
          Uri.parse("http://www.google.pt/"));
startActivity(i);
}

Um intent é um objeto que, basicamente, contém uma mensagem que é passada ao sistema. Neste caso o intent é criado com a mensagem (ação) ACTION_VIEW e com um parâmetro "http://www.google.pt". Um intent pode ter várias formas, mas este é uma das mais usadas. Contém dois parâmetros, o primeiro é a ação a executar (pode ser VIEW, PICK, EDIT, DELETE, etc) e o segundo parâmetro é um argumento para a ação. Neste caso estamos a dizer ao sistema que queremos ver (VIEW) o URL indicado. O sistema analisa o intent e escolhe a aplicação certa para executar a ação pedida. Pode haver mais que uma aplicação que possa executar essa ação, então o sistema pergunta ao utilizador qual a aplicação que pretende usar. Neste caso se houvesse dois browsers presentes no dispositivo o sistema iria perguntar que browser se pretendia usar.

O segundo botão lança a calculadora, usando também um intent.


 public void runApp2(View v) {
    Intent i = new Intent();
    i.setClassName("com.android.calculator2", "com.android.calculator2.Calculator");
    startActivity(i);
 }

Este intent é diferente. Começamos por criar um intent vazio e adicionamos um nome de classe. Este nome de classe é definido a partir do packagename da atividade. Por isso é importante que cada atividade tenha um nome único, porque esse nome vai ser utilizado para lançar a atividade e tem portanto de ser único.

O terceiro botão apenas mostra uma mensagem de notificação, que no Android é chamado um Toast.

public void runApp3(View v) {
    String msg = "Hello World, again!";
    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
 
O quarto botão liga a vibração durante 1 segundo (1000 milisegundos). Usa o serviço de sistema VIBRATOR_SERVICE.

public void runApp4(View v) {
    Vibrator vib;
     vib = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
     vib.vibrate(1000);
}
 
O quinto botão obtém informação de localização do GPS ou da rede. Primeiro tenta obter a informação do GPS (GPS_PROVIDER). Se a informção não estiver disponível (GPS desligado ou sem sinal) pede informação à rede (NETWORK_PROVIDER). A informação obtida é mostrada num Toast.

public void runApp5(View v) {
    LocationManager locator = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    Location l = locator.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    if (l == null) {
        // Fall back to coarse location.
        l = locator.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
    }
    double alt = l.getAltitude();
    double lat = l.getLatitude();
    double lon = l.getLongitude();
    long time = l.getTime();
    float acc = l.getAccuracy();
    float speed = l.getSpeed();
    String prov = l.getProvider();
    String msg =
       "Lat: " + lat + "\n" +
       "Long: " + lon + "\n" +
       "Alt: " + alt + "\n" +
       "Time: " + time + "\n" +
       "Accuracy: " + acc + "\n" +
       "Speed: " + speed + "\n" +
       "Provider: " + prov + "\n";
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}

O sexto e último botão obtém e mostra a informação sobre a ligação wifi atual. Usa o serviço de wifi do sistema (WIFI_SERVICE). A informação obtida também é mostrada num Toast.

public void runApp6(View v) {
    String msg;
    WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
  if (wifi.isWifiEnabled()) {
  msg = "Wifi ligado\n";
  WifiInfo winfo = wifi.getConnectionInfo();
 
  int ipadd = winfo.getIpAddress();
  String ip = String.format("%d.%d.%d.%d",
ipadd & 0xff,
ipadd >> 8 & 0xff,
ipadd >> 16 & 0xff,
ipadd >> 24 & 0xff);
  msg +=
"BSSID: " + winfo.getBSSID() + "\n" +
"IP: " + ip + "\n" +
"Speed: " + winfo.getLinkSpeed() + "\n" +
"MAC: " + winfo.getMacAddress() + "\n" +
"RSSI: " + winfo.getRssi() + "\n" +
"SSID: " + winfo.getSSID();
} else
msg = "Wifi desligado";
  Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}


O projeto necessita de uma lista relativamente longa de imports, que devem ser gerados pelo Eclipse, mas para referência aqui ficam:


import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.View;
import android.widget.Toast;

Referências:

http://www.krvarma.com/2010/07/getting-ip-address-of-the-device-in-android/
http://www.damonkohler.com/2009/02/android-recipes.html
além do obrigatório: http://developer.android.com/reference/packages.html

segunda-feira, 23 de abril de 2012

Programar para Android - Índice



Programar para Android - 07


Um botão que muda um texto

Criamos um novo projeto Try05 da maneira habitual e apagamos a string do hello world, para começarmos com uma Activity já pronta, mas vazia.
Criamos uma string chamada tvText e com o conteúdo Número de clicks: e outra string com o nome btText e conteúdo Clica-me.
No layout (main.xml) adicionamos um botão e um TextView. Ao botão associamos o texto btText e à TextView associamos o texto tvText.
A ideia é que ao clicar no botão o texto vá indicando quantos clicks foram contados.

Vamos a Try05Activity e adicionamos a seguinte variável à atividade:

int numClicks = 0;

Esta variável vai contar os clicks e obviamente começa com 0 clicks.
De seguida criamos o seguinte método:


    private void updateText() {
    String s = getString(R.string.tvText) +  " " + numClicks;
    TextView tv1 = (TextView) findViewById(R.id.textView1);
    tv1.setText(s);    
    }

Este método atualiza o texto que pareca na TextView. Começa por definir uma string construída a partir do conteúdo da string tvText com um espaço e mais  número de clicks registados. Para podermos aceder ao texto na TextView textView1 (este é o nome dado à TextView criada no main.xml) precisamos de um objeto a que chamamos tv1 e fazemos a ligação ao texto já existente usando o método findViewById(). A última linha atualiza o texto.

Precisamos agora de um método que aumente o número de clicks cada vez que se clica no botão. Este método é muito simples de implementar. Vamos chamar-lhe processClick().


    public void processClick(View v) {
    numClicks++;
    updateText();
    }

Vamos fazer uma alteração ao método onCreate():


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        updateText();
    }

Apenas foi adicionada a linha a negrito. Esta linha apenas corrige uma questão estética, faz aparecer o 0 (zero) quando o botão ainda não foi clicado nenhuma vez.

Se executarmos agora o código, não é apresentada nenhum erro, mas o programa não faz nada. Falta indicar que vai ser o método processClick() a processar o clicks no botão. Antes fizemos isso com o método setOnClickListener() do botão e tivemos de dizer que a atividade implementava a interface OnClickListener.
Desta vez vamos apenas ao ficheiro main.xml (diretamente ao XML) e acrescentamos esta linha no botão:

        android:onClick="processClick"

Esta linha indica que o método processClick() vai ser o responsável pelo processamento dos clicks neste botão. É claro que o nome do método pode ser outro qualquer, mas o método tem de ser public, não retornar valores (ou seja, ser void) e aceitar um parâmetro do tipo View.

Podemos assim no Android usar o XML para definir o layout da atividade e também para definir o comportamento de alguns componentes ao reagir a determinados eventos (como neste caso o click num botão). Assim na parte de código só fazemos mesmo código. Não é preciso fazer meio metro de código só para criar textos e botões e definir o seu comportamento. O Android permite separar o layout e comportamento para o XML e liberta o ficheiro de código para conter código "útil".


domingo, 22 de abril de 2012

Programar para Android - 06

Try04 - Um botão com ação.

Vamos começar por criar um novo projeto Try04, em que retiramos a mensagem de "Hello World".
Neste projeto vamos criar um botão com o id btButton que ocupe toda a área disponível do ecrã, ou seja, com FILL_PARENT na altura e na largura.
Tal como antes, temos o layout concluído, mas o programa não faz nada.
A ideia é que o botão apresente uma mensagem, quando for clicado mude para uma segunda mensagem e ao ser clicado de novo volte à primeira mensagem.
Vamos então criar duas strings em strings.xml, uma com o id btText e valor Clica-me e outra com id btTextClicked e valor Fui clicado.
Agora vamos ao código.


package pt.omeusite.try04;


import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

Os imports são feitos automaticamente pelo Eclipse, com o Ctrl+Shift+O.

public class Try04Activity extends Activity implements OnClickListener {

A atividade vai implementar a interface OnClickListener, para poder processar os clicks no botão. É possível definir uma nova classe ou uma subclasse para processar os clicks, mas é mais simples fazer que a atividade implemente o interface, pois apenas implica que tenha um método onClick().
Vamos precisar de um objeto do tipo Button e de uma variável inteira, para guardar o estado do botão.

Button btButton;
int status = 0;

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

Começamos o código "a sério" por definir o objeto btButton. O objeto já foi definido no layout em main.xml, por isso agora apenas dizemos para ir buscar o objeto com o id btButton aos recursos.
De seguida definimos a própria atividade (this) como listener dos clicks.

        btButton = (Button) findViewById(R.id.btButton);
        btButton.setOnClickListener(this);
    }

O método que processa os clicks é este. Apenas muda o estado e define o texto do botão de acordo com esse estado.

    public void onClick(View v) {
    status = 1 - status;
    if (status==1)
    btButton.setText(R.string.btTextClicked);
    else
    btButton.setText(R.string.btText);
    }
}

E finalmente um programa que reage a alguma coisa.

Programar para Android - 05


Try03 - Layout com código Java.

Vamos agora tentar o mesmo layout, mas usando apenas Java.
Vamos criar o projeto Try03, e abrimos o Try03Activity.java, main.xml e strings.xml.
Primeiro vamos analisar o conteúdo.


package pt.omeusite.try03;


import android.app.Activity;
import android.os.Bundle;


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

A identificação do package e os imports são o normal do Java.
Apenas está definida a classe Try03Activity que extends a classe Activity. Dentro da nossa classe apenas temos um método onCreate(), que faz o override do onCreate() da classe Activity. O método onCreate() é chamado cada vez que uma atividade é criada, por isso é o local ideal para colocar o código que constrói a atividade (é como se fosse o construtor do objeto). A primeira ação é chamar o onCreate() da classe pai, passando-lhe o Bundle savedInstanceState. Deixemos a explicação do que é este Bundle para mais tarde. Fiquemos só com a noção que precisa estar lá.

A única outra instrução é um enigmático setContentView(R.layout.main), O setContentView() define qual é a view desta atividade (uma View, recordemos é um widget ou um layout), ou seja define o que vai aparecer no écrã. Neste caso passamos-lhe o argumento R.layout.main. O objeto R refere-se à diretoria res, o layout.main é uma referência ao ficheiro main.xml na diretoria layout. Ou seja, vai ler o nosso velho amigo main.xml! Por isso é que quando editamos o main.xml as alterações aparecem no écrã!
Percebido isto, vamos começar a apagar coisas:

  • no Try03Activity.java apagamos exatamente a linha setContentView(R.layout.main);
  • no main.xml apagamos a TextView;
  • no strings.xml apagamos a string hello.
Ficamos com uma aplicação que nada faz (semelhante ao que fizemos em Try00, mas agora há mais algum código no ficheiro .java).
A partir de agora não mexemos mais nos ficheiros XML.
O ficheiro .java fica assim:

package pt.omeusite.try03;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public class Try03Activity extends Activity {
    // estas views vão ser necessárias
    LinearLayout vLayout, hLayout;
    TextView tvTitle, tvName;
    EditText etEdit;
    Button btOk;

/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        /* estes layouts cumprem a mesma função do:
         * android:layout_width="..."
         * android:layout_height="..."
         */
        LinearLayout.LayoutParams lpFF = new LinearLayout.LayoutParams(
                LayoutParams.FILL_PARENT,
                LayoutParams.FILL_PARENT);
        LinearLayout.LayoutParams lpFW = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        // esta layout acrescenta o valor 1.0f, que é o peso relativo da view
        LinearLayout.LayoutParams lpWW = new LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
        
        // criamos o layout vertical que será o topo da view
        vLayout = new LinearLayout(this);
        // definimos orientação como vertical
        vLayout.setOrientation(LinearLayout.VERTICAL);
        // definimos os parametros de layout como FILL_PARENT, FILL_PARENT
        vLayout.setLayoutParams(lpFF);

        // criamos uma nova TextView
        tvTitle = new TextView(this);
        // definimos o texto
        tvTitle.setText("Try03Activity");
        // definimos os parâmetros de layout
        tvTitle.setLayoutParams(lpFW);
        // adicionamos esta View ao layout vertical
        vLayout.addView(tvTitle);

        // criamos um novo layout
        hLayout = new LinearLayout(this);
        // definimos layout horizontal
        hLayout.setOrientation(LinearLayout.HORIZONTAL);
        // definimos os parâmetros
        hLayout.setLayoutParams(lpFW);
        
        // criamos uma nova TextView
        tvName = new TextView(this);
        // definimos o texto
        tvName.setText("Nome");
        // definimos os parâmetros
        tvName.setLayoutParams(lpWW);
        // adicionamos a View ao layout horizontal
        hLayout.addView(tvName);
       
        // criamos uma nos caixa de texto
        etEdit = new EditText(this);
        // definimos tamanho como 10 ems
        etEdit.setEms(10);
        // definimos layout
        etEdit.setLayoutParams(lpWW);
        // adicionamos ao layout horizontal
        hLayout.addView(etEdit);
       
        // adicionamos o layout horizontal ao vertical
        vLayout.addView(hLayout);
        
        // criamos um botão
        btOk = new Button(this);
        // definimos o texto
        btOk.setText("Ok");
        // definimos os parâmetros
        btOk.setLayoutParams(lpFW);
        // adicionamos o botão ao layout vertical
        vLayout.addView(btOk);
       
        // definimos a vista ativa como sendo o layout vertical que acabámos de definir
        // e o layout é mostrado no écrã
        setContentView(vLayout);
    }
}

Acho que não há melhor motivação para usar o editor gráfico ou o XML.
O programa continua a não fazer absolutamente nada, mas foi preciso escrever uma quantidade considerável de código para isso. Mesmo com as ajudas do Eclipse (Ctrl+Shift+O para adicionar os imports automaticamente e Ctrl+Space para ajudar na conclusão das palavras) não é um exercício trivial.
Por isso normalmente as interfaces gráficas não são criadas usando código. Há alturas em que é inevitável, como por exemplo preencher uma lista pendente (Spinner, na nomenclatura Android) com dados retirados de uma base de dados.

Apesar de funcionar, o programa tem um erro conceptual: as strings estão hardcoded na atividade, o que já tínhamos dito que não se deve fazer. Para usar os id das strings é preciso primeiro criá-las no ficheiro strings.xml (ou noutro ficheiro XML, não é obrigatório ser no strings.xml).
Afinal sempre temos de editar o XML. Criamos então as seguintes strings no strings.xml, usando o editor ou diretamente no XML:

<string name="tvTitle">Try02Activity</string>
<string name="tvName">Nome</string>
<string name="btOk">Ok</string>

Notar que para identificador de cada string foi usado o mesmo nome que para o objeto no código Java. Isto NÃO é obrigatório, mas ajuda a manter a sanidade do programador.

Agora precisamos alterar as linhas do código que se referem às strings:

(...)
tvTitle.setText(getString(R.String.tvTitle));
(...)
tvName.setText(getString(R.string.tvName));
(...)
btOk.setText(getString(R.string.btOk));
(...)

E pronto, um programa que não faz nada, mas arrumadinho.

Programar para Android - 04


Try02 - Layout com XML.

Vamos criar um layout exatamente idêntico ao criado no Try01, mas usando apenas os ficheiros XML diretamente.

Começamos por criar um projeto Android chamado Try02.
Por uma questão de criação de bons hábitos vamos abrir os ficheiros Try02Activity.java, res/layout/main.xml e res/values/strings.xml, mas na verdade nem sequer vamos mexer no .java.
Mudamos o main.xml e strings.xml para a vista de XML.

Ao contrário do modo de edição gráfico, em que vamos fazendo as coisas à medida que nos lembramos, do tipo "ah, preciso aqui duma string, click direito e adiciono-a", o modo XML exige que se pense um pouco antes de começar a editar.
Neste caso já sabemos bem o que pretendemos, por isso podemos ir diretamente ao ficheiro strings.xml e adicionar as strings que precisamos, que são:

  • uma string tvName com o valor "Nome", para aparecer como legenda de uma caixa de texto;
  • uma string btOk com o valor "Ok", para aparecer no botão.
A estrutura do strings.xml é bastante simples de perceber e para adicionar as duas strings basta acrescentar duas linhas dentro do resources ... /resources. Fica assim (as linhas adicionadas estão a negrito):

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

    <string name="hello">Try02Activity</string>
    <string name="app_name">Try02</string>
    <string name="tvName">Nome</string>
    <string name="btOk">Ok</string>

</resources>

Também mudamo a mensagem de "Hello World" para aparecer apenas o nome da atividade.

O main.xml já dá mais um pouco de trabalho, mas nada de muito difícil.
Temos de pensar primeiro na estrutura que queremos criar. No topo tem de existir sempre um layout, que no nosso caso é um LinearLayout vertical. Dentro dele vamos colocando os outros elementos e outros layouts. Assim:
  • LinearLayout vertical
    • TextView "Try02Activity"
    • LinearLayout horizontal
      • TextView "Nome"
      • TextField editText1
    • Botão "Ok"
O main.xml criado pelo Eclipse já tem o layout vertical e a primeira TextView, só temos de acrescentar o resto, usando o que já lá está como guia (as linhas acrescentadas estão a negrito):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="@string/tvName" />

        <EditText
            android:id="@+id/editText1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10" />

    </LinearLayout>

    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btOk" />

</LinearLayout>

Algumas coisas são óbvias, outras nem por isso.

Um elemento <LinearLayout> precisa sempre de um </LinearLayout> a fechar, porque um layout pode conter um número indeterminado de elementos, mas um elemento TextView, EditText, Button, etc não precisa da tag a fechar.

Todos os elementos (layouts ou não) precisam de pelo menos as propriedades android:layout_width e android:layout_height definidas. O valor fill_parent indica que o elemento deve estender-se para encher a área disponível no elemento "pai". O valor wrap_content indica que o elemento deve ocupar apenas o espaço necessário.

Os elementos gráficos precisam de um android:id. Observamos que todos os id aqui presentes começam com um @ e que as views (é como são conhecidos os widgets no Android) têm um @+, enquanto que as strings têm um @ sozinho.
O @ indica que não queremos que o que está escrito dentro das "" seja escrito literalmente, mas que seja procurada a referência nos recursos (ou seja algures em res/). No caso das strings o @string/btOk significa "procura nas strings uma com o id btOk". No caso das views o @+id/button1 significa "cria um identificador para esta view chamado button1". Ou seja o + é usado para criar um novo id. Se houver mais tarde uma nova referência a este botão bastaria usar @id/button1.
Também é possível referenciar recursos disponibilizados pelo próprio Android com @android:id/qq_coisa, mas lá iremos.

Se os elementos tiverem algum conteúdo de texto este é definido com android:text. Podemos fazer uma pequena experiência que é acrescentar a linha android:text="Escreva o seu nome" ao EditText. Se corrermos agora a aplicação aparece o texto "Escreva o seu nome" na caixa de edição. Já sabemos que não é boa ideia ter strings hardcoded na aplicação, por isso se quisermos realmente que apareça uma mensagem devemos criar uma string no strings.xml e colocar aqui o seu id.

A propriedade android:ems="10" do EditText parece um pouco mais obscura. Define a largura mínima do campo, que neste caso é de 10 ems. E o que é um "ems"? É a largura da letra mais larga, que é a letra "M", daí o nome. Ou seja, é o número de M's que cabem no campo.

E cá está, mais um programa que não faz nada, sem escrever uma única linha de código.

Programar para Android - 03


Try01 - Layout com o editor gráfico.

Vamos fazer mais uns TryXX até conseguirmos fazer um programa que realize algum trabalho útil (se bem que não muito) lá pelo Try07.

Android usa intensamente o interface gráfico, por isso alguma forma haverá de definir o interface gráfico de uma atividade. Na verdade existem 3 formas: usando métodos Java, usando ficheiros XML e usando o editor de interface gráfica incluído no Eclipse. É claro que o editor do Eclipse é o mais fácil de usar, mas por vezes temos de usar um dos outros métodos. Se, por exemplo, a informação que queremos mostrar é obtida em runtime temos de usar o Java para definir os elementos gráficos.

Neste try vamos começar por usar o editor do Eclipse. Criamos um novo projeto Try01. O Eclipse cria o Hello World do costume. Tipicamente há 3 ficheiros que vamos usar muito: o .java da atividade, o res/value/strings.xml e o res/layout/main.xml, por isso podemos começar por abrir os três.
Já usámos estes três ficheiros no Try00, mas aí limitamo-nos a apagar coisas.

No editor visual do main.xml, vemos uma grande quantidade de informação, o que ao princípio pode ser intimidante.
No lado direito temos uma janela "Outline", que apresenta todos os elementos gráficos, organizados em árvore. Cada elemento gráfico fica dentro de um "layout". O pré-definido é o layout linear vertical (LinearLayout, que é vertical vê-se porque o ícone ao lado apresenta umas caixas empilhadas verticalmente). Há vários (neste momento 8) tipos de layout, mas quase sempre se usam apenas o LinearLayout vertical e horizontal. É possível (e muito comum) colocar um layout dentro de outro layout.
Na janela do main.xml propriamente dito temos dois separadores na base (estilo separadores do Excel) onde podemos ver o Graphical Layout ou XML do ficheiro. Neste try vamos ficar pelo graphical layout, mas podemos sempre dar uma olhadela ao XML, para ver como fica.
Este try não faz nenhum trabalho útil, mas a ideia é criar um layout várias linhas, em que há:

  • um texto na primeira linha (já lá está, é o hello world);
  • um texto e uma caixa de entrada na segunda linha (lado a lado);
  • um botão na terceira linha (o botão é suposto ocupar toda a largura do écrã).


Bem, o texto do hello world já está no sítio, por isso nem lhe mexemos por enquanto.
Com o grupo "Form widgets" selecionado na palete, basta arrastar um TextView para a área do editor gráfico para criar a respetiva área de texto. Um TextView é o que noutros ambiente se chamaria uma Label (ou JLabel). É um texto estático, não interativo. A palette apresenta 4 TextView, a de tamanho normal (TextView), grande (Large), um pouco maior (Medium) e mais pequena (Small). Para este try escolhemos o que nos apetecer no momento. É claro que depois de introduzir a TextView lhe podemos mudar o tamanho (e mais uma tonelada de outras propriedades), mas vamos KISS e usar só o que está na palette.

Agora adicionamos o campo de entrada de texto. Esses campos estão no grupos Text Fields da palette. Há muito tipos, para texto, números, datas, passwords, etc. Escolhemos logo o primeiro e arrastamos para o editor gráfico. O campo de texto fica por baixo (ou por cima, depende de onde o largarmos) do TextView, mas nós queremos que fique ao lado. Não vale a penas arrastar mais, estamos a colocar os elementos dentro de um layout vertical, por isso ficam sempre na vertical. Temos de adicionar um layout horizontal. Estão no grupo Layouts.
Ao arrastar um LinearLayout horizontal, cria-se uma zona retangular vazia. Arrastamos agora o TextView e o Text Field para dentro deste retângulo. Por arrastamento é possível controlar qual fica à esquerda e à direita (prestar atenção às linhas amarelas que aparecem). O aspeto não fica muito bom (o texto deve ficar muito apertado), mas já vamos tratar disso a seguir.

Pro fim arrastamos um botão (está nos Form Widgets, há botões normais e pequenos) para baixo do LinearLayout horizontal. Mas o botão não ocupa a largura toda do écrã. Logo acima da pré-visualização do aspeto gráfico da atividade há uma série de botões. Com o botão selecionado vemos os seguintes:
Os dois primeiros controlam o tipo de layout (vertical ou horizontal) em que o elemento atual (neste caso o botão) está inserido. O terceiro e quarto controlam se o botão ocupa a largura/altura completa do écrã. Clicamos no terceiro. Os restantes botões controlam outras propriedades mais comuns do botão. Mais uma vez, estas propriedades podem ser alteradas na lista de propriedades do botão, esta barra de ferramentas é apenas um atalho para as propriedades mais comuns.

O aspeto da segunda linha (a que tem a TextView e o Text Field) também se pode melhorar com este botão. Provavelmente o melhor é desligar esta opção nos dois widgets.

Agora vamos tratar das strings.
Vamos supor que o campo de texto é para escrever o nosso nome e que, portanto, a TextView à esquerda deve ter a string "Nome".
Ao clicar nesta TextView vemos no Outline que é selecionado o "textView1". É o nome do objeto associada ao widget. Fazendo click direto sobre a TextView (ou sobre o seu identificador no Outline) aparece um menu de contexto. Precisamos da primeira opção, Edit Text...
Aparece uma caixa de diálogo com as strings definidas para a nossa aplicação. Por enquanto devem lá estar duas. Vamos criar uma terceira em New String... Em String colocamos o que queremos que apareça no écrã, neste caso "Nome" (sem aspas). Em New R.string colocamos o identificador que vai identificar este texto, por exemplo tvName (o tv é de TextView, mas é claro que se pode escolher outra coisa). Há mais uma série de propriedades que se podem editar, mas não lhes vamos mexer.
Depois de clicar em Ok, se formos espreitar ao ficheiro res/values/strings.xml, já lá está a nossa string.
Vamos agora editar o texto do botão. Click direito e Edit Text... Vamos criar uma nova string com o texto Ok e o id btOk.

Se queremos mudar o texto do "Hello World...", temos de ir ao strings.xml e alterar lá o valor da string.
Podemos agora correr o projeto.

Não faz absolutamente nada, mas está mais bonito.

sábado, 21 de abril de 2012

Programar para Android - 02


Um programa ainda mais simples.

Vamos fazer um programa para Android ainda mais simples que o "Hello World". O programa mais simples possível, nem sequer uma mensagem apresenta, limita-se somente a existir.
Criamos um novo projeto, chamemos-lhe Try00.
O Eclipse faz o favor de preparar tudo e de nos apresentar já feito um programa com uma atividade que mostra a mensagem "Hello World, Try00Activity!", mas queremos ainda menos. Abrimos o ficheiro de código e apagamos a parte relativa ao método onCreate, ficando apenas:


package pt.omeusite.try00;


import android.app.Activity;


public class Try0Activity extends Activity {
}

Também se retirou uma linha de import, que passou a deixar de ser necessária.

E cá está, o mais simples programa para Android, que não faz absolutamente nada!
Mas a mensagem "Hello World, Try00Activity!" que era apresentada antes tem de estar armazenada em algum lado. No código que apagamos não estava lá essa string, por isso onde andará ela?

Uma string é um recurso e todos os recursos estão na diretoria res. Dentro dela há várias subdiretorias, incluindo uma values e se abrirmos esta está lá uma ficheiro strings.xml. Ao abrir este strings.xml o Eclipse apresenta uma interface bonitinha que nos permite adicionar, remover, editar e ordenar vários elementos (strings, cores, etc). Podemos também ver o ficheiro em toda a sua glória XML clicando no separador na base da janela, mas a vista de recursos é mais civilizada. De momento devem aparecer duas strings uma hello e outra app_name. A app_name contém o nome da aplicação (Try0) mas podemos mudar para o que quisermos. A hello tem a nossa mensagem de hello world. Como apagamos o código vamos também apagar a string.
Se tentarmos correr agora o projeto, vai dar erro. Há um erro no ficheiro res/layout/main.xml.
Temos de ir lá editar este ficheiro. Este é outro XML muito importante e que controla o aspeto gráfico da atividade. Podemos ver e editar o XML diretamente, mas a interface gráfica apresentada pelo Eclipse é mais simples. O que acontece é que este main.xml tem uma referência à string hello que apagamos. A referência tem o aspeto @string/hello, que significa basicamente "vai aos recursos e procura no ficheiro das strings por uma com o nome hello". Temos de apagar esta referência (por curiosidade podemos dar uma olhadela ao main.xml antes e depois de apagar o @string/hello, para ver as diferenças).
Agora sim temos uma Activity que apenas mostra o nome da atividade, sem nada lá dentro.
Para mudar o nome da atividade basta mudar a string app_name em res/values/strings.xml.
Sempre que usarmos uma string numa aplicação podemos escrevê-la diretamente no meio do nosso código, mas é melhor colocar todas as strings no ficheiro strings.xml (dando um ID único a cada uma, claro) e depois usar referências a essas strings no nosso código. Facilita em muito o processo de tradução da aplicação e faz uma separação entre dados (strings) e código, o que é sempre positivo.

sexta-feira, 20 de abril de 2012

Programar para Android - 01


Hello Wordl!

O clássico "Hello World" é muito simples de fazer em Android, é mais difícil é de explicar.
O Android está pensado para ser usado com uma interface gráfica, por isso apesar de ser possível escrever para a consola, é muito mais fácil usar os widgets gráficos.
Para criar um novo projeto: File » New » Project... e escolher Android Project.

  • Next, dar um nome ao projeto, por exemplo Hello World
  • Next, escolher o alvo (target), ou seja a versão de Android
  • Next, a maior parte dos campos já tem preenchidos os valores pré-definidos que são normalmente "seguros", mas o Eclipse insiste em que indiquemos manualmente o packagename. O packagename é um nome constituído apenas por caracteres alfanuméricos e . (ponto final) que identifica o nosso projeto. Este identificador deve ser único, ou seja, não pode (deve) haver em todo o mundo nenhum outro projeto Android com o mesmo nome. Tipicamente usa-se o nome de domínio (admitindo que o programador tem um domínio) invertido adicionando o nome do projeto no fim. Ou seja, se o programador possuir o domínio "omeusite.pt" deverá colocar no packagename pt.omeusite.helloworld. Este nome irá identificar a aplicação se e quando for colocada na Android store, por isso a insistência em que seja um nome único.
  • Ao clicar em Finish é criado o projeto.

E já está pronto o programa Hello World. Para correr basta clicar no botão para correr a aplicação (ou Ctrl+F11). O AVD que tiver sido escolhido é lançado e a aplicação é automaticamente instalada e executada.

Olhando para o projeto no Eclipse vemos que foram criadas uma série de pastas e ficheiros. Não houve necessidade de editar manualmente nenhum desses ficheiros para criar o Hello World e grande parte deles nem devem sequer nunca ser tocados por mãos humanas.
Nas diretorias existem a:

  • src: Aqui estão as fontes do nosso projeto, as classes e packages que criarmos vão todas para aqui. 
  • Android*: Pode haver várias diretorias destas, dependendo do target escolhido, são as libraries da API.
  • gen: Ficheiros gerados pelo compilador, aqui aparecem ficheiros .java gerados automaticamente, ou seja NÃO MEXER.
  • bin: Ficheiros binários gerados pelo compilador, é aqui que aparecem os ficheiros .dex e .apk
  • res: Recursos, podem ser strings, imagens, documentos, whatever e muitos ficheiros XML. O compilador empacota todos os recursos nesta diretoria para serem facilmente acedidos de dentro da aplicação (com um objeto chamado R).
  • assets: São também recursos, mas não são "empacotados" pelo compilador, pelo que o acesso é mais trabalhoso. São usados quando é necessário usar dados em estado raw, ou seja, não pré-preparados pelo compilador. Na prática é pouco usado.
O Android NÃO corre código Java. Android usa um subconjunto da API Java (com algumas adições próprias). As aplicações Java do Android são compiladas normalmente para ficheiros .class (com o compilador Java) e estes ficheiros são de seguida novamente compilados para uma máquina Dalvik. A Dalvik é uma máquina virtual (semelhante ao Java, mas não é a mesma coisa) que está otimizada para dispositivos lentos e com recursos limitados. Os ficheiros .class são convertidos então em ficheiros .dex (Dalvik EXecutables) e empacotados juntamente com todos os recursos (imagens, sons, texto, etc) em ficheiros .apk (Android PacKage). Este ficheiro .apk é então enviado para o dispositivo (real ou virtual) que se encarrega de o descomprimir e colocar todos os ficheiros nos sítios certos, ficando a aplicação disponível para ser executada.
De uma forma simplista podemos pensar numa aplicação Android como:
.java -» .class -» .dex -» .apk

Observando o código da activity aparece algo como:

package pt.omeusite.helloworld;

import android.app.Activity;
import android.os.Bundle;

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

A string "Hello World!" não aparece em lado nenhum! Como raios é que vai ter ao écrã do dispositivo? E de onde é que saiu aquele R?
Respostas no próximo capítulo.

Programar para Android - 00


Instalar o ambiente.

A melhor forma de programa para Android é usar um ambiente com Eclipse+JDK+Android SDK+ADT. É possível criá-lo tanto em Windows como em Linux ou Mac.

1. Fazer o download do eclipse


O Eclipse (http://www.eclipse.org/) é um ambiente de desenvolvimento para Java/C/C++/PHP/Whatever. Tem umas mil versões diferentes, a que interessa para este caso é a Classic. As outras versões também servem, mas iriam trazer módulos desnecessários (http://www.eclipse.org/downloads/).
Depois de fazer o download da versão certa para o sistema operativo certo, basta descomprimir o ficheiro. Não é necessário fazer a instalação, o Eclipse corre a partir da diretoria onde for descomprimido. Provavelmente será conveniente criar um atalho no Desktop para iniciar o Eclipse.

2. Instalar o JDK


O Java Development Kit (JDK) pode ser obtido em http://www.oracle.com/technetwork/java/javase/downloads/index.html. Fazer o download da versão pretendida para o sistema operativo. O Java Runtime Environmet (JRE) não é suficiente, é mesmo necessário o JDK.
No Ubuntu basta fazer algo tipo sudo apt-get install sun-java6-jdk, ou algo semelhante.

3. Obter o Android SDK


O Android Software Development Kit pode ser obtido em http://developer.android.com/sdk/index.html.
A versão Windows tem um instalador, mas basta descomprimir o ficheiro com o SDK. Lembrar onde estão os ficheiros descomprimidos, vão ser precisos mais tarde.

4. Obter o ADT


O Android Development Tools (ADT) é um plugin para Eclipse que permite utilizar o SDK de forma mais fácil. Não é obrigatória a sua instalação, mas é altamente recomendado.
A instalação faz-se no próprio Eclipse, em Help » Install new software...
Clicar em Add... e introduzir o endereço:
https://dl-ssl.google.com/android/eclipse/
Após alguns segundos aparece uma lista de pacotes. Selecionar todos os pacotes (basta selecionar o Developer Tools e são todos automaticamente selecionados), clicar em Next e novamente em Next. Aceitar as licenças e Finish. O Eclipse vai fazer o download e instalar os componentes. Pode aparecer um aviso sobre conteúdo não assinado. No final é preciso reiniciar o Eclipse. Ao reiniciar o Eclipse vai pedir a localização do SDK que descomprimimos no ponto anterior. Após isso o Eclipse faz mais alguns downloads. É preciso aceitar mais algumas licenças de utilização e fazer mais alguns downloads.

5. Configurando


Para instalar outras versões da API do Android, documentação, exemplos, etc, ir a Window » Android SDK Manager.
Aconselha-se a instalação de pelo menos o SDK da versão 2.1, pois a maior parte dos dispositivos no mercado são compatíveis com essa versão.
Para correr um programa feito no Eclipse numa máquina virtual Android é preciso primeiro criar uma máquina Android com o Android Virtual Device Manager (AVD). Ao criar uma máquina virtual especificamos as suas características (ecrã, disco SD, GPS, ...). Para isso ir a Window » AVD Manager. O AVD Manager também permite lançar o emulador. O boot da máquina virtual demora bastante tempo, por isso convém não a desligar (shutdown) depois de arrancar.

6. Fim


E pronto, só falta mesmo aprender a programar para Android.