Transformando seu EditText em FieldMoney (Campo monetário)

Oi,

Comecei a observar nos fóruns de programação Java que existem muitas dúvidas em relação a como fazer um campo monetário no Android.

Após não encontrar nenhum solução adequada, resolvi criar um componente que atendesse essa caracteristica.

Quem ainda não sabe como fazer, segue abaixo:

import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.widget.EditText;

public class FieldMoney
 extends EditText
{
 /**
 Indicativo de atualização do campo.
 */
 private boolean
 ib_update;

 /**
 Construtor da classe.
 @param context
 @param attrs
 @param defStyle
 */
 public FieldMoney
 (
 Context context,
 AttributeSet attrs,
 int defStyle
 )
 {
 super
 (
 context,
 attrs,
 defStyle
 );

 //
 // Inicializa o componente de acordo com suas propriedades.
 //
 ComponenteInicializar();
 }

 /**
 Construtor da classe.
 @param context
 @param attrs
 */
 public FieldMoney
 (
 Context context,
 AttributeSet attrs
 )
 {
 super
 (
 context,
 attrs
 );

 //
 // Inicializa o componente de acordo com suas propriedades.
 //
 ComponenteInicializar();
 }

 /**
 Construtor da classe.
 @param context
 */
 public FieldMoney
 (
 Context context
 )
 {
 super(context);

 //
 // Inicializa o componente de acordo com suas propriedades.
 //
 ComponenteInicializar();
 }

 /**
 Inicializa o componente.
 */
 private
 void ComponenteInicializar()
 {
 //
 // Adiciona o evento de teclas.
 //
 this.setKeyListener(io_key_listener);

 //
 // Preenche o campo com a mascara a ser utilizada.
 //
 this.setText("Valor da Venda");

 //
 // Seta a seleção na primeira casa.
 //
 this.setSelection(1);

 //
 // Adiciona o listener de mudança no texto.
 //
 this.addTextChangedListener
 (
 new TextWatcher()
 {
 public
 void afterTextChanged
 (
 Editable s
 )
 {
 String
 ls_valor_original = s.toString();
//
// Trambique.
//
if (ib_update){ib_update = false;return;}
 //
 // Se o valor original for menor do que 16 posições.
 // Obs: (Permite apenas digitar 16 caracteres)
 //
 if (
 ls_valor_original.length()
 < 16
 )
 {
 //
 // Controlará mascara de edição do campo (casas decimais)
 //
 StringBuffer
 ls_mascara = new StringBuffer();

 //
 // Adiciona o valor original (contido no campo) com o próximo valor.
 // Permitindo apenas caracteres numéricos.
 //
 ls_mascara.append(ls_valor_original.replaceAll("[^0-9]*",""));

 //
 // Receberá um número LONG do campo até o momento.
 //
 Long
 ln_number = new Long(ls_mascara.toString());

 //
 // Faz o replace necessário para manipulação das casas decimais.
 //
 ls_mascara.replace
 (
 0,
 ls_mascara.length(),
 ln_number.toString()
 );

 //
 // Se conteudo menor do que 3.
 //
 if (
 ls_mascara.length() < 3
 )
 {
 //
 // Se o tamanho for de 1 posição.
 //
 if (
 ls_mascara.length() == 1
 )
 {
 //
 // Insere os caracteres em ordem.
 //
 ls_mascara.insert(0, "0").insert(0, ",").insert(0, "0");
 }

 //
 // Se o tamanho for de 2 posições.
 //
 else if (
 ls_mascara.length() == 2
 )
 {
 //
 // Insere os caracteres em ordem.
 //
 ls_mascara.insert(0, ",").insert(0, "0");
 }
 }

 //
 // Se tiver um tamanho maior que 3.
 //
 else
 {
 //
 // Insere a virgula.
 //
 ls_mascara.insert(ls_mascara.length()-2, ",");
 }

 //
 // Se tiver o tamanho de 6 posições.
 //
 if (
 ls_mascara.length() > 6
 )
 {
 //
 // Insere o ponto.
 //
 ls_mascara.insert(ls_mascara.length()-6, '.');

 //
 // Se o tamanho for maior do que 10.
 //
 if (
 ls_mascara.length() > 10
 )
 {
 //
 // Insere o ponto.
 //
 ls_mascara.insert(ls_mascara.length()-10, '.');

 //
 // Se for maior do que 14.
 //
 if (
 ls_mascara.length() > 14
 )
 {
 //
 // insere o ponto.
 //
 ls_mascara.insert(ls_mascara.length()-14, '.');
 }
 }
 }

 //
 // Define que o campo esta atualizando.
 //
 ib_update = true;

 //
 // Roda o novo valor.
 //
 FieldMoney.this.setText(ls_mascara);

 //
 // Faz a seleção.
 //
 FieldMoney.this.setSelection(ls_mascara.length());
 }
 }

 public
 void beforeTextChanged
 (
 CharSequence s,
 int start,
 int count,
 int after
 )
 {

 }

 public
 void onTextChanged
 (
 CharSequence s,
 int start,
 int before,
 int count
 )
 {

 }
 }
 );
 }

 private
 final KeylistenerNumber
 io_key_listener = new KeylistenerNumber();

 private
 class KeylistenerNumber
 extends NumberKeyListener
 {

 public
 int getInputType()
 {
 return InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;

 }

 @Override
 protected
 char[] getAcceptedChars()
 {
 return new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

 }
 }
}

