sexta-feira, 30 de novembro de 2012

Um bicionário = dicionário com duas línguas

Para este post pensei criar um "bicionário" para Android, isto é, um programa que permite verificar se uma palavra existe em duas línguas, neste caso Português e Inglês.




Depois de pesquisar na Net consegui dois ficheiros de texto com uma lista de palavras, um em português outro em inglês.

Então começamos com a interface, muito simples, duas textviews, uma edittext, dois radio buttons dentro de um radioGroup, por fim um botão.

O código XML é


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="14dp"
        android:text="@string/dicionario"
        tools:context=".MainActivity" />

    <RadioGroup
        android:id="@+id/radioDic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/textView1" >
        
    <RadioButton
        android:id="@+id/radioButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="true"
        android:text="@string/pt" />
        
    <RadioButton
        android:id="@+id/radioButton2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:text="@string/ing" />
    
 </RadioGroup>

   <EditText
       android:id="@+id/editText1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_below="@+id/radioDic"
       android:ems="10"
       android:inputType="text" >
    
        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/button1"
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/editText1"
        android:layout_alignBottom="@+id/editText1"
        android:layout_alignParentRight="true"
        android:onClick="bt_verificar"
        android:text="@string/botao" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

Para que o código possa ler os ficheiros temos de os colocar nas pasta res\raw. Os ficheiros colocados nesta pasta são mantidos inalterados dentro da estrutura do projeto.

Quando o botão Verificar é clicado temos de ler o ficheiro correspondente ao dicionário escolhido e procurar a palavra inserida.

Para tal criamos uma função que recebe a palavra e a opção pelo dicionário e devolve true ou false em função como resultado da pesquisa.

private boolean encontra(String palavra,int dic){
    boolean resultado;
    InputStream is;
   
    if(dic==0)
      //carrega dicionario PT
    is = getResources().openRawResource(R.raw.portugues);
    else
    is = getResources().openRawResource(R.raw.ingles);
   
    Scanner pesquisa=new Scanner(is);
    Log.d(TAG,"Português!");
    resultado=false;
    while(pesquisa.hasNext()){
String linha=pesquisa.nextLine();
linha=linha.toLowerCase();
if(linha.equals(palavra)){
resultado=true;
break;
}
    }//while
    pesquisa.close();
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    return resultado;
    }

O problema começa quando tentamos pesquisar uma palavra e o sistema operativo indica que o programa não está a responder, isto devesse ao tempo que demora a percorrer o ficheiro, assim foi necessário criar uma thread para que esta função seja executada em background enquanto mantemos na interface uma animação que indica que a pesquisa está a decorrer.

Esta é a função que é chamada quando o botão Verificar é clicado:
    //botão para verificar palavra
    public void bt_verificar(View view){
    Runnable runnable=new Runnable(){
    public void run(){
EditText texto=(EditText)findViewById(R.id.editText1); //texto introduzido
final Button bt =(Button)findViewById(R.id.button1); //botão verificar
final TextView mensagem=(TextView)findViewById(R.id.textView2); //textview para mostrar mensagem
int l=0;
if(texto.getText().length()==0){
showMessage("Introduza uma palavra a pesquisar!");
return;
}//if
String palavra=texto.getText().toString();
palavra=palavra.toLowerCase();
Log.d(TAG,"A pesquisar " + palavra);
RadioButton rbDicionario=(RadioButton)findViewById(R.id.radioButton1);  //botão radio
if(rbDicionario.isChecked())
l=0; //português
else
l=1; //inglês
handler.post(new Runnable() {
public void run() {
bt.setEnabled(false);
setProgressBarIndeterminateVisibility(true);
mensagem.setText("A Pesquisar...");
}
 });
if(encontra(palavra,l)){
handler.post(new Runnable() {
 //  @Override
public void run() {
bt.setEnabled(true);
setProgressBarIndeterminateVisibility(false);      
mensagem.setText("Palavra encontrada!");
showMessage("Palavra encontrada!");
}
 });
}else{
handler.post(new Runnable() {
 //  @Override
public void run() {
bt.setEnabled(true);
setProgressBarIndeterminateVisibility(false);           
mensagem.setText("Palavra não encontrada!");
showMessage("Palavra não encontrada!");
}
 });
}
    }
    };
    new Thread(runnable).start();
    }

