domingo, 30 de janeiro de 2011

Problema ao iniciar emulador do Android

Olá povo,

Meu aluno Rogério Casal do curso de Android do Unibratec me mandou esse problema e a respectiva solução.

Ao criar um AVD (Android Virtual Device) para que possamos executar um emulador do Android, essas configurações ficam armazenadas no seguinte diretório:

Para Windows:
C:\Usuário\.android\avd\
C:\Documents and Settings\Usuário\.android\avd

Para Linux e OS X:
/home/Usuário/.android/avd/

Porém, se o nome do seu usuário contém acentuação ou cedilha, o emulador não inicia corretamente com AVDs que contenham esses caracteres. Normalmente acontece o seguinte erro:

emulator: ERROR: no search paths found in this AVD's configuration.

A solução encontrada foi mover seu arquivo de AVD para um caminho que não contenha acentuação. Para fazer isso, após criar o AVD, abra o prompt e digite o seguinte comando:

android move avd -n nome_do_avd -p C:\AVDs

O caminho especificado deve sempre criar um diretório ao invés de usar uma pasta já existente (no exemplo acima seria criado o diretório AVDs).

4br4ç05,
nglauber

segunda-feira, 24 de janeiro de 2011

Usando tecla (diálogo) de busca

Olá povo,

Ontem estava eu deitadão assitindo televisão quando chega minha digníssima esposa e me solta essa pérola: "Ei seu blog está desatualizado. A última postagem foi do começo do mês...". Sendo assim, cá estou para mais um post :)

Hoje vou mostrar pra vocês, como utilizar o botão de busca na sua aplicação, que é padrão em todos os na maioria dos aparelhos Android. Esse botão permite que você implemente a funcionalidade de busca de uma maneira padrão à do sistema operacional.

Crie no seu projeto um arquivo chamado busca.xml dentro do diretório res/xml. Nesse arquivo ficarão as configurações do diálogo de busca:


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/app_hint" >
</searchable>

Notem que no arquivo acima, estamos usando duas strings que devem estar definidas no arquivo /res/values/strings.xml. Tentei usar valores hard coded e não funcionou.

Em seguida, devemos informar no AndroidManifest.xml que a atividade tratará o botão de busca. Para isso, devemos adicionar um IntentFilter com a ação android.intent.action.SEARCH e o meta-data passando nosso XML criado acima:

<activity android:name=".MinhaAtividade" >
<intent-filter>
<action
android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/busca"/>
</activity>


Feito isso, basta tratar a Intent com a busca. Coloque o código abaixo no método onCreate() da sua Activity. Caso não queira que seja aberta uma nova instância da Activity a cada nova chamada a tela de busca, marque-a no AndroidManifest.xml como android:launchMode="singleTop" e trate o evento de busca no método onNewIntent().

Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//faça sua busca com a variável query
}



Mais informações aqui.

4br4ç05,
nglauber

sexta-feira, 7 de janeiro de 2011

ANT + Antenna

Olá povo,

Esse post vai em homenagem a primeira turma de pós-graduação em Tecnologias para Desenvolvimento de Aplicações Movéis do CESAR.edu que aguentaram o mico que eu paguei quando o exemplo que eu vou mostrar nesse post não funcionou na sala. Então lá vai...

Vou demonstrar nesse post como utilizar o Ant e o Antenna. O Ant é uma ferramenta para automatização de scripts desenvolvida pela Apache Foundation. Baseia-se em tarefas a serem executadas e descritas em um arquivo XML (chamado de build file).

O Antenna é uma biblioteca que pode ser adicionada ao Ant para prover um conjunto de tarefas exclusivas para o desenvolvimento com Java ME. As tarefas realizadas pelo Antenna executam o trabalho que o WTK realiza, porém, com elas o processo de deploy (geração de um JAR/JAD) da aplicação fica mais flexível e independente de IDE.

Uma das grandes vantagens da utilização dessas ferramentas é auxiliar a prover portabilidade para a diversidade de fabricantes e modelos de aparelhos existentes no mercado, uma vez que tamanho de tela, quantidade e disponibilidade de teclas, formato de áudio e vídeo suportados são algumas das características que variam bastante de modelo para modelo.

Supondo que já temos o Eclipse configurado para Java ME, precisamos apenas baixar o JAR do Antenna que está disponível aqui. Depois, basta configurar no Eclipse o local onde você baixou o JAR. Para isso, acesso o menu Window > Preferences. No lado esquerdo, selecione Java ME, e preencha o campo Antenna JAR com o nome e local onde está o JAR do Antenna.