O grande detalhe desta implementação é o NumberKeyListener presente no final do código. Isso fará transformar o teclado do celular em numérico quando o campo estiver em foco.

A implementação do TextWatcher -> afterTextChanged fará o campo monetário preencher os valores da direita para esquerda (Exemplo: 0,00 -> 0,05 -> 0,55 -> 5,52 …)

Depois de tudo pronto, para ativar este campo, basta acrescentar no xml da Activity o seguinte:

<pacote.FieldMoney
 android:id="@+id/valor_venda"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:lines="1"
 android:maxLength="20"
 android:inputType="numberDecimal" >
 <requestFocus />
</pacote.FieldMoney>

Espero que a minha codificação e minhas nomenclaturas não venham atrapalhar o seu estudo!

Tchauzin!

9º Dia (10/09/2011) – DroidBasket, Efeito Explosivo [part 3]

Bom dia!

Demorei 4 dias para escrever novamente. Pois é, nem sempre temos o tempo a nosso favor. Sim. Quer dizer, Não! não fiquei completamente parada sem tentar evoluir meu jogo, muito pelo contrário, passei grandes dificuldades para faze-lo funcionar após algumas mudanças na codificação.

Uma das coisas chatas ao se programar Android sem o emulador é a falta do DEBUG. Precisei instalar um programa chamado Catlog https://market.android.com/details?id=com.nolanlawson.logcat&hl=pt_BR para descobrir os erros de minha aplicação. É claro que não chega nem perto de um DEBUG step by step, mais como já dizia um dos filósofos malucos: “Ruim com ele, pior sem ele”.

Pois bem, o meu objetivo durante esses 4 dias era criar um efeito explosivo no jogo. Esse efeito aparecerá quando o robô ou bolinha (até então) colidir com o basket ou com a parede existente na parte de baixo da aplicação…

Primeiramente, precisei encontrar algumas imagens de explosão… e foi elas:

Eu fui tão preguiçosa mais tão preguiçosa que não fui capaz de alterar o nome das imagens. Enfim, não lembro onde encontrei essas imagens.

Para carrega-las no meu jogo, criei um método ImagemExplosionSet() na classe Robot.java:


/**
Seta a imagem de explosão.
*/
public
void            ImageExplosionSet
(
Bitmap            ao_image,
Bitmap            ao_image_1,
Bitmap            ao_image_2,
Bitmap            ao_image_3
)
{
io_explosions        =    new Bitmap[4];
io_explosions[0] = ao_image;
io_explosions[1] = ao_image_1;
io_explosions[2] = ao_image_2;
io_explosions[3] = ao_image_3;
}

E para usa-las em meu jogo, fiz algumas mudanças em meu método @override draw:


/**
Desenha os componentes.
*/
@Override
public
void            draw
(
Canvas            canvas
)
{
//
// Se não ocorreu colisão entre robo x parede ou robo x basket ...
//
if    (
io_vct_explosion.size()    ==    0
)
{
canvas.save();
canvas.drawBitmap(image, pos.getX(),pos.getY(), paint);
canvas.restore();
}
else
{
//
// Se não existe imagem carregada ...
//
if    (
io_explosions        ==    null
)
{
//
// Carrega todas as imagens.
//
ImageExplosionSet
(
BitmapFactory.decodeResource(io_resources, R.drawable.asteroid_explode1),
BitmapFactory.decodeResource(io_resources, R.drawable.asteroid_explode2),
BitmapFactory.decodeResource(io_resources, R.drawable.asteroid_explode3),
BitmapFactory.decodeResource(io_resources, R.drawable.asteroid_explode4)
);
}

//
// Desenha a explosão.
//
drawExplosion(canvas);
}
}

/**
Desenha a explosão do robô.
*/
public
void            drawExplosion
(
Canvas            ao_canvas
)
{
Explosion
ex            =    io_vct_explosion.remove(0);

try
{
for    (
int
i            =    0
;
i            <    io_explosions.length
;
i++
)
{
ao_canvas.drawBitmap
(
io_explosions[i], ex.mDrawX, ex.mDrawY-50, ex.paint
);
}
}
catch (Exception e)
{
//            Log.d(TAG, "Erro: "+e+" -------------> "+io_explosions + " --------------> "+ex+" ---------->>>> "+ao_canvas);
}
}

A primeira condição do método draw() é: if (io_vct_explosion.size() == 0)

Essa variável é alimentada no método processAI() apenas quando o robô colidir com o basket ou com a parede, já alimentando suas coordenadas x e y para carregar a imagem no local certo em drawExplosion:

@Override
public
void            processAI()
{
Rect
bounds            =    new Rect((int) pos.getX() - 5, (int) pos.getY() - 5, (int) pos.getX() + 5, (int) pos.getY() + 5);

for    (
Entity
ent            :    pieces.getPieces()
)
{
//
// Se não encontrou nenhum parade bloqueando ...
//
if    (
ent             ==    this
)
{
//
// Continua rodando a aplicação.
//
continue;
}

//
// Se encontrou algo blocante ...
//
if    (
ent            instanceof    BlockableEntity
)
{
//
// Se "esbarrou" a bolinha em algo ...
//
if    (
bounds.intersect(ent.getBounds())
)
{
//
// MUDA A ROTA DA BOLINHA.
//

Vector2D n = ((BlockableEntity) ent).getNormal();
// r = v-2 * v.dot( n ) * n
dir = dir.minus(n.multiply(2).multiply(dir.dot(n)));

//
// Se bateu no basket ...
//
if    (
ent        instanceof    Basket
)
{
//
// Define a velocidade máxima.
//
speed            =    999;

//
// Cria a explosão.
//
Explosion
ex            =    new Explosion();
ex.mAniIndex         =    0;
ex.mDrawX         =    pos.getX();
ex.mDrawY         =    pos.getY();
ex.paint        =    paint;
io_vct_explosion.add(ex);

//
// Notifica a batida no basket.
//
((Basket)ent).notifyHit();
}
//
// Se bateu na parede ...
//
else if    (
ent        instanceof    Wall
)
{
//
// Define a velocidade máxima.
//
speed            =    999;

//
// Cria a explosão.
//
Explosion
ex            =    new Explosion();
ex.mAniIndex         =    0;
ex.mDrawX         =    pos.getX();
ex.mDrawY         =    pos.getY();
ex.paint        =    paint;
io_vct_explosion.add(ex);

//
// Notifica a batida no basket.
//
((Wall)ent).notifyHit();
}

break;
}
}
}
move();
}

Aqui pode-se observar algo muito importante. Mesmo carregando a imagem da explosão e mostrando ela na tela, o robô irá mudar sua direção para um sentido reverso. E isso eu não quero. Foi então que eu tive duas opções: Remover o a imagem do robô ou aumentar sua velocidade reversa para uma velocidade infinita aos olhos humanos.

Podem perceber no código a variável speed. Ela possuí um valor normal até colidir com alguma coisa explosiva. Ou seja, quando o robô colidir com o basket ou parede, elevei o valor dessa variavel para 999. Não estou querendo dizer que essa é uma saida perfeita, pois não é. Mais foi a única coisa que me veio a cabeça as 7:00 horas da manhã de sabado.

Os fontes já estão no repositório, para baixarem basta entrar em: https://github.com/downloads/runthecode/DroidBasket/LinaFerreira-DroidBasketv1.2.2.rar

Tchauzin!

 

8º Dia (06/09/2011) – DroidBasket, Activity Secundaria [part 2]

Oi,

Dando continuidade ao game DroidBasket, hoje comecei a criar a tela de boas-vindas.

Como comentei no post realizado no dia 5 http://runthecode.wordpress.com/2011/08/31/5%C2%BA-dia-31082011-ligacao-xml/ a programação do Android é nada mais do que a junção/ligação de um arquivo XML com o código fonte.

Dei inicio então a criação do arquivo welcome.xml:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/welcome_text_view"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:gravity="center"
android:text="Bem vindo ao DroidBasket!"
>
</TextView>
<TextView android:id="@+id/welcome_text_view"
android:layout_width="fill_parent"
android:layout_height="60dip"
android:gravity="center"
android:text="COMO JOGAR"
>
</TextView>
<TextView android:id="@+id/welcome_text_view"
android:layout_width="fill_parent"
android:layout_height="70dip"
android:gravity="left"
android:text="- Cairá 100 robôs do céu em velocidade constante, porém em posições diferentes.
- Usando o touch deve-se juntar o máximo de robôs possíveis."
>
</TextView>
<TextView android:id="@+id/welcome_text_view"
android:layout_width="fill_parent"
android:gravity="center"
android:text="RESULTADO" android:layout_height="42dp">
</TextView>
<TextView android:id="@+id/welcome_text_view"
android:layout_width="fill_parent"
android:layout_height="120dip"
android:gravity="left"
android:text="
- Coletando 50 robôs, significa ter um bom reflexo e uma boa visão de jogo;
- Coletando um valor superior a 50 já é uma grande conquista;
- Coletando um valor abaixo de 50, o usuário terá que começar a praticar;"
>
</TextView>
<Button
android:id="@+id/welcome_ok_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Continuar"
>
</Button>
</TableLayout>

Por meio do Eclipse, pode-se visualizar como o arquiv XML está ficando, neste meu caso a telinha ficou assim:

Para ligar nosso xml com o código fonte e ai sim criar nossa Activity secundaria, foi necessário criar a classe WelcomeActivity.java no mesmo pacote da Activity principal (DroidBasketActivity):

//
// Pacote ao qual a classe pertence
//
package            br.techs.jpong;

//
// Importa as bibliotecas DroidBasket.
//
import             br.techs.R;
import             android.app.Activity;
import            android.os.Bundle;
import             android.view.View;
import             android.widget.Button;

public class        WelcomeActivity
extends        Activity
{
/**
Primeiro método a ser chamado após criar a Activity.
*/
public
void            onCreate
(
Bundle            savedInstanceState
)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);

Button
lo_btn_continuar    =    (Button)findViewById(R.id.welcome_ok_button);
lo_btn_continuar.setOnClickListener
(
new View.OnClickListener()
{
public
void            onClick
(
View            ao_view
)
{
finish();
}
}
);
}
}