Ao executar este código numa thread é necessário criar uma handler para podermos atualizar a interface do utilizador.

Na interface também foi adicionada por código uma progressBar que aparece e desaparece de acordo com o código que está a ser executado.

O código completo da aplicação fica assim:
package edu.pjcferreira.bicionario;

import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private Handler handler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        
        setContentView(R.layout.activity_main);

        setProgressBarIndeterminateVisibility(false);
        handler= new Handler();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    private void showMessage(CharSequence text){
    Context context=getApplicationContext();
    int duration=Toast.LENGTH_SHORT;
    Toast toast=Toast.makeText(context, text, duration);
    toast.show();
    }
    //botão para verificar palavra
    public void bt_verificar(View view){
    Runnable runnable=new Runnable(){
    public void run(){
EditText texto=(EditText)findViewById(R.id.editText1); //texto introduzido
final Button bt =(Button)findViewById(R.id.button1); //botão verificar
final TextView mensagem=(TextView)findViewById(R.id.textView2); //textview para mostrar mensagem
int l=0;
if(texto.getText().length()==0){
showMessage("Introduza uma palavra a pesquisar!");
return;
}//if
String palavra=texto.getText().toString();
palavra=palavra.toLowerCase();
Log.d(TAG,"A pesquisar " + palavra);
RadioButton rbDicionario=(RadioButton)findViewById(R.id.radioButton1);  //botão radio
if(rbDicionario.isChecked())
l=0; //português
else
l=1; //inglês
handler.post(new Runnable() {
public void run() {
bt.setEnabled(false);
setProgressBarIndeterminateVisibility(true);
mensagem.setText("A Pesquisar...");
}
 });
if(encontra(palavra,l)){
handler.post(new Runnable() {
 //  @Override
public void run() {
bt.setEnabled(true);
setProgressBarIndeterminateVisibility(false);      
mensagem.setText("Palavra encontrada!");
showMessage("Palavra encontrada!");
}
 });
}else{
handler.post(new Runnable() {
 //  @Override
public void run() {
bt.setEnabled(true);
setProgressBarIndeterminateVisibility(false);           
mensagem.setText("Palavra não encontrada!");
showMessage("Palavra não encontrada!");
}
 });
}
    }
    };
    new Thread(runnable).start();
    }

    private boolean encontra(String palavra,int dic){
    boolean resultado;
    InputStream is;
   
    if(dic==0)
      //carrega dicionario PT
    is = getResources().openRawResource(R.raw.portugues);
    else
    is = getResources().openRawResource(R.raw.ingles);
   
    Scanner pesquisa=new Scanner(is);
    Log.d(TAG,"Português!");
    resultado=false;
    while(pesquisa.hasNext()){
String linha=pesquisa.nextLine();
linha=linha.toLowerCase();
if(linha.equals(palavra)){
resultado=true;
break;
}
    }//while
    pesquisa.close();
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    return resultado;
    }
}

Fica aqui o projeto e a aplicação.

sexta-feira, 23 de novembro de 2012

A unidade em 3D - Unity 3D

Para hoje temos um motor de jogo muito popular, o Unity 3D. Chegou recentemente à versão 4.0 e está melhor do que nunca, ao contrário de outros programas este tem "envelhecido" muito bem.


A versão mais recente apresenta a possibilidade de exportar o nosso jogo para a plataforma Linux, nas versões anteriores só existia a possibilidade de criar jogos para Windows e Mac OS.




O principio deste motor é que o centro do desenvolvimento está nos elementos gráficos e não no código.

Para além disso existe a possibilidade de programar em várias linguagens das quais Javascript, C# e Boo.

O código fica sempre associado aos objetos e molda o comportamento destes reagindo a eventos como o desenho de frames, a colisão ou a criação de objetos.