Vamos utilizar um projeto Java ME bem simples no Eclipse para demonstrar a utilização do Ant+Antenna. A estrutura final do projeto ficará como na imagem abaixo:


Vamos começar detalhando a classe PrincipalMIDlet.java:

public class PrincipalMIDlet extends MIDlet {

protected void startApp() {
Display.getDisplay(this).
setCurrent(new MeuCanvas());
}

protected void destroyApp(boolean uncond){
notifyDestroyed();
}

protected void pauseApp() {
}
}

class MeuCanvas extends Canvas {
//#ifdef operadora
//#expand private String OPERADORA = "%operadora%";
//#else
private String OPERADORA = "Indefinido";
//#endif

//#ifdef background
//#expand private int COR_BACKGROUND = %background%;
//#else
private int COR_BACKGROUND = 0;
//#endif

private static final int COR_TEXTO = 0xFFFFFF;

private Image icon;

public MeuCanvas() {
try {
icon = Image.createImage("/icon.png");
} catch (IOException e) {
}
}

protected void paint(Graphics g) {
int w = getWidth();
int h = getHeight();

g.setColor(COR_BACKGROUND);
g.fillRect(0, 0, w, h);

g.drawImage(icon, w/2, h/2,
Graphics.BOTTOM | Graphics.HCENTER);

g.setColor(COR_TEXTO);
g.drawString(OPERADORA, w/2, h/2,
Graphics.TOP | Graphics.HCENTER);
}
}

A parte bacana do código acima, está na definição das constantes OPERADORA e COR_BACKGROUND. Notem que elas estão envolvidas por trechos de código que aparentemente são comentários do Java, mas na verdade elas são diretivas de pré-processamento. Esse recurso é possível graças ao Antenna, que faz com que essas diretivas tenham funcionalidade similar as diretivas de compilação da linguagem C.

A instrução //#ifdef verifica se uma determinado símbolo foi definido, em caso positivo, o código-fonte que estiver dentro dessa diretiva é o que será compilado, caso contrário será compilado o código que está dentro da diretiva //#else. Toda diretiva //#ifdef deve ser fechada com a diretiva //#endif.

A diretiva //#expand substituirá qualquer símbolo que estiver entre "%" com seu respectivo valor. No nosso exemplo, definimos dois arquivos de símbolos que contêm os símbolos "operadora" e "background". Esses arquivos são mostrados abaixo:

#Claro.symbols
operadora='Claro'
background='0xFF0000'

#Vivo.symbols
operadora='Vivo'
background='0x0000FF'

Um outro detalhe a ser observado é que quando a imagem icon.png foi carregada, ela não foi carregada do diretório Vivo ou Claro. Isso porque, geraremos um JAR e um JAD da nossa aplicação para cada operadora, então os recursos de cada uma delas ficará na raiz do JAR.

Vamos agora ao script de deploy. A estrutura do build.xml, está conforme a imagem abaixo:


<?xml version="1.0" encoding="UTF-8"?>

<project name="ExemploAnt" basedir="." >

<!-- Carrega as tarefas do Antenna -->
<taskdef classpath="${antenna.lib}"
resource="antenna.properties"/>

<!-- Carrega o arquivo de propriedade -->
<property file="meubuild.properties"/>

<!-- Realiza o deploy para a Vivo -->
<target name="deployVivo">
<!-- Atribui a propriedade 'operadora' que
será utilizada no restante do script -->
<property name="operadora" value="Vivo"/>

<!-- Chama a tarefa fazerDeploy -->
<antcall target="gerarJAR" />

<!-- Chama a tarefa fazerDeploy -->
<antcall target="executarMIDlet" />
</target>

<!-- Realiza o deploy para a Claro -->
<target name="deployClaro">
<property name="operadora" value="Claro" />
<antcall target="gerarJAR" />
<antcall target="executarMIDlet" />
</target>

<!-- Gera o JAR -->
<target name="gerarJAR" depends="compilar" >
<wtkpackage
autoversion="${do-autoversion}"
bootclasspath="${wtk.libs}"
jadfile="${work.dir}/deployed/${project.name}.jad"
jarfile="${work.dir}/deployed/${project.name}.jar"
libclasspath=""
obfuscate="${do-obfuscate}"
preverify="true">

