You are on page 1of 79

Programação
para
a
Plataforma
Android
–
Aula
6


Gráficos
2D

•  Representando
cores

•  Desenhando
formas
simples

•  Lindando
com
eventos
de
baixo
nível

•  Como
criar
animações
simples?

•  Como
adicionar
um
key‐pad
à
aplicação?

•  Representando
iterações
entre
aKvidades

Cores

•  Android
representa
cores
como
inteiros
de
32

bits,
em
formato
hexadecimal.


, G r e e n e B lue
Red
Mas o
0x
FF
32
15
21
 sã o ó b v i o s .
?
quê é alpha

BLUE

GREEN

RED

ALPHA

Exemplos
de
Cores


0x3500FFFF
 0xFF00FFFF
 0xFF0000FF
 0x000000FF



View

•  A
classe
básica
que
descreve
elementos

gráficos
é
View.

•  Essa
classe
possui
o
método
onDraw,
que

determina
o
que
será
desenhado
na
tela
do

disposiKvo.

•  Esse
método
recebe
um
objeto
do
Kpo

Canvas,
que
representa
a
tela
to
disposiKvo.

Gráficos
de
Baixo
Nível

public
class
GraphicsView
extends
View
{




public
GraphicsView(Context
context)
{


O q ue a classe




super(context);

G r a phicsView


}




@Override
 desenha?


protected
void
onDraw(Canvas
canvas)
{






Path
circle
=
new
Path();





circle.addCircle(150,
150,
60,
DirecKon.CW);





Paint
aPaint
=
new
Paint();
 Como usar




aPaint.setColor(Color.LTGRAY);
 GraphicsView?




canvas.drawPath(circle,
aPaint);





Paint
bPaint
=
new
Paint();





bPaint.setColor(Color.BLUE);





canvas.drawTextOnPath("Mais
vale
um
pássaro
na
mão
que
dois
voando!",









circle,
0,
20,
bPaint);



}


}
 GraphicsView.java

Gráficos
de
Baixo
Nível


Como usar
GraphicsView?
ExampleGraphics.java


Views
são
usadas
como
Layouts


public
class
ExampleGraphics
extends
AcKvity
{



@Override



public
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





setContentView(new
GraphicsView(this));



}

}

O que quer dizer
passar “this”
para a nossa
View?
GraphicsView.java


Mais
um
Exemplo

public
class
GraphicsView
extends
View
{




public
GraphicsView(Context
context)
{






super(context);



}

 E o que


@Override
 obteríamos com


protected
void
onDraw(Canvas
canvas)
{

 esse comando?




Path
circle
=
new
Path();





circle.addRect(2,
2,
100,
100,
Direc=on.CW);





Paint
aPaint
=
new
Paint();





aPaint.setColor(Color.LTGRAY);





canvas.drawPath(circle,
aPaint);





Paint
bPaint
=
new
Paint();





bPaint.setColor(Color.BLUE);





canvas.drawTextOnPath("Mais
vale
um
pássaro
na
mão
que
dois
voando!",









circle,
0,
20,
bPaint);



}


}

Dica
Legal:
tamanho
da
janela

private int height;
private int width;

public GraphicsView(Context context) {


super(context);
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
display.getMetrics(metrics);
height = metrics.heightPixels;
width = metrics.widthPixels;
}
Mais
um
Exemplo

public
class
GraphicsView
extends
View
{




public
GraphicsView(Context
context)
{






super(context);



}




@Override



protected
void
onDraw(Canvas
canvas)
{






Path
circle
=
new
Path();





circle.addRect(2,
2,
100,
100,
Direc=on.CW);





Paint
aPaint
=
new
Paint();





aPaint.setColor(Color.LTGRAY);
 O que mais




canvas.drawPath(circle,
aPaint);
 podemos fazer




Paint
bPaint
=
new
Paint();
 com Path?




bPaint.setColor(Color.BLUE);





canvas.drawTextOnPath("Mais
vale
um
pássaro
na
mão
que
dois
voando!",









circle,
0,
20,
bPaint);



}


}

Drawables

•  Drawables
são
enKdades
que
podem
ser

visualizadas
na
tela
do
disposiKvo.

•  Em
geral
esses
recursos
ficam
armazenados
na

pasta
drawable.

•  Exemplos
incluem
figuras
(jpeg,
png,
svg),

padrões
de
cores,
desenhos
vetoriais,

camadas,
etc.

drawable/gradient.xml


Exemplo
de
Drawable

•  Esse
arquivo
será
usado
como
pano
de
fundo

em
nosso
exemplo
corrente:

<?xml
version="1.0"
encoding="uo‐8"?>

<shape
xmlns:android=




"hrp://schemas.android.com/apk/res/android">





<gradient
 O que esse






android:startColor="#FFFFFF”
 drawable






android:endColor="#808080"
 faz?






android:angle="270"
/>

</shape>

drawable/gradient.xml


Exemplo
de
Drawable

•  Esse
arquivo
será
usado
como
pano
de
fundo

em
nosso
exemplo
corrente:

<?xml
version="1.0"
encoding="uo‐8"?>

<shape
xmlns:android=




"hrp://schemas.android.com/apk/res/android">





<gradient







android:startColor="#FFFFFF”







android:endColor="#808080"







android:angle="270"
/>

</shape>
 o u sá-
E com
lo?
GraphicsView.java


Exemplo
de
Drawable

•  Esse
arquivo
será
usado
como
pano
de
fundo

em
nosso
exemplo
corrente:

public
class
GraphicsView
extends
View
{



public
GraphicsView(Context
context)
{





super(context);





setBackgroundResource(R.drawable.gradient);



}

}

ConKnuando
nosso
Sudoku