Como optei por criar um botão <CONTINUAR> na tela de boas-vindas, nada mais justo do que criar um Listener para ele. Dentro deste Listener, foi implementado o método onClick(), no qual realiza a chamada da função finish() -> Responsável por “acabar” com a minha Activity secundaria.

Dentro de minha Acitivity principal (DroidBasketActivity) realizei a chamada da secundaria:

Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);

Logo, a classe completa ficou:

package            br.techs.jpong;

//
//Importa as bibliotecas DroidBasket.
//
import            br.techs.R;

//
//Importa as bibliotecas Android.
//
import             android.app.Activity;
import             android.content.Intent;
import             android.os.Bundle;
import             android.os.Handler;
import             android.os.Message;

public class         DroidBasketActivity
extends        Activity
{
/**
Variaveis de trabalho.
*/

private static final int
REFRESH            =    1;

private            Handler
io_gui_refresher;

private            ComplexGameView
io_game_view;

/**
Primeiro método a ser chamado após criar a Activity.
*/
public
void            onCreate
(
Bundle            savedInstanceState
)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.droidbasket);

Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);

//
// Recebe a VIEW do jogo.
//
io_game_view         =    (ComplexGameView)findViewById(R.id.gameView);

//
// Atualiza a tela do jogo.
//
io_gui_refresher     =    new Handler()
{
public
void            handleMessage
(
Message            msg
)
{
if    (
msg.what         ==    REFRESH
)
{
io_game_view.invalidate();
}
super.handleMessage(msg);
}
};
io_game_view.setCallbackHandler(io_gui_refresher);
}
}

Um detalhe extremamente importante para que tudo isso funcione: Não esquecer de adicionar a nossa Activity Secundaria no arquivo AndroidManifest.xml da seguinte maneira:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.techs" android:versionCode="1" android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:label="@string/app_name" android:name=".jpong.DroidBasketActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".jpong.WelcomeActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:label="@string/app_name">
</activity>
</application>
<uses-sdk android:minSdkVersion="7" />

<uses-feature android:name="android.hardware.touchscreen.multitouch"
android:required="true" />

</manifest>

Posso dizer que este pequeno detalhe me deixou horas e horas fritando aqui na maquina. rsrs

No fim, estou muito feliz com esse progresso! A cada dia que passa fico mais feliz em programar esse game.

Novos fontes disponíveis em: https://github.com/downloads/runthecode/DroidBasket/LinaFerreira-DroidBasketv1.2.0.rar

Tchauzin!

7º Dia (02/09/2011) – DroidBasket [part 1 ]

Bom dia,

Após passar praticamente 1 semana estudando o mundo Android, resolvi iniciar o desenvolvimento do jogo DroidBasket exposto no 6º dia.

Explicação do Jogo:

  • Cairá 100 robôs do céu em velocidade constante, porém em posições diferentes;
  • Usando o touch o usuário deverá juntar o máximo de robôs possíveis;

Objetivo do Jogo:

  • Conseguir coletar o máximo de robôs possíveis;

Resultado esperado:

  • Se coletar 50 robôs, significa ter um bom reflexo e uma boa visão;
  • Se conseguir coletar mais de 50 já é uma grande conquista;
  • Se coletar um valor abaixo de 50, o usuário terá que começar a praticar um pouco mais a cada dia, com o intuito de acelerar seus reflexos;

Como a ideia é a diversão, irei disponibilizar o fonte e explicar o que fiz até agora.

Antes de começarmos, gostaria de agradecer o portal pontoV http://pontov.com.br/ e meu amigo Marky que desenvolveu esse artigo http://pontov.com.br/site/java/69-android/247-android-desenhando-na-tela?q=%2Fjava%2F69-android%2F247-android-desenhando-na-tela&start=5 no qual vamos utiliza-lo para a construção deste game.

Então, antes de mais nada, é obrigatório realizar o download dos fontes do artigo:

https://github.com/MarkyVasconcelos/AndroidTechsGames

Vou mostrar agora quais foram as modificações realizadas em cada classe:

JPongActivity.java -> DroidBasketActivity.java

package            br.techs.jpong;

//
//Importa as bibliotecas DroidBasket.
//
import            br.techs.R;

//
//Importa as bibliotecas Android.
//
import             android.app.Activity;
import             android.os.Bundle;
import             android.os.Handler;
import             android.os.Message;

public class         DroidBasketActivity
extends        Activity
{
/**
Variaveis de trabalho.
*/

private static final int
REFRESH            =    1;

private            Handler
io_gui_refresher;

private            ComplexGameView
io_game_view;

/**
Primeiro método a ser chamado após criar a Activity.
*/
public
void            onCreate
(
Bundle            savedInstanceState
)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.droidbasket);

//
// Recebe a VIEW do jogo.
//
io_game_view         =

(ComplexGameView)findViewById(R.id.gameView);

//
// Atualiza a tela do jogo.
//
io_gui_refresher     =    new Handler()
{
public
void            handleMessage
(
Message            msg
)
{
if    (
msg.what         ==    REFRESH
)
{
io_game_view.invalidate();
}
super.handleMessage(msg);
}
};
io_game_view.setCallbackHandler(io_gui_refresher);
}
}

  • A principal modificação desta classe, foi retirar a chamada da Thread que faz referência a classe ComplexGameView.java