<fileset dir="${work.dir}/bin/"/>
<fileset dir="${work.dir}/resources/"/>
</wtkpackage>
</target>

<!-- Compila os arquivos *,java-->
<target name="compilar" depends="preverificar" >
<wtkbuild
bootclasspath="${wtk.libs}"
destdir="${work.dir}/bin/"
encoding="UTF-8"
preverify="false"
source="1.3"
sourcepath=""
srcdir="${work.dir}/preverified/"/>
</target>

<!-- Efetua o pré-processamento dos arquivos
*.java utilizando as diretivas -->
<target name="preverificar"
depends="copiarCodigoFonte" >

<wtkpreprocess
debuglevel="info"
destdir="${work.dir}/preverified/"
printsymbols="true"
srcdir="${work.dir}/classes"
verbose="true">

<!--Carrega o arquivo de
símbolos da operadora-->
<symbols_file name="${operadora}.symbols/"/>
</wtkpreprocess>
</target>

<!-- Copia os arquivos *.java para
o diretório de build -->
<target name="copiarCodigoFonte"
depends="copiarRecursos" >

<copy overwrite="true" todir="${work.dir}/classes/">
<fileset dir="src" includes="**/**.java"/>
</copy>
</target>

<!-- Copia os arquivos de recurso para o diretório de build -->
<target name="copiarRecursos"
depends="criarDiretorios">

<copy file="Application Descriptor"
tofile="${work.dir}/deployed/${project.name}.jad"/>

<copy todir="${work.dir}/resources/">
<fileset dir="res/${operadora}"
excludes="**/**.java"/>
</copy>
</target>

<!-- Cria os sub-diretórios do diretório de build -->
<target name="criarDiretorios" depends="limparTudo" >
<!-- Dir onde ficarão JAR e JAD -->
<mkdir dir="${work.dir}/deployed/"/>
<!-- Dir onde *.java pré-processados ficarão -->
<mkdir dir="${work.dir}/preverified/"/>
<!-- Dir onde *.java serão copiados de /src -->
<mkdir dir="${work.dir}/classes/"/>
<!-- Dir dos recursos (imagens, áudio, etc) -->
<mkdir dir="${work.dir}/resources/"/>
<!-- Dir onde os *.class serão compilados -->
<mkdir dir="${work.dir}/bin/"/>
</target>

<!-- Apaga todo o diretório de build -->
<target name="limparTudo">
<delete dir="${work.dir}" failonerror="false"/>
</target>

<!-- Executa o JAR/JAD no emulador -->
<target depends="" name="executarMIDlet">
<wtkrun
jadfile="${work.dir}/deployed/${project.name}.jad" />
</target>

</project>