•  Até
agora
temos
um
aplicaKvo
que
contém
um

belo
layout
inicial,
e
uma
caixa
“about”!

Sudoku.java


Começando
um
Jogo
Novo

private
void
openNewGameDialog()
{



new
AlertDialog.Builder(this).setTitle(R.string.new_game_Ktle)





.setItems(







R.array.difficulty,
new
DialogInterface.OnClickListener()
{









public
void
onClick(DialogInterface
dialoginterface,
int
i)
{











startGame(i);









}







}
 m que
E teo




).show();
 con s is
}
 ét o do
m
G ame?
start
Sudoku.java

De que outras
Usando
a
IDE
 formas
podemos usar
a IDE para
sermos mais
private
void
startGame(int
i)
{
 produtivos?




Log.d(TAG,
"clicked
on
"
+
i);





Intent
intent
=
new
Intent(Sudoku.this,
Game.class);





intent.putExtra(Game.KEY_DIFFICULTY,
i);





startAcKvity(intent);

}


•  Esse
programa
não
irá
compilar,
pois
a
classe

Game
ainda
não
foi
definida.

–  Mas
podemos
defini‐la
facilmente,
usando
os

ganchos
deixados
pela
interface
de

desenvolvimento.

Setup
Inicial

•  A
classe
Game,
sendo
uma
aKvidade,
precisa

ser
registrada
no
manifesto.

<acKvity
android:name=".Game"
 AndroidManifest.xml





android:label="@string/game_Ktle"/>


•  Criemos
também
algumas
strings
para

usarmos
em
nossa
implementação
de
Game.

<string
name="game_Ktle">Game</string>
 strings.xml

<string
name="no_moves_label">No
moves</string>

<string
name="keypad_Ktle">Keypad</string>

Game.java


A
Classe
Game
public
class
Game
extends
AcKvity
{



private
staKc
final
String
TAG
=
"Sudoku";
 Que dois
a rg u mentos


private
int
puzzle[]
=
new
int[9
*
9];

s ã o e sses?


private
PuzzleView
puzzleView;



@Override



protected
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





Log.d(TAG,
"onCreate");





int
diff
=
getIntent().getIntExtra(KEY_DIFFICULTY,
DIFFICULTY_EASY);


Precisamos agora Quais são os passos


O que um “Game”
definir o jogo para criarmos o
sabe fazer?
propriamente dito. jogo?



}

}
 Ps.: Que bandeira e essa?
Game.java


A
Classe
Game
public
class
Game
extends
AcKvity
{



private
staKc
final
String
TAG
=
"Sudoku";



private
int
puzzle[]
=
new
int[9
*
9];



private
PuzzleView
puzzleView;



@Override



protected
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





Log.d(TAG,
"onCreate");





int
diff
=
getIntent().getIntExtra(KEY_DIFFICULTY,
DIFFICULTY_EASY);


Mas,
antes
de
proseguirmos,
por
que
estamos

representando
uma
matriz
desse
jeito?
Dica:
tem

a
ver
com
eficiência.



}

}

public
class
Game
extends
AcKvity
{
 A classe


public
staKc
final
int
DIFFICULTY_EASY
=
0;
 PuzzleView


public
staKc
final
int
DIFFICULTY_MEDIUM
=
1;
 ainda não


public
staKc
final
int
DIFFICULTY_HARD
=
2;
 existe.


private
staKc
final
String
TAG
=
"Sudoku";



private
int
puzzle[]
=
new
int[9
*
9];



private
PuzzleView
puzzleView;
 A
Classe
Game


@Override



protected
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





Log.d(TAG,
"onCreate");





int
diff
=
getIntent().getIntExtra(KEY_DIFFICULTY,
DIFFICULTY_EASY);





puzzle
=
getPuzzle(diff);

Vários métodos




calculateUsedTiles();

ainda não




puzzleView
=
new
PuzzleView(this);

compilam.




setContentView(puzzleView);





puzzleView.requestFocus();



}

}
 Game.java

PuzzleView

•  A
classe
PuzzleView
é
reponsável
por

desenhar
o
tabuleiro
do
jogo.


•  Qual
o
“super
Kpo”
dessa
classe?


•  O
que
ela
precisa
saber
fazer?

Puzzle
View

PuzzleView.java

public
class
PuzzleView
extends
View
{
 Agora precisamo
s


private
staKc
final
String
TAG
=
"Sudoku";
 descobrir o
tamanho de um


private
final
Game
game;

quadrado do
sudoku. Como


public
PuzzleView(Context
context)
{
 fazemos isto?




super(context);





this.game
=
(Game)
context;





setFocusable(true);





setFocusableInTouchMode(true);



}




protected
void
onSizeChanged(int
w,
int
h,
int
oldw,
int
oldh)
{}

}

PuzzleView.java


Computando
Pequenos
Quadrados

private
float
width;
//
width
of
one
Kle

private
float
height;
//
height
of
one
Kle

private
int
selX;
//
X
index
of
selecKon

private
int
selY;
//
Y
index
of
selecKon

private
final
Rect
selRect
=
new
Rect();


@Override

protected
void
onSizeChanged(int
w,
int
h,
int
oldw,
int
oldh)
{





width
=
w
/
9f;





height
=
h
/
9f;





getRect(selX,
selY,
selRect);





Log.d(TAG,
"onSizeChanged:
width
"
+
width
+
",
height
"
+
height);





super.onSizeChanged(w,
h,
oldw,
oldh);

}


private
void
getRect(int
x,
int
y,
Rect
rect)
{





rect.set((int)
(x
*
width),
(int)
(y
*
height),
(int)
(x
*
width
+
width),









(int)
(y
*
height
+
height));

}

PuzzleView.java