Motivo: Apenas Por preferencia e visualização, optei por criar uma Thread interna na  classe ComplexGameView.

Modificações na Classe ComplexGameView:

package         br.techs.jpong;

//
//Importa as bibliotecas DroidBasket.
//
import             br.techs.PiecesManager;
import             br.techs.R;
import            br.techs.hud.ScoreFinalboard;
import             br.techs.hud.Scoreboard;
import             br.techs.math.Vector2D;
import             br.techs.pieces.Robot;
import             br.techs.pieces.Basket;
import             br.techs.pieces.Basket.Axis;
import             br.techs.pieces.Wall;
import             java.util.Random;

//
//Importa as bibliotecas Android.
//
import             android.content.Context;
import             android.graphics.BitmapFactory;
import             android.graphics.Canvas;
import             android.graphics.Color;
import             android.graphics.Paint;
import             android.graphics.Rect;
import             android.os.Handler;
import             android.os.Message;
import             android.util.AttributeSet;
import             android.view.MotionEvent;
import             android.view.View;

public class         ComplexGameView
extends     View
implements    Runnable
{

/**
Variaveis de trabalho.
*/
//
// Tamanho da tela.
//
private            int
width            =    480,
height             =    800;

private         Paint
background;

private         PiecesManager
manager;

private            Basket
basket;

private            Wall
wall;

private            int
in_robot_count        =    0;

private            Handler
io_gui_refresher;

/**
Construtores.
*/

public            ComplexGameView
(
Context            context
)
{
super(context);
init();
}

public            ComplexGameView
(
Context         context,
AttributeSet         attrs
)
{
super(context, attrs);
init();
}

public            ComplexGameView
(
Context            context,
AttributeSet         attrs,
int             defStyle
)
{
super(context, attrs, defStyle);
init();
}

/**
Cria os componentes para a view.
*/
public
void             init()
{
//
// Pinta o fundo.
//
background         =    new Paint();
background.setColor(Color.BLACK);

//
// Armazena o gerenciador dos compoentes.
//
manager            =    new PiecesManager();

//
// Cria as 3 paredes.
//
// Se for a parede esquerda, esse vetor é (1, 0);
// Se for a direita (-1, 0);
// Se for a de cima (0, -1);
// E se for a de baixo (0, 1);
//
manager.addPiece(new Wall(new Rect(0, 0, 10, height),new Vector2D(1,

0))); // esquerda
manager.addPiece(new Wall(new Rect(width - 10, 0, width, height),new

Vector2D(-1, 0))); // direita

wall            =    new Wall(new Rect(0, height - 10, width,

height),new Vector2D(0, 1));
manager.addPiece(wall); // de baixo

//
// Cria o componente de captura para as bolinhas.
//
basket            =    new Basket(new Vector2D(0, 1),

Axis.X_AXIS, 700);
manager.addPiece(basket);
manager.addPiece(new Scoreboard(basket));

//
// Cria a tela responsável por criar as 100 bolinhas na tela.
//
new Thread()
{
//
// Define as prioridades da thread.
//
{
setDaemon(true);
}
@Override
public
void            run()
{
//
// Faz o procedimento até a thread ser interrompida.
//
while    (
basket.notifyHitGet()+wall.notifyHitGet()
<=    100
)
{
if    (
in_robot_count         %

100
==    0
)
{
//
// Cria o robo.
//
createRobot();
}

//
// Processa as informações.
//
manager.processAI();

//
// Atualiza o jogo.
//
Message
lo_message        =    new Message();
lo_message.what        =    1;
io_gui_refresher.sendMessage(lo_message);

try
{
//
// Aguarda um segundo para criar outro.
//
Thread.sleep(15);
}
catch (Exception    ex)
{
// Aconteceu alguma coisa.
}

//
// Conta o robot.
//
in_robot_count++;
}
}
}.start();
}

/**
Pinta os componentes.
*/
public
void             onDraw
(
Canvas             canvas
)
{
canvas.save();
canvas.drawRect(0, 0, getWidth(), getHeight(), background);
manager.draw(canvas);
canvas.restore();
}

/**
Cria o robozinho.
*/
private
void            createRobot()
{
//
// Cria um random para alterar a posição dos robos.
//
Random
rdm            =    new Random();

//
// Cria um novo vector com uma outra direção.
//
Vector2D
vec            =    new Vector2D(10 + rdm.nextInt(550), 20);

//
// Cria o novo robo.
//
Robot
lo_robot        =    new Robot
(
vec,
new Vector2D(0, 1),
15,
manager,
Color.rgb(50 + rdm.nextInt(206),

50 + rdm.nextInt(206), 50 + rdm.nextInt(206)),

BitmapFactory.decodeResource(getResources(), R.drawable.ball)
);
manager.addPiece(lo_robot);
}

/**
Evento de TOQUE.
*/
public
boolean             onTouchEvent(MotionEvent evt)
{
for    (
int
i            =    0
;
i            <    evt.getPointerCount()
;
i++
)
{
float
y            =    evt.getY(i);

//
// Movimenta o basket.
//
basket.notifyMotionEvent(evt.getX(i), evt.getY(i));
}

return true;
}

public
void            setCallbackHandler
(
Handler            ao_gui_refresher
)
{
io_gui_refresher        =    ao_gui_refresher;

}

@Override
public
void            run()
{

}
}

  • Retirada a parede (Wall) na parte de cima do Jogo;
  •  Alterado o nome da variavel PAD para Basket;
  •  Removido o uso de um dos Baskets disponiveis no jogo principal;
  •  Alterado o nome do método createBall() para createRobot();
  •  Alterando a direção das bolinhas no método createRobot() para cairem constantemente sem mudar a direção.
  •  Modificação no método onTouchEvent() para trabalhar apenas com 1 Basket;
  •  Criação da Thread responsável por criar as bolinhas do jogo e atualizar a view principal.