Como vocês puderam notar, diversas linhas do scrpit utilizam a notação ${variavel}. Isso representa uma propriedade ou um símbolo. Os símbolos estão definidos nos arquivos *.symbols e são usados para o pré-processamento. Já as propriedades são definidas no arquivo meubuild.properties (ou individualmente como é feito com a propriedade "operadora". O arquivo meubuild.propreties ficou da seguinte forma:

# Diretório raiz do WTK
wtk.home=C\:\\WTK2.5.2_01
# Deve obfuscar?
do-obfuscate=false
# Versão do MIDP e do CLDC
wtk.midp.version=2.0
wtk.cldc.version=1.1
# MIDlet deve ser auto versionado?
do-autoversion=false
# JAR do Antenna
antenna.lib=C\:\\nglauber\\J2ME\\antenna-bin-1.2.1-beta.jar
# JARs necessários na compilação
wtk.libs=C\:\\WTK2.5.2_01\\lib\\midpapi20.jar:C\:\\WTK2.5.2_01\\lib\\cldcapi11.jar:
# Nome do projeto
project.name=ExemploAnt
# Diretório de build
work.dir=output

Para executar o script de build, arraste esse arquivo para a view Ant do Eclipse. Se não estiver visível, acesse o menu Window > Show View > Other..., na janela que for exibida escolha Ant. Feito isso, basta dar um duplo-clique sobre a tarefa deployVivo ou deployClaro.

O resultado será similar a figura abaixo:



Quem quiser mais detalhes pode dar uma olhada no artigo que escrevi pra Web Mobile 29.

4br4ç05,
nglauber

terça-feira, 4 de janeiro de 2011

Curso de Android em Recife

Olá povo,

Amanhã começa a quinta turma de Google Android da Especializa Treinamentos. O curso tem carga horária de 40 horas e as aulas serão ministradas por mim, de Segunda a Quinta, das 19:00 as 22:00.

No curso, estudaremos a revolucionária plataforma para smartphones e tablets da Google e como desenvolver aplicativos para ela. Os conceitos de Activities, Handlers, ContentProviders e Services serão aplicados na prática. Vamos ver como armazenar informações no banco de dados do celular com SQLite. Comunicação com WebServices e com o GoogleMaps. Envio de SMS. E muito mais.

Quem tiver interesse, pode entrar na página do curso para obter mais informações e fazer sua inscrição. Corram que ainda há tempo!

4br4ç05,
nglauber

segunda-feira, 3 de janeiro de 2011

Áudio no Android

Olá povo,

Para tocar qualquer som no Android, podemos utilizar a classe MediaPlayer. Com ela podemos executar arquivos de áudio que estejam dentro do APK (na pasta assets ou res/raw) ou ainda no sistema de arquivos do aparelho (como SD card por exemplo).
// Carregando audio do diretório res/raw
MediaPlayer player = 
  MediaPlayer.create(this, R.raw.explosion);
player.start();

// Carregando audio do cartão de memória
MediaPlayer mp = new MediaPlayer();
mp.setDataSource("/sdcard/explosion.mp3");
mp.prepare();
mp.start();


Porém, em um dos aplicativos que desenvolvemos aqui no trabalho, nós precisávamos tocar dois sons simultâneamente: a música do jogo e o efeito sonoro. A classe MediaPlayer não é aconselhável para efeitos sonoros uma vez que eles precisam de um tempo de resposta baixo. Para esse trabalho, podemos utilizar as classes SoundPool e AudioManager (do pacote android.media) que facilitam o trabalho com sons, permitindo tocá-los concorrentemente.

Vejam o exemplo comentado abaixo:
public class SoundManager {
  // Total de sons no pool
  private static final int MAXSTREAMS = 4;
  // Instância única 
  private static SoundManager instance;
  
  // Pool de sons
  private SoundPool mSoundPool;
  // AudioManager para controlar o volume do som
  private AudioManager mAudioManager;
  // Lista com os ids dos sons adicionados
  private ArrayList<Integer> mSoundPoolMap;
  // Pilha que armazena as transações 
  // de execução dos sons
  private Stack<Integer> mSongsTransactions;
  
  private Context mContext;

  // Construtor privado pra implementar o 
  // Singleton Design Pattern
  private SoundManager(Context ct) {
    mContext = ct;
    mSoundPoolMap = new ArrayList<Integer>();
    mSongsTransactions = new Stack<Integer>();
    
    // Criando o pool de sons
    mSoundPool = new SoundPool(
        MAXSTREAMS, AudioManager.STREAM_MUSIC, 0);
    
    // AudioManager é um serviço de sistema
    mAudioManager = (AudioManager) 
        mContext.getSystemService(
            Context.AUDIO_SERVICE);
  }

  // Método estático para obter a instância única
  public static SoundManager getInstance(Context ct) {
    if (instance == null){
      instance = new SoundManager(ct);
    }
    return instance;
  }

  // Adiciona um som ao pool
  public void addSound(int soundId) {
    mSoundPoolMap.add(
      /* Carrega e obtém o id do som no pool
       * O segundo parâmetro o id do recurso
       * E o terceiro não serve pra nada :) ,
       * Mas na documentação diz pra colocar 1 */
      mSoundPool.load(mContext, soundId, 1));
  }

  // Manda tocar um determinado som
  public void playSound(int index) {
    /* O AudioManager é usado aqui pra obter
     * o valor atual do volume do aparelho para
     * não tocar o som nem baixo nem alto demais.
     * A divisão que é feita aqui é pq o método 
     * requer um valor entre 0.0 e 1.0. */
    float streamVolume = 
      mAudioManager.getStreamVolume(
          AudioManager.STREAM_MUSIC);
    streamVolume /= 
      mAudioManager.getStreamMaxVolume(
          AudioManager.STREAM_MUSIC);
    
    /* playId, armazena o id da requisição do som 
     * a ser tocado. Ele é usado para parar um 
     * determinado som a qualquer momento. */
    int playId = mSoundPool.play(
      mSoundPoolMap.get(index), // ID do som
      streamVolume, // volume da esquerda
      streamVolume, // volume da direita
      1, // prioridade 
      0, // -1 toca repetidamente, 
         // n = número de repetições)
      1  // pitch. 0.5f metade da velocidade
         // 1 = normal e 2 = dobro da velocidade
      );
    
    // adiciona o id da transação na pilha
    mSongsTransactions.push(playId);
  }

  public void stopSounds() {
    // Percorre todos os ids da pilha e manda
    // parar todos os sons
    while (mSongsTransactions.size() > 0)
      mSoundPool.stop(mSongsTransactions.pop());
  }

  // Libera os recursos alocados
  public void cleanup() {
    mSoundPool.release();
    mSoundPool = null;
    mSoundPoolMap.clear();
    mSongsTransactions.clear();
    mAudioManager.unloadSoundEffects();
  }
}

Com a classe acima, podemos executar duas mídias concorrentemente e com um tempo de resposta muito bom. Vejam como utiliza-la abaixo:
public class TesteSomActivity 
  extends Activity 
  implements OnClickListener{

  SoundManager sm;
 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    // Informa que a aplicação controlará o 
    // volume da media do telefone pelos botões
    // de volume do aparelho
    setVolumeControlStream(
      AudioManager.STREAM_MUSIC);
        
    sm = SoundManager.getInstance(this);
    sm.addSound(R.raw.explosao);
    sm.addSound(R.raw.musica);
        
    ((Button)findViewById(R.id.btnStart1)).
      setOnClickListener(this);
    ((Button)findViewById(R.id.btnStart2)).
      setOnClickListener(this);
    ((Button)findViewById(R.id.btnStop)).
      setOnClickListener(this);
  }

  public void onClick(View v) {
    if (v.getId() == R.id.btnStart1){
      sm.playSound(0);

    } else if (v.getId() == R.id.btnStart2){
      sm.playSound(1);

    } else if (v.getId() == R.id.btnStop){
      sm.stopSounds();
    }
  }
}