O Unity dispõe de ferramentas para criar objetos apesar de que para criar gráficos mais elaborados convém utilizar outras ferramentas especializadas como o Blender e importar esses elementos para o nosso projeto.

Cada jogo no Unity pode ter vários níveis sendo representados por cenas que podem representar níveis jogáveis ou menus e cutscenes.

Para este projeto vamos utilizar a versão 3.5.

O jogo começa-se por criar um projeto com esta janela


Ao criar o projeto podemos optar por importar vários pacotes disponíveis com o Unity de origem, neste trabalho vamos criar um programa simples e por isso não importamos nada.

Depois de clicar no botão Create o Unity fecha e abre o novo projeto, cada projeto no Unity fica numa pasta com todos os elementos dentro dessa pasta.

Vamos começar por inserir um plano que será o chão do nosso jogo.

 O resultado desta ação é

Agora um pouco sobre a interface.

Do lado direito temos o Inspector que apresenta as propriedades do objeto selecionado, que neste caso é o plano inserido.

Mais à esquerda temos duas janelas, a de cima apresenta a hierarquia dos objetos da cena, a de baixo apresenta todos os elementos do projeto.

As duas janelas grandes apresentam o projeto em modo de edição e em modo de execução, a de cima é o espaço disponível para manipular os objetos durante a criação do jogo a de baixo representa o que a câmera vê, sim sempre que criamos um projeto novo por defeito é inserido sempre um objeto do tipo Camera que representa o que o jogador vai ver.

Por baixo do menu principal temos


O primeiro botão faz o scroll da cena, também podemos fazer o scroll premindo o botão do meio do rato. O botão da seta permite mover o objetos, tal como as setas que aparecem quando selecionamos um objeto. O terceiro botão permite rodar os objetos e o quarto permite redimensionar.

O botões mais à direita permitem executar e pausar a cena. Não podemos esquecer que as alterações feitas à cena durante a pausa do programa não são gravadas por isso qualquer alteração definitiva deve ser feita quando o programa está parado e não em pausa.

De seguida vamos inserir um Cube, ou melhor vários.
Um cubo vai servir para a base de uma alavanca, por isso temos de o rodar de forma a que fique em forma de cunha.
Assim selecionamos o botão que permite rodar os objetos, referido acima, e rodamos sobre o eixo pretendido.

Depois de o rodar vamos mudar a cor para que seja mais fácil distinguir os objetos e o jogo não fica tão monótono visualmente.

Para mudarmos a cor de um objeto temos de criar um material novo, para isso vamos à barra do projeto e escolhemos Create - Material
De seguida damos um nome ao material, eu vou escolher a cor, neste caso azul, depois, no Inspector mudamos a propriedade Main Color e podemos alterar o Shader para obter efeitos diferentes, eu vou utilizar o Shader Bumped Specular.

O nosso cubo fica assim



O próximo passo é criar a alavanca que vai projetar uma caixa para isso vamos utilizar dois cubes. Primeiro adicionamos um cubo que serve como base e depois um segundo que evita que o objeto a projetar escorregue para trás, este elemento vai ser construído utilizando a possibilidade do Unity em ter objetos hierarquicamente dependentes de outros fazendo com que estes se movam em simultâneo como se fossem só um.
Então aqui vai um cubo com nome alavanca, que foi redimensionado para ter este aspeto.


Agora criamos outro cubo que arrastamos, na barra da hierarquia para cima da alavanca o que faz com que este cubo dependa do anterior. Antes de arrastar devemos colocar o cubo no local pretendido.
O resultado final é este.


Agora quando movemos o cubo denominado alavanca o outro também se move.

Se testarmos o jogo agora nada acontece para que a alavanca tenha um comportamento realista basta adicionar um componente que implementa os cálculos de física nomeadamente a gravidade, as colisões e as forças.

Então com a alavanca selecionada vamos a Component - Physics - Rigidbody.


Se agora testarmos o jogo a alavanca já sofre os efeitos da gravidade.