Mostrando a Thread antiga:

@Override
public void run() {
while (true) {
try {
if (loops % 500 == 0)
createBall();
manager.processAI();

Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);

Thread.sleep(10);
loops++;
} catch (Exception e) {
}
}
}

Apresentando a Thread nova:

//
// Cria a tela responsável por criar as 100 bolinhas na tela.
//
new Thread()
{
//
// Define as prioridades da thread.
//
{
setDaemon(true);
}
@Override
public
void            run()
{
//
// Faz o procedimento até a thread ser interrompida.
//
while    (
basket.notifyHitGet()+wall.notifyHitGet()
<=    100
)
{
if    (
in_robot_count         %

100
==    0
)
{
//
// Cria o robo.
//
createRobot();
}

//
// Processa as informações.
//
manager.processAI();

//
// Atualiza o jogo.
//
Message
lo_message        =    new Message();
lo_message.what        =    1;
io_gui_refresher.sendMessage(lo_message);

try
{
//
// Aguarda um segundo para criar outro.
//
Thread.sleep(15);
}
catch (Exception    ex)
{
// Aconteceu alguma coisa.
}

//
// Conta o robot.
//
in_robot_count++;
}
}
}.start();

  • A Thread irá parar quando cairem 100 robozinhos. Ou seja, A quantidade capturada pelo Basket somada com a quantidade de robozinhos que bateram na parede.

Pad.java -> Basket.java

package         br.techs.pieces;

//
//Importa as bibliotecas DroidBasket.
//
import             br.techs.math.Vector2D;

//
//Importa as bibliotecas Android.
//
import            android.graphics.Canvas;
import             android.graphics.Color;
import             android.graphics.Paint;
import             android.graphics.Rect;

public class        Basket
extends     BlockableEntity
{

/**
Variaveis de trabalho.
*/
private            Paint
io_wall_paint;

private            int
io_default_distance_from_axis;

private            Axis
io_this_axis;

private            int
in_basket_count        =    0;

/**
Construtores.
*/

public            Basket
(
Vector2D        normal,
Axis            axis,
int            defaultDistance
)
{
super(new Rect(), normal);
io_wall_paint        =    new Paint();
io_wall_paint.setColor(Color.GRAY);
io_this_axis        =    axis;
this.io_default_distance_from_axis
=    defaultDistance;
}

/**
Atualiza os movimentos dos basket.
@param x
@param y
*/
public
void            notifyMotionEvent
(
float            x,
float            y
)
{
io_this_axis.atualize(this, x, y);
}

/**
Pinta o desenho na "view".
*/
@Override
public
void            draw
(
Canvas            canvas
)
{
canvas.save();
canvas.drawRect(getBounds(), io_wall_paint);
canvas.restore();
}

public
enum            Axis
{
X_AXIS
{
@Override
void            atualize
(
Basket            pad,
float            x,
float            y
)
{
pad.setBounds
(
new Rect((int) x - 150,
pad.io_default_distance_from_axis,
(int) x-10,
pad.io_default_distance_from_axis + 10)
);
}
},
Y_AXIS
{
@Override
void            atualize
(
Basket            pad,
float            x,
float            y
)
{
pad.setBounds
(
new Rect(pad.io_default_distance_from_axis,
(int) y - 150, pad.io_default_distance_from_axis

+ 10,
(int) y + 150)
);
}

};
abstract
void            atualize
(
Basket            pad,
float            x,
float            y
);
}

/**
Conta a quantidade de vezes que o robo bate no basket.
*/
public
void            notifyHit()
{
in_basket_count++;
}

/**
@return
Retorna a quantidade de vezes que o robo bateu no basket.
*/
public
int            notifyHitGet()
{
return (in_basket_count);
}

@Override
public
void            processAI()
{
// Do nothing, position is handled by events
}
}

  • Criação dos métodos notifyHit() e notifyHitGet() responsáveis por contar a quantidade de robozinhos em contato com o Basket.
  • Diminuindo o tamanho do Basket no método Axis.

Modificações na classe Wall:

package br.techs.pieces;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import br.techs.math.Vector2D;

public class Wall extends BlockableEntity {
private Paint wallPaint;

private            int
in_basket_count        =    0;

public Wall(Rect bounds, Vector2D normal) {
super(bounds, normal);
wallPaint = new Paint();
wallPaint.setColor(Color.GRAY);
}

@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.drawRect(getBounds(), wallPaint);
canvas.restore();
}

@Override
public void processAI() {
// Do nothing, walls don't think
}

/**
Conta a quantidade de vezes que o robo bate no basket.
*/
public
void            notifyHit()
{
in_basket_count++;
}