Marcando
a
Seleção

s a p a r t e s e rá útil
private
float
width;
//
width
of
one
Kle
 Es
is
private
float
height;
//
height
of
one
Kle
 somente ma
ara
private
int
selX;
//
X
index
of
selecKon
 tarde, mas p
se
private
int
selY;
//
Y
index
of
selecKon
 que serve es
private
final
Rect
selRect
=
new
Rect();
 código?
@Override

protected
void
onSizeChanged(int
w,
int
h,
int
oldw,
int
oldh)
{





width
=
w
/
9f;





height
=
h
/
9f;





getRect(selX,
selY,
selRect);





Log.d(TAG,
"onSizeChanged:
width
"
+
width
+
",
height
"
+
height);





super.onSizeChanged(w,
h,
oldw,
oldh);

}


private
void
getRect(int
x,
int
y,
Rect
rect)
{





rect.set((int)
(x
*
width),
(int)
(y
*
height),
(int)
(x
*
width
+
width),









(int)
(y
*
height
+
height));

}

O
Tabuleiro

sos
Quais os pas
o
envolvidos n
desenho do
tabuleiro?
Seria possível
estabelecermos
uma sequência
de passos para
esse desenho?

O que são as
cores dos
pequenos
retângulos?
PuzzleView.java


Desenhando
o
Tabuleiro

@Override

 Onde
protected
void
onDraw(Canvas
canvas)
{

 deveríamos




//
Desenhe
o
fundo...
 definir as




Paint
background
=
new
Paint();

 cores?




background.setColor(getResources().getColor(






R.color.puzzle_background));






canvas.drawRect(0,
0,
getWidth(),
getHeight(),
background);






//
Desenhe
as
raias...






//
Desenhe
os
números...






//
Desenhe
as
dicas...






//
Desenhe
a
área
selecionada…

}

values/colors.xml


Cores
como
recursos

E por
enquanto o
<?xml
version="1.0"
encoding="u@‐8"?>

que será
<resources>
 desenhado?




<color
name="background">#3500ffff</color>





<color
name="puzzle_background">#ffe6f0ff</color>





<color
name="puzzle_hilite">#ffffffff</color>





<color
name="puzzle_light">#64c6d4ef</color>





<color
name="puzzle_dark">#6456648f</color>





<color
name="puzzle_foreground">#ff000000</color>





<color
name="puzzle_hint_0">#64ff0000</color>





<color
name="puzzle_hint_1">#6400ff80</color>





<color
name="puzzle_hint_2">#2000ff80</color>





<color
name="puzzle_selected">#64ff8000</color>

</resources>

Desenhando
o
Tabuleiro


Como
desenhar a
grade do
sudoku?

e:
Lembre-s
e m o s g r ades
t
e
maiores
. Como
menores
desenha
ra
grade
menor?

PuzzleView.java


Auto‐Relevo

Paint
dark
=
new
Paint();

dark.setColor(getResources().getColor(R.color.puzzle_dark));

Paint
hilite
=
new
Paint();

hilite.setColor(getResources().getColor(R.color.puzzle_hilite));

Paint
light
=
new
Paint();
 q u e s ão
Po r
light.setColor(getResources().getColor(R.color.puzzle_light));

sempre
e se n h a d as
d ?
//
Draw
the
minor
grid
lines
 s l i n ha s
dua
for
(int
i
=
0;
i
<
9;
i++)
{





canvas.drawLine(0,
i
*
height,
getWidth(),
i
*
height,
light);





canvas.drawLine(0,
i
*
height
+
1,
getWidth(),
i
*
height
+
1,
hilite);





canvas.drawLine(i
*
width,
0,
i
*
width,
getHeight(),
light);





canvas.drawLine(i
*
width
+
1,
0,
i
*
width
+
1,
getHeight(),
hilite);

}

PuzzleView.java


Auto‐Relevo

Como
a
Paint
dark
=
new
Paint();

desenhar
dark.setColor(getResources().getColor(R.color.puzzle_dark));

a de m a i or?
Paint
hilite
=
new
Paint();
 gr
hilite.setColor(getResources().getColor(R.color.puzzle_hilite));

Paint
light
=
new
Paint();

light.setColor(getResources().getColor(R.color.puzzle_light));


//
Draw
the
minor
grid
lines

for
(int
i
=
0;
i
<
9;
i++)
{





canvas.drawLine(0,
i
*
height,
getWidth(),
i
*
height,
light);





canvas.drawLine(0,
i
*
height
+
1,
getWidth(),
i
*
height
+
1,
hilite);





canvas.drawLine(i
*
width,
0,
i
*
width,
getHeight(),
light);





canvas.drawLine(i
*
width
+
1,
0,
i
*
width
+
1,
getHeight(),
hilite);

}

Mas ainda
falta o mais
difícil:
Grade
Maior

desenhar os
números!

for
(int
i
=
0;
i
<
9;
i++)
{



canvas.drawLine(0,
i
*
height,
getWidth(),
i
*
height,
light);



canvas.drawLine(0,
i
*
height
+
1,
getWidth(),
i
*
height
+
1,
hilite);



canvas.drawLine(i
*
width,
0,
i
*
width,
getHeight(),
light);



canvas.drawLine(i
*
width
+
1,
0,
i
*
width
+
1,
getHeight(),
hilite);



if
(i
%
3
==
0)
{





canvas.drawLine(0,
i
*
height,
getWidth(),
i
*
height,
dark);





canvas.drawLine(0,
i
*
height
+
1,
getWidth(),
i
*
height
+
1,
hilite);





canvas.drawLine(i
*
width,
0,
i
*
width,
getHeight(),
dark);





canvas.drawLine(i
*
width
+
1,
0,
i
*
width
+
1,
getHeight(),
hilite);



}

}
 PuzzleView.java