Agora vamos adicionar mais dois cubos, um que fica na alavanca e que será projetado o outro que vai servir de peso para projetar o anterior.

O primeiro vai se chamar caixa e fica assim.


Não esquecer de adicionar um Rigidbody à caixa para ela ter um comportamento realista.

O segundo cubo fica assim, suspenso sobre a alavanca, neste não adicionamos o Rigidbody porque só pretendemos que ele caia quando o utilizador premir a barra de espaço. Este vai ter o nome peso.


Agora vamos passar ao código, a linguagem de programação a utilizar é Javascript, então na barra do projeto escolhemos Create - Javascript. O nome do ficheiro é controlo. Depois de criado o ficheiro fazemos duplo clique nele e abre o MonoDevelop que o Unity utiliza para compilar o código.

Aqui pretendemos que ao premir a tecla barra de espaço a caixa caia em cima da alavanca então o que vamos fazer é adicionar um Rigidbody à caixa e afinar o massa do objeto para que a distância da projeção seja maior ou menor de acordo como  alvo que pretendemos atingir, que para já ainda não adicionamos.

O código fica assim:

#pragma strict

function Start () {

}

function Update () {

//larga a caixa quando prime espaço
if(Input.GetButtonUp("Jump")){
this.gameObject.AddComponent(Rigidbody);
this.gameObject.rigidbody.mass=50;
}

}

A função Start não utilizamos, esta é chamada uma fez quando o objeto é criado.

A função Update, onde inserimos o código, é chamada por cada frame desenhada assim se o utilizador premiu a tecla e largou adicionamos o componente Rigidbody e aumentamos a massa (peso) para que a força de projeção seja maior. Não testamos a tecla espaço diretamente mas sim o botão Jump que por defeito está associado à barra de espaço, como podemos ver em Edit - Project Settings - Input.

Depois de gravarmos o ficheiro voltamos ao Unity, não é preciso fechar o MonoDevelop, e agora basta arrastar o ficheiro do código para o cube peso.

Ao testar o programa podemos ver que ao largar o peso a alavanca projeta a caixa.

Para tornar o jogo um pouco mais interessante vamos deixar o utilizador posicionar a caixa antes de a deixar cair, para isso voltamos ao MonoDevelop e adicionamos o código:


var horiz : float = Input.GetAxis("Horizontal");

//movimenta na horizontal
this.gameObject.transform.Translate(Vector3(horiz,0,0)*Time.deltaTime);

Este código cria uma variável que lê a deslocação horizontal e aplica à caixa.

A função Update fica assim:


function Update () {
var horiz : float = Input.GetAxis("Horizontal");

//movimenta na horizontal
this.gameObject.transform.Translate(Vector3(horiz,0,0)*Time.deltaTime);
//larga a caixa quando prime espaço
if(Input.GetButtonUp("Jump")){
this.gameObject.AddComponent(Rigidbody);
this.gameObject.rigidbody.mass=50;
}

}

Agora o utilizador pode largar o peso mais longe ou mais perto da base da alavanca projetando a caixa mais perto ou mais longe.

Por fim vamos adicionar um alvo a atingir, mais um cubo. O nome é alvo e a cor vai ser o laranja e não esquecer de adicionar o Rigidbody.



Este cubo vai detetar a colisão com a caixa com o seguinte código:


#pragma strict

var tempo : float = 5.0;
var tempovivo : boolean=false;
var particulas : GameObject;

function Start () {

particulas.GetComponent(ParticleSystem).Clear();

}

function Update () {

if(tempovivo){
tempo=tempo-Time.deltaTime;
if(tempo<=0){
this.gameObject.renderer.active=false;
}
}
}

function OnCollisionEnter(colisao: Collision){

if(colisao.gameObject.name=="caixa"){
tempovivo=true;
particulas.GetComponent(ParticleSystem).Play();
}

}