/**
@return
Retorna a quantidade de vezes que o robo bateu no basket.
*/
public
int            notifyHitGet()
{
return (in_basket_count);
}

}

  • Remoção dos métodos addHitListener e notifyHit.
  • Criação dos métodos notifyHit() e notifyHitGet() responsáveis por contar a quantidade de robozinhos em contato com a parede (Wall).

Ball.java -> Robot.java

package            br.techs.pieces;

//
//Importa as bibliotecas DroidBasket.
//
import             br.techs.PiecesManager;
import             br.techs.math.Vector2D;

//
//Importa as bibliotecas Android.
//
import             android.graphics.Bitmap;
import             android.graphics.Canvas;
import             android.graphics.Color;
import             android.graphics.Paint;
import             android.graphics.Rect;

public class         Robot
extends        Entity
{
private            Paint
paint;

private            Vector2D
pos;

private         Vector2D
dir;

private         float
speed;

private         PiecesManager
pieces;

private            Bitmap
image;

public            Robot
(
Vector2D        pos,
Vector2D        dir,
float            speed
)
{
super(new Rect());

this.pos = pos;
this.dir = dir;
this.speed = speed;

paint = new Paint();
paint.setColor(Color.RED);
}

public            Robot
(
Vector2D        pos,
Vector2D        dir,
float            speed,
PiecesManager        manager,
int            color,
Bitmap            image
)
{
super(new Rect());

this.pos = pos;
this.dir = dir;
this.speed = speed;

paint = new Paint();
paint.setColor(color);

pieces = manager;
this.image = image;
}

public void setPiecesManager(PiecesManager manager) {
pieces = manager;
}

@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.drawBitmap(image, pos.getX(),pos.getY(), paint);
//        canvas.drawCircle(pos.getX(), pos.getY(), 10, paint);
canvas.restore();
}

private void move() {
pos.plusMe(dir.multiply(speed));
}

@Override
public
void            processAI()
{
Rect
bounds            =    new Rect((int) pos.getX() - 5, (int) pos.getY() - 5, (int) pos.getX() + 5, (int) pos.getY() + 5);

for    (
Entity
ent            :    pieces.getPieces()
)
{
//
// Se não encontrou nenhum parade bloqueando ...
//
if    (
ent             ==    this
)
{
//
// Continua rodando a aplicação.
//
continue;
}

//
// Se encontrou algo blocante ...
//
if    (
ent            instanceof    BlockableEntity
)
{
//
// Se "esbarrou" a bolinha em algo ...
//
if    (
bounds.intersect(ent.getBounds())
)
{
//
// MUDA A ROTA DA BOLINHA.
//

Vector2D n = ((BlockableEntity) ent).getNormal();
// r = v-2 * v.dot( n ) * n
dir = dir.minus(n.multiply(2).multiply(dir.dot(n)));

//
// Se bateu numa parede ...
//
if    (
ent        instanceof    Basket
)
{
((Basket)ent).notifyHit();
}
else if    (
ent        instanceof    Wall
)
{
((Wall)ent).notifyHit();
}

break;
}
}
}
move();
}
}

  •  Modificação no método processAI() para incrementar a contagem da captura do robô ao bater no Basket e na parede (Wall)

Bom, espero não ter esquecido de nada… No mais, retirei alguns código que achei desnecessários (que acredito ter passado despercebido) do tipo: for (int i = 0; i < 1; i++) na classe ComplexGameView.

Estou colocando a disposição o fonte nos links: https://github.com/downloads/runthecode/DroidBasket/LinaFerreira-DroidBasketv1.0.0.rar e http://www.megaupload.com/?d=NJK0ITQE

O próximo passo e fazer o robozinho (ou bolinha) se destruir ao bater no Basket ou na Parede. E também criar uma Acitivity mostrando o resultado e a pontuação.

Tchauzin!

6º Dia (01/09/2011) – Mudança de Planos

Oi,

Não vou postar nada novo em relação ao Android. Apenas vim dizer que mudei minha ideia de desenvolvimento.Resolvi descartar a hipótese de criar um ClientSocket e irei desenvolver um jogo (DroidBasket).

Como o meu principal objetivo é a diversão, nada mais justo do que começar criando um jogo!

A ideia inicial é fazer um chuva de Robozinhos onde o usuário (por meio de uma sacola) tenta captura-los.

Não terá níveis de dificuldades. Serão lançados 100 robozinhos em sequencia e em locais diferentes na tela, após cair todos os 100, o usuário terá um status de quantos foram capturados.

Então, vou postar diariamente os avanços nesse jogo e as minhas dificuldades. Conto com a ajuda de vocês para superar meus desafios!

Tchauzin!

5º Dia (31/08/2011) – Ligação XML

Oi,

Antes de colocar a “mão na massa” e começar a programar, nada mais justo do que estudar e conhecer os fundamentos do Android.

Vou falar uma verdade, estava tudo muito confuso na minha cabeça. Depois de ler muitos artigos e tutoriais comecei a compreender o funcionamento da coisa e vi que não era algo tão complicado assim.

Um aplicativo Android funciona por meio de arquivos XML. Sim! Arquivos XML. Primeiro você cria o arquivo “layout” de sua aplicação e depois via código fonte faz a ligação deste arquivo. Algo semelhante a uma prototipagem de uma tela feita no papel. Desenhamos a tela a caneta e depois a transformamos em código.