PuzzleView.java


Preparando
terreno
para
os
números


Paint
foreground
=
new
Paint(Paint.ANTI_ALIAS_FLAG);

foreground.setColor(getResources().





getColor(R.color.puzzle_foreground));
 O que esses
foreground.setStyle(Style.FILL);
 comandos
estão
foreground.setTextSize(height
*
0.75f);

fazendo?
foreground.setTextScaleX(width
/
height);

foreground.setTextAlign(Paint.Align.CENTER);

Como
O que é representar e E como
aliasing? armazenar os desenhar os
números? números?
PuzzleView.java


Preparando
terreno
para
os
números


Paint
foreground
=
new
Paint(Paint.ANTI_ALIAS_FLAG);

foreground.setColor(getResources().





getColor(R.color.puzzle_foreground));

foreground.setStyle(Style.FILL);

foreground.setTextSize(height
*
0.75f);

foreground.setTextScaleX(width
/
height);

foreground.setTextAlign(Paint.Align.CENTER);


O que é
aliasing?
PuzzleView.java


Desenhando
Números

//
Draw
the
number
in
the
center
of
the
Zle

FontMetrics
fm
=
foreground.getFontMetrics();

//
Centering
in
X:
use
alignment
(and
X
at
midpoint)

float
x
=
width
/
2;

//
Centering
in
Y:
measure
ascent/descent
first
 Como ficaria o
float
y
=
height
/
2
‐
(fm.ascent
+
fm.descent)
/
2;
 tabuleiro sem
for
(int
i
=
0;
i
<
9;
i++)
{
 isso?




for
(int
j
=
0;
j
<
9;
j++)
{









canvas.drawText(this.game.getTileString(i,
j),
i
*
width
+
x,
j













*
height
+
y,
foreground);





}
 Método altam
ente
}
 da a
misterioso ain
ser escrito.
Aspect
RaKo

•  Esse
ajuste,
por

height / 2 -
(fm.ascent +
fm.descent) / 2,
é

necessário,
senão

estaríamos
colocando
a

base
da
fonte
no
centro
do

ladrilho.

O
que
temos
por
enquanto

•  Quatro
Kpos
de
linhas,

formando
dois
tamanhos

de
grades,
são

desenhados.

•  Números
são

desenhados
entre
as

grades.

•  Mas
ainda
falta
interagir

com
o
usuário.

•  E
falta
a
lógica
do
jogo!

Selecionando
Células

•  Seria
interessante
que
o
usuário
pudesse

selecionar
uma
célula
do
tabuleiro.

1.  A
célula
selecionada
deveria
ser
marcada
com

uma
cor
diferente.

2.  O
usuário
deveria
ser
capaz
de
mover
a
célula

selecionada.


n te m o s p r im eiro
T e
resolver o Como
mo
problema 1: co essa c
colori
r
a
marcar a célul élula?

selecionada?
Guardando
a
célula
selecionada

•  Retângulo
são
muito
importantes
em

programação
gráfica.

–  Android
possui
até
mesmo
uma
classe
para

representá‐los:
Rect.java.

Como
se
uma á lecionar
rea
PuzzleView.java
(ainda
no
método
onDraw)
 retang
ular?
Log.d(TAG,
"selRect="
+
selRect);

Paint
selected
=
new
Paint();

selected.setColor(getResources().getColor(R.color.puzzle_selected));

canvas.drawRect(selRect,
selected);

O que é esse
objeto mesmo?
Selecionando
Retângulos

PuzzleView.java

Como o método
private
void
select(int
x,
int
y)
{
 select é




invalidate(selRect);
 chamado?




selX
=
Math.min(Math.max(x,
0),
8);





selY
=
Math.min(Math.max(y,
0),
8);





getRect(selX,
selY,
selRect);





invalidate(selRect);

á r e a r e t angular
}
 A
funciona como um
s o r q u e r e cebe
cur ário.
t o s d o u s u
Qual é o uso de even
o m o v i m e ntar
invalidate Com
?
nesse exemplo? esse cursor
Como
Movimentando
o
Cursor
 inserir
n ú m e r
os
o s?
public
boolean
onKeyDown(int
keyCode,
KeyEvent
event)
{





Log.d(TAG,
"onKeyDown:
keycode="
+
keyCode
+
",
event="
+
event);





switch
(keyCode)
{





case
KeyEvent.KEYCODE_DPAD_UP:
 Essas






select(selX,
selY
‐
1);
 variáveis são






break;
 parte do




case
KeyEvent.KEYCODE_DPAD_DOWN:
 estado de






select(selX,
selY
+
1);

PuzzleView






break;





case
KeyEvent.KEYCODE_DPAD_LEFT:

O que é o






select(selX
‐
1,
selY);







break;

estado de um




case
KeyEvent.KEYCODE_DPAD_RIGHT:
 objeto?






select(selX
+
1,
selY);







break;





default:







return
super.onKeyDown(keyCode,
event);





}





return
true;



}
 PuzzleView.java

Inserindo
os
Números

PuzzleView.java

case
KeyEvent.KEYCODE_0:


case
KeyEvent.KEYCODE_SPACE:
setSelectedTile(0);
break;


case
KeyEvent.KEYCODE_1:
setSelectedTile(1);
break;


case
KeyEvent.KEYCODE_2:
setSelectedTile(2);
break;


case
KeyEvent.KEYCODE_3:
setSelectedTile(3);
break;


case
KeyEvent.KEYCODE_4:
setSelectedTile(4);
break;


case
KeyEvent.KEYCODE_5:
setSelectedTile(5);
break;


case
KeyEvent.KEYCODE_6:
setSelectedTile(6);
break;