A função OnCollisionEnter é chamada pelo Unity sempre que acontece uma colisão com o objeto, neste caso só reagimos que o objeto que colidiu foi a caixa. Nesse caso vamos iniciar um temporizador e um sistema de partículas para fazer um efeito especial. Vamos criar o sistema de partículas já a seguir.

Ao iniciar é chamada a função Start que limpa o sistema de partículas.

Em cada frame, caso o temporizador esteja "vivo" descontamos o tempo que passou desde a última frame desenhada e se esgotamos o temporizador faz desaparecer o objeto, neste caso o alvo.

Vamos agora criar o sistema de partículas, para isso no menu GameObject - Create Other - Particle System.


Agora posicionamos o sistema de partículas na mesma posição do alvo e desativamos a opção Play On Awake.

De seguida associamos o novo código ao alvo e por fim só falta associar o sistema de partículas ao código, para isso selecionamos o alvo e na barra Inspector, na parte relativa ao código, existe uma propriedade que não está definida que é a propriedade Particulas para resolver este problema basta arrastar o sistema de partículas da barra da hierarquia para a propriedade no Inspector.


Agora podemos gravar a cena, e organizar o projeto criado pastas para os scripts e para os materiais.

Para publicar o projeto no menu File escolhemos Build Settings e na janela seguinte clicamos no botão Add Current...



Podemos também escolher a plataforma para a qual pretendemos publicar o projeto, por fim clicamos no botão Build e indicamos a pasta de saída.

O projeto e o executável estão aqui.

sexta-feira, 9 de novembro de 2012

Android - Scroll View e outras afinações

No post anterior, ao criar a nossa primeira aplicação Android, o código utilizado foi, por opção, simples. Não sendo implementados mecanismos de validação dos dados, de igual modo a interface, apesar de utilizar duas atividades com dois layouts, era básica.

Neste exemplo vamos criar um programa simples mas com alguns mecanismos fundamentais em aplicações robustas, nomeadamente, verificação dos dados introduzidos e um scroll view que permite visualizar informação que não cabe no espaço disponível no ecrã do dispositivo.

Durante este artigo implementamos dois menus, um que é apresentado com o botão menu do dispositivo e outro que está sempre visível na barra no nome da aplicação.

O que se pretende é demonstrar conceitos simples ao criar um programa que nos vai mostrar a tabuada de um determinado valor introduzido pelo utilizador.




Então começamos, criamos um projeto novo no Eclipse com uma atividade principal, a MainActivity.

A Interface da MainActivity
A interface é muito simples, um TextView com o texto Tabuada, não esquecer que devemos definir as strings no ficheiro strings.xml que está na pasta resources\values, se não o fizermos o Eclipse vai dar uns avisos que essa é que é a boa prática.
Além disso incluímos um EditText para introdução de dados, neste caso como só pretendemos valores numéricos podemos utilizar a propriedade inputType para referir que pretendemos number|numberDecimal, assim evitamos ter de testar se o utilizador introduziu letras em vez de números.
Por fim temos o botão Calcular, que tem a propriedade onClick definida com o nome da função que vai responder aos cliques, neste caso a função é click_calcular.

Como esta interface é pequena não é necessário prever a possibilidade de ter de fazer o scroll do conteúdo.

Sempre que o Eclipse cria uma atividade por defeito gera um menu com uma opção chamada settings, este menu está na pasta menu dentro da pasta res (resources) e é apresentado ao utilizador quando este clica no botão menu do dispositivo móvel.

Vamos alterar este menu para apresentar as opções Autor e Sair.

Então o código da interface fica assim:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/ntabuada"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <EditText
        android:id="@+id/txt_teste"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:ems="10"
        android:inputType="number|numberDecimal" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_marginTop="36dp"
        android:onClick="click_calcular"
        android:text="@string/calcular" />

</RelativeLayout>

O código do menu fica:
<!-- Este menu é chamado com o botão do sistema -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_sobre"
        android:title="@string/about"
        android:orderInCategory="100"
        android:showAsAction="never"
        /><!-- se estiver always o menu aparece em cima no título da aplicação -->
    <item android:id="@+id/menu_sair"
        android:title="@string/off"
        android:orderInCategory="100"
        android:showAsAction="never"
        />