O primeiro botão executará um efeito sonoro de explosão, enquanto que o segundo tocará a música do jogo. O terceiro parará ambos.
Se o aúdio for muito grande, é melhor optar por combinar o MediaPlayer para a música do jogo e SoundPool para os efeitos sonoros.

Para fazer com que sua aplicação controle o volume da mídia através dos botões de volume do aparelho (ou das teclas + e - no emulador) basta colocar a linha abaixo no onCreate da sua Activity:

setVolumeControlStream(AudioManager.STREAM_MUSIC);


Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

P.S.: Esse exemplo foi baseado em dois posts encontrados por dois colaboradores daqui do CESAR.
Post 1 e
Post 2

sábado, 1 de janeiro de 2011

LWUIT

Olá povo,

No primeiro post de 2011, falarei um pouquinho sobre o LWUIT (Lightweight UI Toolkit). Quem já desenvolveu alguma aplicação comercial com Java ME, sabe o como é complicado criar uma UI (User Interface) amigável com os componentes padrão. Os componentes LCDUI (Liquid Cristal Display UI) estão disponíveis no pacote javax.microedition.lcdui e trazem o benefício de manter a aparência da aplicação Java ME similar a uma aplicação nativa do aparelho. Porém, esses componentes são muito limitados dado o baixo poder de processamento dos dispositivos móveis na época em que esse framework foi concebido.

O LWUIT é um framework leve (como o próprio nome diz) e bastante poderoso para criação da interface gráfica de aplicações Java ME. Ele traz algumas similaridades com o Swing do Java SE e traz diversas funcionalidades interessantes como: uma boa quantidade de componentes; transições de tela; suporte a temas, estilos e fontes personalizadas; ajuste automático a diversos tamanhos de tela.

O LWUIT pode ser baixado separadamente a partir do site da Oracle. Clique aqui para baixar o ZIP com o JAR, documentação e exemplos de uso do framework.

Descompacte o conteúdo do arquivo em qualquer lugar da sua máquina e crie um novo projeto Java ME no Eclispe. Copie o arquivo LWUIT.jar (que está dentro do diretório lib) para seu projeto e adicione-o ao Build Path. Configure para que as classes do JAR do LWUIT sejam exportadas junto com o JAR da aplicação. Para fazer isso, clique com o botão direito sobre o projeto e clique em Properties. Ao abrir as opções do projeto, clique em Java Build Path no lado esquerdo, e na aba Order and Export, marque o checkbox do LWUIT.jar.

Uma vez com o projeto configurado, podemos executar o exemplo abaixo.