case
KeyEvent.KEYCODE_7:
setSelectedTile(7);
break;


case
KeyEvent.KEYCODE_8:
setSelectedTile(8);
break;


case
KeyEvent.KEYCODE_9:
setSelectedTile(9);
break;

 m é t o d oé
Que
…

esse?
break;

Ladrinhos
válidos
e
inválidos

PuzzleView.java


public
void
setSelectedTile(int
Kle)
{





if
(game.setTileIfValid(selX,
selY,
Kle))
{









invalidate();


}
else
{









Log.d(TAG,
"setSelectedTile:
invalid:
"
+
Kle);





}

}
 E os
Argh… mais um
ai i s p o s i t ivos
método
es s e v d
e têm
misterioso para Só q u
pi l h a  que não
a
implementarmos. pa r a
teclas?
MúlKplos
DisposiKvos

PuzzleView.java

case
KeyEvent.KEYCODE_0:


case
KeyEvent.KEYCODE_SPACE:
setSelectedTile(0);
break;


case
KeyEvent.KEYCODE_1:
setSelectedTile(1);
break;


case
KeyEvent.KEYCODE_2:
setSelectedTile(2);
break;


case
KeyEvent.KEYCODE_3:
setSelectedTile(3);
break;


case
KeyEvent.KEYCODE_4:
setSelectedTile(4);
break;


case
KeyEvent.KEYCODE_5:
setSelectedTile(5);
break;


case
KeyEvent.KEYCODE_6:
setSelectedTile(6);
break;


case
KeyEvent.KEYCODE_7:
setSelectedTile(7);
break;


case
KeyEvent.KEYCODE_8:
setSelectedTile(8);
break;


case
KeyEvent.KEYCODE_9:
setSelectedTile(9);
break;


case
KeyEvent.KEYCODE_ENTER:

 o
Faremos d…
case
KeyEvent.KEYCODE_DPAD_CENTER:

 K e y Pa
game.showKeypadOrError(selX,
selY);

 show o s ver
o is. V a m
break;
 d e p
tela!
a nossa
Respiremos
um
Pouco

•  Vamos
definir
esse
método

game.getTileString
para

vermos
o
que
temos
até
aqui:

Game.java


public
String
getTileString(int
i,
int
j)
{





return
"F";

}
 m
c ês a ch a
O que vo agora?
u e t em os
q
Muitos
F’s!

•  Vamos
definir
esse
método

game.getTileString
para

vermos
o
que
temos
até
aqui:

Game.java


public
String
getTileString(int
i,
int
j)
{





return
"F";

}
 s agora
mo
Precisa eventos
m
lidar co o r onde
e . P
de toqu s?
eç a m o
com
Eventos
de
Toque

PuzzleView.java

@Override


public
boolean
onTouchEvent(MoKonEvent
event)
{




if
(event.getAcKon()
!=
MoKonEvent.ACTION_DOWN)






return
super.onTouchEvent(event);




select((int)
(event.getX()
/
width),




(int)
(event.getY()
/
height));




game.showKeypadOrError(selX,
selY);




Log.d(TAG,
"onTouchEvent:
x
"
+
selX
+
",
y
"
+
selY);




return
true;


Sudoku é um jogo
}
 bem difícil. Como
podemos ajudar o
usuário?
Implemente um
sistema de “dicas”.
Cores
de
Ajuda

•  Podemos
colorir
os

ladrilhos
de
forma

diferente,
dependendo

de
quantos
valores

possíveis
cada
ladrilho

pode
assumir.

Dicas

PuzzleView.java
(dentro
do
método
onDraw)

//
A
cor
da
dica
é
baseada
no
número
de
opções
restantes

Paint
hint
=
new
Paint();

int
c[]
=
{
getResources().getColor(R.color.puzzle_hint_0),



getResources().getColor(R.color.puzzle_hint_1),



getResources().getColor(R.color.puzzle_hint_2),
};
 xar
o s d e i
Rect
r
=
new
Rect();
 Vam
s e m é t odo
for
(int
i
=
0;
i
<
9;
i++)
{
 es


for
(int
j
=
0;
j
<
9;
j++)
{
 a r a d e pois
p
.




int
movesleŽ
=
9
‐
game.getUsedTiles(i,
j).length;
 também




if
(movesleŽ
<
c.length)
{







getRect(i,
j,
r);







hint.setColor(c[movesleŽ]);
 Mas o que






canvas.drawRect(r,
hint);
 vocês acham




}
 que ele faz?


}

}

Efeitos
Especiais


Idéias?!
Números inválidos:
Não é necessário
Se o usuário tentar entrar
números inválidos, que já usar o
foram usados na linha ou na acelerômetro ou
coluna, então façamos a tela
tremer por um segundo. qualquer coisa
desse tipo.!
Animações
Simples

•  Android
permite‐nos
definir
efeitos
de

animação
simples
em
XML.

•  Podemos
chamar
esses
efeitos
a
parKr
de

setSelectedTile:

PuzzleView.java

public
void
setSelectedTile(int
Kle)
{




if
(game.setTileIfValid(selX,
selY,
Kle))
{
 E o que
s e r ia e sse






invalidate();

?
}
else
{
 recurso






Log.d(TAG,
"setSelectedTile:
invalid:
"
+
Kle);







startAnimaKon(AnimaKonUKls.loadAnimaKon(game,
R.anim.shake));





}

}

Animação
em
XML

anim/shake.xml

<?xml
version="1.0"
encoding="uo‐8"?>

<translate





xmlns:android=







"hrp://schemas.android.com/apk/res/android"





android:fromXDelta="0"





android:toXDelta="10"





android:duraKon="1000"