</menu>

As duas opções que o menu apresenta ficam definidas no ficheiro das strings. Estas têm a propriedade showAsAction definida como never, assim, este menu só aparece quando o utilizador pressiona o botão de menu no dispositivo.

Agora a classe MainActivity:
package edu.pjcferreira.Tabuada;

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
public final static String EXTRA_NTABUADA = "ntabuada";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    /*função chamada no click*/
    public void click_calcular(View view){
    Intent intent = new Intent(this,CalculaTabuada.class);  //define a atividade a ser chamada
    //nº da tabuada
    EditText editTextTeste = (EditText) findViewById(R.id.txt_teste);
    //verifica se foi preenchido
    if(editTextTeste.getText().length()==0){
    showMessage("Por favor introduz um número!");
    return;
    }
    String message1 = editTextTeste.getText().toString();
    intent.putExtra(EXTRA_NTABUADA, message1);
    startActivity(intent);
    }
    //função chamada para preparar o menu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    //menu do sistema
    //função chamada quando uma opção do menu é selecionada
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getTitle().toString().equals("Sobre"))
showMessage("Paulo Ferreira");
if(item.getTitle().toString().equals("Sair")){
showMessage("That's all folks!");
finish();
}
return true;
}
    /* mostra o texto passado numa pequena mensagem */
    private void showMessage(CharSequence text) {
    Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
//faz aparecer uma mensagem pequena durante um certo tempo
Toast toast = Toast.makeText(context, text, duration);
toast.show();    
    }
}

Criamos uma função para apresentar as Toast, mensagens pequenas que são mostradas durante alguns segundos ao utilizador, simplificando o seu uso.

O código da função onOptionsItemSelected é executado sempre que o utilizador escolheu uma opção do menu.

O código da função click_calcular é executado com o clique no botão, nesta função, antes de chamar outra atividade testamos se o utilizador preencheu alguma coisa, é para isso que serve a linha 
if(editTextTeste.getText().length()==0)

A função onOptionsItemSelected é chamada pelo sistema quando o utilizador clica no botão do menu e depois numa das opções, o modo como deteto qual a opção escolhida passa por verificar o paramêtro da função que corresponde ao selecionado.

Para terminar a parte interessante desta atividade falamos do modo como é chamada a segunda atividade, o método foi o mesmo do post anterior, criamos um Intent e passamos o número introduzido pelo utilizador.

Segunda Atividade
A segunda atividade vai receber o valor da tabuada e calcula apresentando ao utilizador o resultado num TextView. Neste layout incluímos um ScrollView que permitirá ao utilizador deslocar o conteúdo independentemente da orientação do dispositivo.

A Interface
O código da segunda interface é:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:onClick="click_bt_alunosnet"
        android:text="@string/bt_alunosnet" />
    
    <TextView
        android:id="@+id/txt_Resultado"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceMedium" />
    </RelativeLayout>
</ScrollView>


Neste código destaco a tag ScrollView que envolve todo o conteúdo, além disso inclui um botão que permitirá abrir uma página web no browser do dispositivo.

O menu

<!-- Este menu está visivel -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_sobre"
        android:title="@string/about"
        android:orderInCategory="100"
        android:showAsAction="always"
        />  
    <item android:id="@+id/menu_sair"
        android:title="@string/back"
        android:orderInCategory="100"
        android:showAsAction="always"
        />
</menu>

O código de menu é semelhante ao outro menu com a diferença na propriedade showAsAction que como está definida em always faz aparecer o menu na barra da aplicação.

O código