import java.io.IOException;

import javax.microedition.midlet.MIDlet;

import com.sun.lwuit.*;
import com.sun.lwuit.animations.CommonTransitions;
import com.sun.lwuit.events.*;
import com.sun.lwuit.layouts.*;
import com.sun.lwuit.plaf.UIManager;
import com.sun.lwuit.util.Resources;

public class TesteLWUIT_MIDlet extends MIDlet
// Interface genérica para tratar
// eventos dos componentes
implements ActionListener {

private Command cmdLista;
private Command cmdBack;
private Command cmdTabs;
private Command cmdExit;

private Button btn1;
private CheckBox checkbox;

private Form frmLista;
private Form frmPrincipal;
private Form frmTabs;

protected void startApp() {
iniciaLWUIT();
iniciaComandos();
iniciaFormPrincipal();
iniciaFormLista();
iniciaFormTabs();
}

protected void pauseApp() {
}

protected void destroyApp(boolean arg0){
notifyDestroyed();
}

private void iniciaLWUIT() {
// Inicializando o Display do LWUIT
Display.init(this);

try {
// Carrega o arquivo de recursos
Resources r = Resources.open("/LWUITtheme.res");

// Definindo o tema da aplicação
UIManager.getInstance().setThemeProps(
r.getTheme(r.getThemeResourceNames()[0]));

} catch (java.io.IOException e) {
}
}

// Iniciando comandos que são
// utilizados nos formulários
private void iniciaComandos() {
cmdLista = new Command("Lista");
cmdBack = new Command("Voltar");
cmdExit = new Command("Sair");
cmdTabs = new Command("Tabs");
}

// Criando tela principal
private void iniciaFormPrincipal() {
// Caixa de texto com capacidade de 20 caracteres
TextField txt = new TextField(20);

// Imagem para ser associada ao label
Image img = null;
Label imageLabel = null;

try {
// Carregando a imagem
img = Image.createImage("/duke.jpeg");
// Criando Label com a imagem
imageLabel = new Label(img);
// Define o texto do Label
imageLabel.setText("Label com Imagem");
// Define a posição do texto em relação a imagem
imageLabel.setTextPosition(Label.RIGHT);

} catch (IOException e) {
}

// Criando dois botões
btn1 = new Button("Botão 1");

// Definindo alinhamento do texto do botão
btn1.setAlignment(Label.CENTER);
// Informa que o botão pode
// receber foco com o direcional
btn1.setFocusable(true);

// Informa que a classe que
// tratará evento de clique
btn1.addActionListener(this);

// Criando RadioButtons
RadioButton rb1 = new RadioButton("Masculino");
RadioButton rb2 = new RadioButton("Feminino");
// Criando o RadioGrou e adicionando os botões
ButtonGroup group1 = new ButtonGroup();
group1.add(rb1);
group1.add(rb2);

// Checkbox
checkbox = new CheckBox("Não selecionado");
checkbox.addActionListener(this);

// Combobox
String[] content = {
"Vermelho", "Azul", "Verde", "Amarelo" };
ComboBox comboBox = new ComboBox(content);

// Inicializando e configurando o formulário
frmPrincipal = new Form();
frmPrincipal.setTitle("Formulário");
// Definindo o layout do formulário.
// BoxLayout adiciona os componentes um abaixo do
// outro (Y_AXIS) ou um ao lado do outro (X_AXIS)
frmPrincipal.setLayout(
new BoxLayout(BoxLayout.Y_AXIS));
// Adicionando componentes
frmPrincipal.addComponent(new Label("Nome:"));
frmPrincipal.addComponent(txt);
frmPrincipal.addComponent(imageLabel);
frmPrincipal.addComponent(btn1);
frmPrincipal.addComponent(rb1);
frmPrincipal.addComponent(rb2);
frmPrincipal.addComponent(comboBox);
frmPrincipal.addComponent(checkbox);
// Adicionando comandos
frmPrincipal.addCommand(cmdExit);
frmPrincipal.addCommand(cmdLista);
frmPrincipal.addCommand(cmdTabs);
// Definindo qual classe
// tratará eventos de comandos
frmPrincipal.addCommandListener(this);
// Definindo animação que será feita
// ao fechar e exibir o form
// Neste caso, Slide horizontal
frmPrincipal.setTransitionOutAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL,
false, 200));
frmPrincipal.setTransitionInAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL,
true, 200));
// Exibe o form
frmPrincipal.show();
}