android:interpolator="@anim/cycle_7"
/>


•  O
número
de
vezes,
e
a
aceleração

de
uma
animação
também
são

definidas
em
um
arquivo
XML.

RepeKções
da
Animação

q u e e s se
O
anim/cycle_7.xml

t e r p o l ador
in
<?xml
version="1.0"
encoding="uo‐8"?>
 faz?




<cycleInterpolator







xmlns:android=








"hrp://schemas.android.com/apk/res/android"







android:cycles="7"
/>


Ok, avancemos:
podemos ajudar o
usuário com um
teclado virtual.
Key
Pad


Tela de teclas:
A tela de teclas deve aparecer
sempre que o usuário apertar
o seletor do telefone.
A tela permite que o usuário
escolha o número que será
armazenado naquela posição.

o s e r i ao
Com
y o u t d essa
la
tela de
teclas?
<?xml
version="1.0"
encoding="u@‐8"?>

<TableLayout
xmlns:android="hdp://schemas.android.com/apk/res/android"

android:id="@+id/keypad"
android:orientaZon="verZcal"

Layout
de

android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:stretchColumns="*">

<TableRow>

Tabelas

<Buron
android:id="@+id/keypad_1"
android:text="1">

</Buron>

<Buron
android:id="@+id/keypad_2"
android:text="2">

</Buron>

<Buron
android:id="@+id/keypad_3"
android:text="3">

</Buron>

</TableRow>

<TableRow>

<Buron
android:id="@+id/keypad_4"
android:text="4">

</Buron>

<Buron
android:id="@+id/keypad_5"
android:text="5">

</Buron>

<Buron
android:id="@+id/keypad_6"
android:text="6">

</Buron>

</TableRow>

<TableRow>

<Buron
android:id="@+id/keypad_7"
android:text="7">

</Buron>

<Buron
android:id="@+id/keypad_8"
android:text="8">

Quem “cria” o
</Buron>
 Key Pad? Como
<Buron
android:id="@+id/keypad_9"
android:text="9">

</Buron>
 essa atividade é
</TableRow>
 invocada?
</TableLayout>
 layout/keypad.xml

Chamando
o
Keypad

Game.java

protected
void
showKeypadOrError(int
x,
int
y)
{





int
Kles[]
=
getUsedTiles(x,
y);





if
(Kles.length
==
9)
{









Toast
toast
=
Toast.makeText(this,
R.string.no_moves_label,











Toast.LENGTH_SHORT);









toast.setGravity(Gravity.CENTER,
0,
0);
 Para que








toast.show();
 serve esse




}
else
{
 código?








Dialog
v
=
new
Keypad(this,
=les,
puzzleView);









v.show();
 Como é a
Gostaríamos de habilitar




}
 implementa-
somente aqueles
}
 ção da classe
números que são válidos
KeyPad?
para a posição.
Keypad.java
 A
Classe
Key
Pad

public
class
Keypad
extends
Dialog
{





private
final
View
keys[]
=
new
View[9];





private
View
keypad;





private
final
int
useds[];





private
final
PuzzleView
puzzleView;





public
Keypad(Context
context,
int
useds[],
PuzzleView
puzzleView)
{









super(context);

C omo é a








this.useds
=
useds;
 -
implementa








this.puzzleView
=
puzzleView;

ção de




}

onCreate?




protected
void
onCreate(Bundle
savedInstanceState)
{









...





}





...

}

Keypad.onCreate()

Keypad.java

protected
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





setTitle(R.string.keypad_Ktle);





setContentView(R.layout.keypad);





findViews();





for
(int
element
:
useds)
{









if
(element
!=
0)













keys[element
‐
1].setVisibility(View.INVISIBLE);





}





setListeners();
 Implementemos Para que
}

os “escudadores serve esse
de eventos”. código?
Keypad.java

Listeners

private
void
setListeners()
{





for
(int
i
=
0;
i
<
keys.length;
i++)
{









final
int
t
=
i
+
1;









keys[i].setOnClickListener(new
View.OnClickListener()
{













public
void
onClick(View
v)
{
















returnResult(t);













}









});





}





keypad.setOnClickListener(new
View.OnClickListener()
{









public
void
onClick(View
v)
{
 E como seria a












returnResult(0);
 implementação








}
 de




});
 returnResult?
}

Listeners

private
void
setListeners()
{





for
(int
i
=
0;
i
<
keys.length;
i++)
{









final
int
t
=
i
+
1;









keys[i].setOnClickListener(new
View.OnClickListener()
{













public
void
onClick(View
v)
{

Keypad.java
















returnResult(t);
private
void
returnResult(int
Kle)
{













}
 



puzzleView.setSelectedTile(Kle);









});
 



dismiss();





}
 }





keypad.setOnClickListener(new
View.OnClickListener()
{









public
void
onClick(View
v)
{

E qual a












returnResult(0);

semântica de








}

dismiss()?




});

}

Associando
eventos
a
views

private
void
setListeners()
{





for
(int
i
=
0;
i
<
keys.length;
i++)
{









final
int
t
=
i
+
1;









keys[i].setOnClickListener(new
View.OnClickListener()
{













public
void
onClick(View
v)
{
 O que são esses















returnResult(t);
 objetos












}
 chamados








});
 “keys”?




}





keypad.setOnClickListener(new
View.OnClickListener()
{









public
void
onClick(View
v)
{













returnResult(0);









}





});

}
 Keypad.java

Views
e
Teclas

Keypad.java
 Agora devemos
private
final
View
keys[]
=
new
View[9];
 concentrar-nos