package edu.pjcferreira.Tabuada;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class CalculaTabuada extends Activity{
//Chamada quando a atividade é criada
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int ntabuada=0;
        //recolher o nº da tabuada
        Intent intent = getIntent();
        String strTemp = intent.getStringExtra(MainActivity.EXTRA_NTABUADA);
        ntabuada=Integer.parseInt(strTemp);
  //mostra o novo layout
        setContentView(R.layout.resultado);
        //atualiza o novo layout com os dados
        TextView txt_resultado=(TextView)findViewById(R.id.txt_Resultado);
        txt_resultado.setText("");
        for(int i=1;i<=10;i++){
            txt_resultado.setText(txt_resultado.getText() + " " + i + " x " + ntabuada + " = " + (i*ntabuada) + "\n");
        }
    }

    public void click_bt_alunosnet(View view){
    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://alunosnet.pt.vu"));
   
    startActivity(i);  
    }
    //Função chamada para apresentar o menu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.resultado_menu, menu);
        return true;
    }
    //menu do sistema
    //função chamada quando uma opção do menu é selecionada
public boolean onOptionsItemSelected(MenuItem item) {

if(item.getTitle().toString().equals("Sobre"))
showMessage("Paulo Ferreira");

if(item.getTitle().toString().equals("Voltar")){
finish();
}

return true;
}
    /* mostra o texto passado numa pequena mensagem */
    private void showMessage(CharSequence text) {
    Context context = getApplicationContext();
int duration = Toast.LENGTH_SHORT;
//faz aparecer uma mensagem pequena durante um certo tempo
Toast toast = Toast.makeText(context, text, duration);
toast.show();    
    }
}
Deste código realço a função do botão que cria uma Intent para abrir uma página Web no browser.
Como incluímos um ScrollView na interface podemos mudar a orientação do dispositvo e fazer deslizar o conteúdo.

Como se pode ver pelas imagens o menu desta vez está sempre visível.
O projeto pode ser retirado aqui e a aplicação pronta instalar aqui.

sexta-feira, 2 de novembro de 2012

Android - para tudo há uma primeira vez

Depois de termos visto como criar um projeto em Eclipse para a plataforma Android vamos agora criar um pequeno programa que permite demonstrar o modo como se pode implementar uma interface com vários "ecrãs", os denominados formulários da plataforma .NET.

Para tal vamos criar um pequeno programa que lê do utilizador três notas (teste, exame e trabalho), cada nota tem um determinado valor percentual na nota final (teste - 30%, trabalho - 20% e exame - 50%).

Assim vamos criar uma interface que permite a leitura dos valores e que apresenta um botão para calcular o valor da nota final.

Primeiro preparamos a primeira activity com a seguinte interface:
Então temos três TextViews, que é o objeto que permite mostrar texto no ecrã, semelhante ao Label da plataforma .NET.
Incluímos, ainda, três EditTexts que permitem receber do utilizador os valores pretendidos, neste exemplo valores númericos, na plataforma .NET são as TextBoxes.
Por fim um botão.
O código XML ficou assim:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/teste"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_marginTop="38dp"
        android:text="@string/exame"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <EditText
        android:id="@+id/txt_teste"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:ems="10"
        android:hint="Nota Teste"
        android:inputType="number|numberDecimal" />
    
    <EditText
        android:id="@+id/txt_exame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/textView2"
        android:layout_alignBottom="@+id/textView2"
        android:layout_alignParentRight="true"
        android:ems="10"
        android:hint="Nota Exame"
        android:inputType="number|numberDecimal" />

    <EditText
        android:id="@+id/txt_trabalho"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/txt_exame"
        android:layout_marginTop="18dp"
        android:ems="10"
        android:hint="Nota Trabalho"
        android:inputType="number|numberDecimal" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/txt_trabalho"
        android:layout_alignBottom="@+id/txt_trabalho"
        android:layout_alignParentLeft="true"
        android:text="@string/trabalho"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/txt_trabalho"
        android:layout_marginTop="32dp"
        android:onClick="click_calcular"
        android:text="@string/calcular" />

</RelativeLayout>

No dispositivo móvel fica qualquer coisa como:

Agora toca a programar, a partir do código base basta indicar o comportamento do botão, para isso na propriedade onClick definimos o nome da função a chamar com o clique:
O código da função cria e executa uma intent que chama a activity que vai calcular a nota final, para que o cálculo seja possível é necessário passar para a activity as três notas. O código:

    public void click_calcular(View view){
    Intent intent = new Intent(this,CalculaNota.class);  //define a atividade a ser chamada
    //teste
    EditText editTextTeste = (EditText) findViewById(R.id.txt_teste);
    String message1 = editTextTeste.getText().toString();
    //exame
    EditText editTextExame = (EditText) findViewById(R.id.txt_exame);
    String message2 = editTextExame.getText().toString();
    //trabalho
    EditText editTextTrabalho = (EditText) findViewById(R.id.txt_trabalho);
    String message3 = editTextTrabalho.getText().toString();
   
    intent.putExtra(EXTRA_TESTE, message1);
    intent.putExtra(EXTRA_EXAME, message2);
    intent.putExtra(EXTRA_TRABALHO, message3);
    startActivity(intent);
    }

O resto do código fica tal e qual o Eclipse o gerou.

De seguida criamos outra classe para a segunda activity, o nome a atribuir é CalculaNota e a primeira coisa a fazer é recuperar as três notas passadas, de seguida calcular o valor da nota final e depois apresentar tudo na nova interface.
Agora por partes, primeiro criamos a interface com o seguinte código:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/txt_Teste"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/teste"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/txt_Exame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/txt_Teste"
        android:layout_marginTop="60dp"
        android:text="@string/exame"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/txt_Trabalho"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/txt_Teste"
        android:layout_marginTop="15dp"
        android:text="@string/trabalho"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/txt_NotaFinal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/txt_Exame"
        android:layout_marginTop="25dp"
        android:text="Medium Text"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

A interface é muito simples, apresenta quatro TextViews, uma para cada nota e outra para a nota final.
No editor a interface fica assim:
O nome deste layout é "resultado" assim o código da nova activity fica:

package edu.pjcferreira.NotaAluno;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;

public class CalculaNota extends Activity{
//Chamada quando a atividade é criada
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //recolher as notas
        Intent intent = getIntent();
        //nota do teste
        String strTeste = intent.getStringExtra(MainActivity.EXTRA_TESTE);
        float teste=Float.parseFloat(strTeste);
        //nota do exame
        String strExame=intent.getStringExtra(MainActivity.EXTRA_EXAME);
        float exame=Float.parseFloat(strExame);
        //nota do trabalho
        String strTrabalho=intent.getStringExtra(MainActivity.EXTRA_TRABALHO);
        float trabalho=Float.parseFloat(strTrabalho);
        //nota final
        float notaFinal=(float) (teste*0.3+trabalho*0.2+exame*0.5);
        //mostra o novo layout
        setContentView(R.layout.resultado);
        //atualiza o novo layout com os dados
        //exame
        TextView txt_exame=(TextView)findViewById(R.id.txt_Exame);
        txt_exame.setText(txt_exame.getText() + " (50%): " + strExame);
        //tabalho
        TextView txt_trabalho=(TextView)findViewById(R.id.txt_Trabalho);
        txt_trabalho.setText(txt_trabalho.getText() + " (20%): " + strTrabalho);
        //teste
        TextView txt_teste=(TextView)findViewById(R.id.txt_Teste);
        txt_teste.setText(txt_teste.getText() + " (30%): " + strTeste);
        //nota final
        TextView txt_notafinal=(TextView)findViewById(R.id.txt_NotaFinal);
        txt_notafinal.setText("Nota Final: " + String.valueOf(notaFinal));
    }

}

No dispositivo móvel aparece assim quando clicamos no botão:
MUITO IMPORTANTE: temos de adicionar esta nova activity ao manifesto da aplicação se não o programa encerra ao clicar no botão, para isso basta fazer duplo clique no ficheiro do manifesto da aplicação a partir do explorador do projeto e adicionar através da interface ou diretamente no código XML.


Código XML:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.pjcferreira.NotaAluno"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="CalculaNota"></activity>
    </application>

</manifest>

E pronto o programa está pronto a executar.
O código fonte está aqui e o executável aqui.