Para que tudo fique mais claro, vou postar um exemplo feito para Android:

Conforme pode-se notar acima, criei meu arquivo XML. (Semelhante a uma tela feita no papel). Neste XML declarei 2 componentes: TextView e Button.

Agora, vamos fazer a ligação deste arquivo XML com o meu código Java. Note que utilizei o método findViewById() para encontrar o botão no XML.

Na imagem acima, não coloquei a chamada do método startActivity(). A chamada deste, proporcionaria ao usuário ver a telinha criada:

Outros fundamentos que aprendi hoje

O que é uma Activity?

  • Uma Activity é nada mais do que uma tarefa executada por uma aplicação.;
  • É considerado um componente chave do Android;
  • Uma classe gerenciadora UI (Interface com o usuário)

Exemplos de uma tarefa?

  • Apresentação de uma tela de boas-vindas;
  • Uma lista itens;
  • A ação de um botão;

Componentes Android

O que é um TextView ?

  • Um texto que será exibido na tela através da propriedade android:text, cujo valor deverá estar mapeado pelo string.xml

O que é um Button ?

  • Representa um simples botão. Semelhante ou até mesmo igual a um JButton.

O que é um ImageButton?

  • Similar ao botão, porém, você adiciona uma imagem neste botão.

O que é um EditText?

  • Uma caixa onde o usuário pode digitar alguma informação. (JTextField)

O que é um CheckBox?

  • Um botão do tipo “caixa de seleção”, onde existe dois estados: marcado e não-marcado. (JCheckBox)

O que é um RadioGroup e RadioButton?

  • Este componente é parecido com o checkbox, porém, sua diferença é que ele não permite você selecionar mais de uma opção. Um radiobutton também tem dois estados, marcado e não-marcado. O radiogroup serve para agrudar estes radiobuttons, onde só é possível selecionar um radiobutton dos que existem dentro daquel radiogroup. Por exemplo: uma tela onde você deverá escolher entre sim e não, ou seja, deverá ser apenas uma opção.

O que é um ToggleButton?

  • Parecido como checkbox, onde você tem o estado marcado e não-marcado, porém, ele tem um indicador de luz mostrando qual é a opção atual.

Entendendo o XML

android:id

  • É o identificador do componente, que usaremos para chamá-lo de outro lugar.

android:layout_width

  • É onde você define como será a largura. Para preencher a tela inteira você deverá usar fill_parent, caso deseja que ele se adapte ao tamanho do conteúdo, utilize wrap_content.

android:layout_height

  • Similar ao android:layout_width, porém, você vai ajustar a altura do componente, seguindo o mesmo conceito.

android:text

  • É a propriedade que corresponde ao texto que deverá ser exibido no componente.

android:src

  • É usada para localizar a imagem que irá ser carregada no botão.

style

  • Local onde você define qual será o estilo do botão, no nosso caso, definimos que seria como uma estrela.

android:orientation

  • Onde definimos no RadioGroup, como será distribuídos os RadioButtons, verticalmente ou horizontalmente.

android:textOn

  • Atributo do ToggleButton que define qual texto será exibido quando a flag estiver marcada.

android:textOff

  • Atributo do ToggleButton que define qual texto será exibido quando a flag NÃO estiver marcada.

Viu? Não é tão complicado assim.

Tchauzin!

4º Dia (27/08/2011) – Hello, World

Oi,

Após ler o seguinte texto “As a developer, you know that the first impression of a development framework is how easy it is to write “Hello, World.” Well, on Android, it’s pretty easy. It’s particularly easy if you’re using Eclipse as your IDE, because we’ve provided a great plugin that handles your project creation and management to greatly speed up your development cycles.” em http://developer.android.com/resources/tutorials/hello-world.html resolvi tentar fazer o meu primeiro programa.

O objetivo deste programa será criar um Client Socket que irá conectar em um Server Socket que estará presente em uma maquina Desktop.

Algo meio complicado para o meu primeiro programa em Android né? rsrs

Quando criei meu pacote, foi criado uma estrutura totalmente diferente e não sei explicar o porque disso (ainda).

Para maiores detalhes, pode-se consultar o blog do Marky http://markytechs.wordpress.com/2011/01/24/android-estrutura-e-organizacao-da-aplicacao/

Quando criei minha classe AutomationRobot o método main não foi criado e isso foi estranho! Mais para tudo na vida existe uma explicação:

No padrão Android, qualquer aplicação pode usar o elemento de outra aplicação. Ou seja, se você quer capturar uma foto com o dispositivo da camera, ao invés de desenvolver isso, pode-se simplesmente usar o que já foi feito por um outro aplicativo, por meio das Activity. Portanto, ao contrário de aplicações Java, o Android não tem um único ponto de entrada.

Minha classe AutomationRobot:

Inicialmente, estava utilizando o método setContentView(R.layout.main); Como não sei como isso funciona, fiz essa modificação na classe.

Irei estudar a partir de agora o significado de algumas nomenclaturas e métodos do Andoid e postarei no meu próximo dia (próximo contato) e também mostrarei essa aplicação Emulada no dispositivo.

Tchauzin!