// Inicializa uma tela de Lista
private void iniciaFormLista() {
String[] itens = {
"Opção 1", "Opção 2", "Opção 3",
"Opção 4", "Opção 5", "Opção 6",};
List lista = new List(itens);

frmLista = new Form("Exemplo Lista");
// Utilizando o BorderLayout
// Ele utiliza o conceito de NORTE,
// SUL, LESTE, OESTE e CENTRO
// Ao deixar um componente no centro
// ele ocupará o espaço restante da tela,
// como só tem um, ele preencherá toda a tela
frmLista.setLayout(new BorderLayout());
frmLista.addComponent(BorderLayout.CENTER, lista);
frmLista.addCommand(cmdBack);
frmLista.addCommandListener(this);
}

// Inicializa tela com abas
private void iniciaFormTabs() {
// Inicializa o painel de abas,
// mostrandos-as em cima
TabbedPane tabbedPane =
new TabbedPane(TabbedPane.TOP);
// Cria as abas adicionando um Label nas
// duas primeiras e um TextArea na terceira.
tabbedPane.addTab("Aba 1",
new Label("Label na primeira aba"));
tabbedPane.addTab("Aba 2", new Label("Aba 2"));
tabbedPane.addTab("Aba 3", new TextArea());

frmTabs = new Form("Exemplo Tabs");
frmTabs.setLayout(new BorderLayout());

frmTabs.addComponent(
BorderLayout.CENTER, tabbedPane);
frmTabs.addCommand(cmdBack);
frmTabs.addCommandListener(this);
}

// Tratando eventos das telas
public void actionPerformed(ActionEvent event) {
if (event.getSource() == cmdLista){
frmLista.show();

} else if (event.getSource().equals(cmdBack)){
frmPrincipal.show();

} else if (event.getSource() == btn1 ||
event.getSource() == cmdTabs){
frmTabs.show();

} else if (event.getSource() == checkbox){
if (checkbox.isSelected()) {
checkbox.setText("Selecionado");
} else {
checkbox.setText("Não selecionado");
}

} else if (event.getSource() == cmdExit){
destroyApp(true);
}
}
}


Abaixo temos um screenshot das telas da aplicação em execução:




Mas porque diabos a aplicação ficou com esse aspecto? E se eu quiser mudar o esquema de cores ou fontes ou qualquer outra coisa?

Se observarmos no método iniciaLWUIT, carregamos um arquivo de recursos chamado LWUITtheme.res. Esse arquivo já vem com o LWUIT e está localizado no diretório LWUIT_1_4\LWUITDemo\src. O LWUIT trabalha com um único arquivo de recursos que permite criar temas para definir as cores e fontes de cada elemento da UI de uma aplicação. Para criar e editar esses arquivos de recursos o LWUIT disponibiliza um editor visual que pode inclusive ser utilizado por designers. Esse editor está dentro do diretório util/ResourceEdit.exe (usuários não-windows podem executar o JAR diretamente que está no mesmo diretório). Abaixo temos um screenshot do editor, se quiser abrir o tema é só abrir o arquivo LWUITtheme.res com a ferramenta:



Alguns telefones podem não gostar do tamanho que o JAR pode ficar (em torno de 500KB) :) Para isso utilize o Proguard para obfuscar o código-fonte da aplicação e esse tamanho diminuirá sensivelmente.

4br4ç05,
nglauber

quarta-feira, 8 de dezembro de 2010

Java ME: Configuração do Ambiente

Olá povo,

Nessa sexta começo minha carreira como professor universitário :) Ministrarei a disciplina de Java ME no Curso de Especialização em Engenharia de Software com Ênfase em Tecnologias para Desenvolvimento de Aplicações Móveis do CESAR.edu. Então como vocês devem imaginar, vão vir por aí alguns post sobre o assunto. O primeiro deles será sobre como configurar o ambiente e fazer o bom e velho "Hello World".

Baixando o JDK

Precisaremos incialmente do JDK que está disponível em:
http://www.oracle.com/technetwork/java/javase/downloads/index.html

A instalação em bem simples, basta seguir o assitente. (e venhamos e convenhamos, todos que estão lendo este post já devem ter o JDK na máquina :)

Baixando o WTK (Wireless Toolkit)

Para baixar o WTK devemos acessar a seguinte URL:
http://www.oracle.com/technetwork/java/javame/downloads/index.html