na lógica do
private
void
findViews()
{
 jogo.




keypad
=
findViewById(R.id.keypad);
 Primeiro,




keys[0]
=
findViewById(R.id.keypad_1);
 precisamos




keys[1]
=
findViewById(R.id.keypad_2);
 implementar




keys[2]
=
findViewById(R.id.keypad_3);
 setTileIfValid





keys[3]
=
findViewById(R.id.keypad_4);





keys[4]
=
findViewById(R.id.keypad_5);





keys[5]
=
findViewById(R.id.keypad_6);





keys[6]
=
findViewById(R.id.keypad_7);





keys[7]
=
findViewById(R.id.keypad_8);





keys[8]
=
findViewById(R.id.keypad_9);

}

setTileIfValid

Game.java

protected
boolean
setTileIfValid(int
x,
int
y,
int
value)
{






int
Kles[]
=
getUsedTiles(x,
y);






if
(value
!=
0)
{


Em poucas








for
(int
Kle
:
Kles)
{


palavras: o que












if
(Kle
==
value)


esse método
















return
false;

 está fazendo?








}






}






setTile(x,
y,
value);

 E como seria
a
implementaç




calculateUsedTiles();

 ão
de




return
true;

 getUsedTile
}
 s?
setTileIfValid

Qual é o modelo
deprotected
boolean
setTileIfValid(int
x,
int
y,
int
value)
{


nosso
Su



int
Kles[]
=
getUsedTiles(x,
y);


doku?




if
(value
!=
0)
{

Game.java









for
(int
Kle
:
Kles)
{


private
final
int
used[][][]
=
new
int[9][9][];














if
(Kle
==
value)


















return
false;


protected
int[]
getUsedTiles(int
x,
int
y)
{










}

 



return
used[x][y];






}

 }





setTile(x,
y,
value);






calculateUsedTiles();






return
true;


}

Inserindo
os
Números

Game.java

protected
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





Log.d(TAG,
"onCreate");





int
diff
=
getIntent().getIntExtra(KEY_DIFFICULTY,
DIFFICULTY_EASY);





puzzle
=
getPuzzle(diff);

Sempre que atualizamos




calculateUsedTiles();

o tabuleiro do jogo, o




puzzleView
=
new
PuzzleView(this);
 método onCreate é




setContentView(puzzleView);
 chamado, e é preciso




puzzleView.requestFocus();
 calcular quais números
}
 não são mais válidos
para cada célula. Como
fazer isso?
Calculando
ladrilhos
usados

Não é problema
Game.java
 métodos
private
void
calculateUsedTiles()
{
 diferentes com




for
(int
x
=
0;
x
<
9;
x++)
{
 nomes iguais?








for
(int
y
=
0;
y
<
9;
y++)
{













used[x][y]
=
calculateUsedTiles(x,
y);









}





}
 Como calcular quais
}
 ladrilhos não podem
ser usados para uma
certa posição do
Sudoku?

private
int[]
calculateUsedTiles(int
x,
int
y)
{





int
c[]
=
new
int[9];
 for
(int
i
=
0;
i
<
9;
i++)
{
 Game.java





//
horizontal





for
(int
i
=
0;
i
<
9;
i++)
{
 





if
(i
==
y)







if
(i
==
y)
 for
(int
i
=
0;
i
<
9;
i++)
{









con=nue;
 









conKnue;







int
t
=
getTile(x,
i);





if
(i
==
x)







if
(t
!=
0)







int
t
=
getTile(x,
i);









c[t
‐
1]
=
t;









conKnue;





}







if
(t
!=
0)





//
verKcal





for
(int
i
=
0;
i
<
9;
i++)
{
 



int
t
=
getTile(i,
y);







if
(i
==
x)
 









c[t
‐
1]
=
t;









con=nue;
 



if
(t
!=
0)







int
t
=
getTile(i,
y);
 }







if
(t
!=
0)









c[t
‐
1]
=
t;









c[t
‐
1]
=
t;





}





//
same
cell
block

}





int
startx
=
(x
/
3)
*
3;





int
starty
=
(y
/
3)
*
3;





for
(int
i
=
startx;
i
<
startx
+
3;
i++)
{





int
startx
=
(x
/
3)
*
3;







for
(int
j
=
starty;
j
<
starty
+
3;
j++)
{









if
(i
==
x
&&
j
==
y)
 



int
starty
=
(y
/
3)
*
3;











con=nue;









int
t
=
getTile(i,
j);
 



for
(int
i
=
startx;
i
<
startx
+
3;
i++)
{









if
(t
!=
0)











c[t
‐
1]
=
t;
 





for
(int
j
=
starty;
j
<
starty
+
3;
j++)
{







}





}





//
compress









if
(i
==
x
&&
j
==
y)





int
nused
=
0;





for
(int
t
:
c)
{











conKnue;







if
(t
!=
0)









nused++;
 







int
t
=
getTile(i,
j);
 Como remover




}





int
c1[]
=
new
int[nused];
 







if
(t
!=
0)
 os zeros desse




nused
=
0;





for
(int
t
:
c)
{
 









c[t
‐
1]
=
t;
 arranjo de






if
(t
!=
0)









c1[nused++]
=
t;





}







}
 posições usadas?




return
c1;



}





}