A versão mais recente da plataforma é a 3.0, mas atualmente ela só está disponível para Windows. Dessa forma, utilizaremos a versão 2.5.2 que está disponível para Windows e Linux.


Após baixar o WTK, basta seguir as instruções do assistente.


Instalando a IDE

Para desenvolvermos aplicações Java ME, podemos utilizar a IDE de nossa preferência. As mais famosas são o Eclipse e o Netbeans. Ambas contam com plugins para desenvolvimento para edição móvel de Java.


O Eclipse conta com uma versão especial para desenvolvimento mobile chamada Pulsar. Ela já vem com as ferramentas necessárias para criação de aplicações Java ME, e para baixá-la basta acessar: http://www.eclipse.org/pulsar/
e descompactar em qualquer lugar do seu HD.

Mas para quem já tem seu Eclipse já todo configurado, não precisa se preocupar em instalar outra versão da IDE. Basta adicionar o plugin MTJ (Mobile Tools for Java) e você terá um ambiente pronto para criar suas aplicações Java ME. Para isso, acesse o menu Help > Install new software e adicione a URL: http://download.eclipse.org/mtj/updates/1.1.2/stable e em seguida prosseguir com o assitente.


Para os desenvolvedores que utilizam o Netbeans, a IDE já pode ser baixada com o Mibility Pack. Que permite a criação de aplicativos Java ME. Caso sua versão não conte com esse plugin, basta baixa-lo dentro da própria ferramenta acessando o menu Tools > Plugins e selecionando o plugin Mobility.

Configurando a IDE para o SDK

Uma vez que temos o SDK e a nossa IDE favorita instalados, devemos fazer a ligação de ambos.

Para o Eclipse faremos o seguinte:
1) Acesse o menu Window > Preferences;
2) Clique em Java ME e em seguida preencha o campo WTK home com o local onde o WTK está instalado;
3) Na mesma janela, clique em Device Management que é um subitem de Java ME. Clique no botão Manual Install... para informar os emuladores que iremos utilizar.
4) Abrirá a janela Manual Device Installation, clique no botão Browse... e selecione o diretório de instalação do WTK.
5) Clique em Finish para fechar a tela atual e OK para fechar a tela de configuração.

Para o Netbeans:
1) Acesse o menu Tools > Java Plaforms;
2) Clique em Add Platform. Em seguida, selecione Emulator for Java ME Platform e clique em Next;
3) O assistente deve achar o local da instalação automaticamente, caso contrário indique o local da instalação e clique em Next.
4) Clique em Finish e a configuração estará concluída.


Hello World!!!

Vamos agora satisfazer os deuses da programação e fazermos um HelloWorld com a plataforma Java ME.

Eclipse
1) Acesse o menu File > New > Project...
2) Em seguida, selecione Java ME > MIDlet Project
3) Dê o nome de HelloWorld ao projeto e clique em Finish
4) Na pasta src clique com o botão direito, e selecione New... > Java ME MIDlet
5) Abrirá o assitente de criação de classes. Dê a classe o nome de HelloWorldMIDlet e clique em Finish

Netbeans
1) Acesse o menu File > New project...
2) Na categoria Java ME, selecione Mobile Application
3) Dê o nome de HelloWorld ao projeto.
O Netbeans já disponibiliza uma opção que cria o HelloWorldMIDlet (estraga prazer:) Desmarque essa opção e clique em Finish.
4) Na pasta src clique com o botão direito, e selecione New... > MIDlet...
5) Abrirá o assitente de criação de classes. Dê a classe o nome de HelloWorldMIDlet e clique em Finish

Agora, deixe a classe HelloWorldMIDlet conforme abaixo:
public class HelloWorldMIDlet extends MIDlet {
public HelloWorldMIDlet() {
Display d = Display.getDisplay(this);

Form form = new Form("Hello!");
form.append("Olá mundo Java ME");

d.setCurrent(form);
}

protected void destroyApp(boolean unconditional) {}

protected void pauseApp() {}

protected void startApp() {}
}


No Eclipse, clique com o botão direito sobre o projeto e selecione: Run as > Emulated Java ME JAD.
No Netbeans, basta clicar no botão play verdinho :p

Quando o emulador for executado, clique na tecla do celular correspondente a opção Launch. O resultado deve ser conforme a figura abaixo:



Qualquer dúvida ou sugestão, deixem seus comentários.

4br4ç05,
nglauber