private
int[]
calculateUsedTiles(int
x,
int
y)
{





int
c[]
=
new
int[9];
 Game.java





//
horizontal





for
(int
i
=
0;
i
<
9;
i++)
{







if
(i
==
y)









conKnue;







int
t
=
getTile(x,
i);







if
(t
!=
0)









c[t
‐
1]
=
t;





}





//
verKcal





for
(int
i
=
0;
i
<
9;
i++)
{
 int
nused
=
0;







if
(i
==
x)









conKnue;







int
t
=
getTile(i,
y);

for
(int
t
:
c)
{







if
(t
!=
0)









c[t
‐
1]
=
t;
 

if
(t
!=
0)





nused++;





}





//
same
cell
block





int
startx
=
(x
/
3)
*
3;





int
starty
=
(y
/
3)
*
3;





for
(int
i
=
startx;
i
<
startx
+
3;
i++)
{

}







for
(int
j
=
starty;
j
<
starty
+
3;
j++)
{









if
(i
==
x
&&
j
==
y)











conKnue;









int
t
=
getTile(i,
j);









if
(t
!=
0)

int
c1[]
=
new
int[nused];











c[t
‐
1]
=
t;







}
 nused
=
0;

for
(int
t
:
c)
{





}





//
compress





int
nused
=
0;





for
(int
t
:
c)
{







if
(t
!=
0)



if
(t
!=
0)









nused++;





}
 



c1[nused++]
=
t;





int
c1[]
=
new
int[nused];





nused
=
0;





for
(int
t
:
c)
{

}







if
(t
!=
0)









c1[nused++]
=
t;
 return
c1;





}





return
c1;



}

Game.java


Representando
o
Puzzle

•  Podemos
representar
o
puzzle
como
um

arranjo
de
tamanho
9
×
9:



private
int
puzzle[]
=
new
int[9
*
9];


Nesse caso, como


implementar os
métodos:
getTile(x, y) e
?
setTile(x, y, value)
Game.java


Representando
o
Puzzle

•  Podemos
representar
o
puzzle
como
um

arranjo
de
tamanho
9
×
9:



private
int
puzzle[]
=
new
int[9
*
9];


private
int
getTile(int
x,
int
y)
{





return
puzzle[y
*
9
+
x];

}


private
void
setTile(int
x,
int
y,
int
value)
{





puzzle[y
*
9
+
x]
=
value;

}

Game.java


O
Sudoku
Inicial

private
final
String
easyPuzzle
=


•  Precisamos
inicializar
 "360000000004230800000004200"
+


o
tabuleiro
com
um
 "070460003820000014500013020"
+


"001900000007048300000000045";


puzzle.

•  Este
pode
ser
fácil,
 private
final
String
mediumPuzzle
=


"650000070000506000014000005"
+


médio
ou
di‘cil.
 "007009000002314700000700800"
+


•  Podemos
representar
 "500000630000201000030000097";



esses
puzzles
como
 private
final
String
hardPuzzle
=


strings.
 "009000000080605020501078000"
+


"000000700706040102004000000"
+


"000720903090301080000000600";

Game.java


O
Sudoku
Inicial

private
final
String
easyPuzzle
=


•  Por
que
representar
as
 "360000000004230800000004200"
+


configurações
iniciais
 "070460003820000014500013020"
+


"001900000007048300000000045";


de
puzzles
como

Strings,
e
não
como
 private
final
String
mediumPuzzle
=


arranjos
de
inteiros?
 "650000070000506000014000005"
+


"007009000002314700000700800"
+


"500000630000201000030000097";



private
final
String
hardPuzzle
=


"009000000080605020501078000"
+


"000000700706040102004000000"
+


"000720903090301080000000600";

Game.java


Obtendo
um
puzzle
novo

private
int[]
getPuzzle(int
diff)
{




String
puz;




switch
(diff)
{






case
DIFFICULTY_HARD:








puz
=
hardPuzzle;








break;






case
DIFFICULTY_MEDIUM:

 Agora, escreva um






puz
=
mediumPuzzle;

 método toPuzzleString,






break;

 que converta um arranjo




case
DIFFICULTY_EASY:

 de inteiros, descrevendo




default:

 um puzzle, em uma






puz
=
easyPuzzle;

 string.






break;




}




return
fromPuzzleString(puz);


}

Game.java


toPuzzleString


staKc
private
String
toPuzzleString(int[]
puz)
{




StringBuilder
buf
=
new
StringBuilder();



for
(int
element
:
puz)
{





buf.append(element);



}



return
buf.toString();
 Faça o caminho inverso
}
 agora: escreva um
método que converta
uma string em um
arranjo de inteiros.
Game.java


fromPuzzleString


staKc
protected
int[]
fromPuzzleString(String
string)
{




int[]
puz
=
new
int[string.length()];




for
(int
i
=
0;
i
<
puz.length;
i++)
{






puz[i]
=
string.charAt(i)
‐
'0';




}




return
puz;


}

PuzzleView.java


getTileString(x,
y)

float
x
=
width
/
2;

float
y
=
height
/
2
‐
(fm.ascent
+
fm.descent)
/
2;

for
(int
i
=
0;
i
<
9;
i++)
{





for
(int
j
=
0;
j
<
9;
j++)
{









canvas.drawText(this.game.getTileString(i,
j),
i
*
width
+
x,
j













*
height
+
y,
foreground);





}
 Implemente o método
}
 getTileString, que retorna
uma string descrevendo o
número em uma posição
do tabuleiro, ou a string
vazia se aquela posição
estiver vaga.
Game.java


getTileString(x,
y)


protected
String
getTileString(int
x,
int
y)
{





int
v
=
getTile(x,
y);





if
(v
==
0)









return
"";





else









return
String.valueOf(v);

}

O
Projeto
de
SoŽware

ue
Poder-se-ia dizer q
i
esse projeto possu
ra.
falhas de arquitetu
Você conseguiria
identificá-las?
O
Projeto
de
SoŽware

ue
Poder-se-ia dizer q •  A
implementação
do

i
esse projeto possu
falhas de arquitetu
ra. puzzle
está
muito

Você conseguiria misturada
com
a

identificá-las?
lógica
do
jogo.

•  Mas
isso
pode
não
ser

um
problema.

•  Reveja
Game.java
e

discuta
o
projeto


You might also like