Gráficos 2D

Propaganda
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.
0x
FF
32
15
21
lue
B
e
n
e
e
r
G
,
Red
Mas o
.
s
o
i
v
b
ó
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)
{
ue a classe
q
O
super(context);
hicsView
p
a
r
G
}
desenha?
@Override
protected
void
onDraw(Canvas
canvas)
{
Path
circle
=
new
Path();
circle.addCircle(150,
150,
60,
DirecKon.CW);
Como usar
Paint
aPaint
=
new
Paint();
GraphicsView?
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);
}
}
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
}
obteríamos com
@Override
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>
sáu
o
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);
}
}
ue
q
m
E
eo
t
s
i
s
).show();
con
do
o
t
é
}
m
ame?
G
start
Sudoku.java
Usando
a
IDE
De que outras
formas
podemos usar
a IDE para
sermos mais
produtivos?
private
void
startGame(int
i)
{
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"
android:label="@string/game_Ktle"/>
AndroidManifest.xml
•  Criemos
também
algumas
strings
para
usarmos
em
nossa
implementação
de
Game.
<string
name="game_Ktle">Game</string>
<string
name="no_moves_label">No
moves</string>
<string
name="keypad_Ktle">Keypad</string>
strings.xml
Game.java
A
Classe
Game
public
class
Game
extends
AcKvity
{
Que dois
private
staKc
final
String
TAG
=
"Sudoku";
mentos
u
g
r
a
private
int
puzzle[]
=
new
int[9
*
9];
ses?
s
e
o
ã
s
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
definir o jogo
propriamente dito.
}
}
O que um “Game”
sabe fazer?
Quais são os passos
para criarmos o
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
{
public
staKc
final
int
DIFFICULTY_EASY
=
0;
public
staKc
final
int
DIFFICULTY_MEDIUM
=
1;
public
staKc
final
int
DIFFICULTY_HARD
=
2;
A classe
PuzzleView
ainda não
existe.
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);
puzzle
=
getPuzzle(diff);
Vários métodos
calculateUsedTiles();
ainda não
puzzleView
=
new
PuzzleView(this);
compilam.
setContentView(puzzleView);
puzzleView.requestFocus();
}
}
Game.java
A
Classe
Game
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
{
private
staKc
final
String
TAG
=
"Sudoku";
private
final
Game
game;
public
PuzzleView(Context
context)
{
super(context);
this.game
=
(Game)
context;
setFocusable(true);
setFocusableInTouchMode(true);
}
Agora precisamo
s
descobrir o
tamanho de um
quadrado do
sudoku. Como
fazemos isto?
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
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();
rá útil
e
s
e
t
r
a
p
a
s
Es
is
somente ma
ara
tarde, mas p
se
que serve es
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
que será
desenhado?
<?xml
version="1.0"
encoding="u@‐8"?>
<resources>
<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
ades
r
g
s
o
m
e
t
e
maiores
.
menores
Como
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();
ão
s
e
u
q
r
o
P
light.setColor(getResources().getColor(R.color.puzzle_light));
sempre
as
d
a
h
n
e
s
e
d
?
//
Draw
the
minor
grid
lines
s
a
h
n
i
l
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
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();
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);
}
Como
a
desenhar
or?
i
a
m
e
d
a
gr
Mas ainda
falta o mais
difícil:
desenhar os
números!
Grade
Maior
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
comandos
foreground.setStyle(Style.FILL);
estão
foreground.setTextSize(height
*
0.75f);
fazendo?
foreground.setTextScaleX(width
/
height);
foreground.setTextAlign(Paint.Align.CENTER);
O que é
aliasing?
Como
representar e
armazenar os
números?
E como
desenhar os
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
isso?
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);
}
ente
Método altam
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.
eiro
m
i
r
p
s
o
m
e
t
n
e
T
resolver o
mo
problema 1: co
a
marcar a célul
selecionada?
Como
colori
r
essa c
élula?
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.
PuzzleView.java
(ainda
no
método
onDraw)
Como
se
uma á lecionar
rea
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
select é
chamado?
private
void
select(int
x,
int
y)
{
invalidate(selRect);
selX
=
Math.min(Math.max(x,
0),
8);
selY
=
Math.min(Math.max(y,
0),
8);
getRect(selX,
selY,
selRect);
invalidate(selRect);
ngular
a
t
e
r
a
e
r
á
A
}
como um
Qual é o uso de
invalidate
nesse exemplo?
funciona
cebe
e
r
e
u
q
r
o
s
cur
ário.
u
s
u
o
d
s
o
t
even
ntar
e
m
i
v
o
m
o
Com
?
esse cursor
Movimentando
o
Cursor
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);
estado de um
break;
objeto?
case
KeyEvent.KEYCODE_DPAD_RIGHT:
select(selX
+
1,
selY);
break;
default:
return
super.onKeyDown(keyCode,
event);
}
return
true;
}
PuzzleView.java
Como
os
inserir
s?
o
r
e
m
ú
n
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;
oé
d
o
t
é
case
KeyEvent.KEYCODE_9:
setSelectedTile(9);
break;
m
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
método
v
e
s
es
e
u
q

misterioso para
a
Só
h
l
pi
a
a
r
a
implementarmos. p
ivos
t
i
s
o
p
s
i
d
têm
que não
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
aremos
F
d…
case
KeyEvent.KEYCODE_DPAD_CENTER:
a
P
y
e
K
show
er
v
s
game.showKeypadOrError(selX,
selY);
o
m
a
is. V
o
p
e
d
break;
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
a
h
c
a
s
ê
c
O que vo agora?
os
m
e
t
e
u
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
onde
r
o
P
.
e
de toqu
s?
o
m
a
ç
e
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
i
e
d
s
o
Rect
r
=
new
Rect();
Vam
odo
t
é
m
e
s
for
(int
i
=
0;
i
<
9;
i++)
{
es
pois
e
d
for
(int
j
=
0;
j
<
9;
j++)
{
a
r
a
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
Números inválidos:
Se o usuário tentar entrar
números inválidos, que já
foram usados na linha ou na
coluna, então façamos a tela
tremer por um segundo.
Idéias?!
Não é necessário
usar o
acelerômetro ou
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)
{
E o que
if
(game.setTileIfValid(selX,
selY,
Kle))
{
sse
e
a
i
r
e
s
invalidate();
?
recurso
}
else
{
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
anim/cycle_7.xml
se
s
e
e
u
q
O
ador
l
o
p
r
e
t
in
faz?
<?xml
version="1.0"
encoding="uo‐8"?>
<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.
ao
i
r
e
s
o
Com
essa
d
t
u
o
y
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"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="*">
<TableRow>
<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">
</Buron>
<Buron
android:id="@+id/keypad_9"
android:text="9">
</Buron>
</TableRow>
</TableLayout>
layout/keypad.xml
Layout
de
Tabelas
Quem “cria” o
Key Pad? Como
essa atividade é
invocada?
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
}
implementasomente aqueles
ção da classe
}
números que são válidos
para a posição.
KeyPad?
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);
mo é a
o
C
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);
}
Para que
setListeners();
Implementemos
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()
{
Keypad.java
public
void
onClick(View
v)
{
returnResult(t);
private
void
returnResult(int
Kle)
{
}
puzzleView.setSelectedTile(Kle);
});
dismiss();
}
}
keypad.setOnClickListener(new
View.OnClickListener()
{
public
void
onClick(View
v)
{
qual a
E
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
private
final
View
keys[]
=
new
View[9];
private
void
findViews()
{
keypad
=
findViewById(R.id.keypad);
keys[0]
=
findViewById(R.id.keypad_1);
keys[1]
=
findViewById(R.id.keypad_2);
keys[2]
=
findViewById(R.id.keypad_3);
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);
}
Agora devemos
concentrar-nos
na lógica do
jogo.
Primeiro,
precisamos
implementar
setTileIfValid
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?
}
}
E como seria
setTile(x,
y,
value);
a
implementaç
ão
calculateUsedTiles();
de
return
true;
getUsedTile
s?
}
setTileIfValid
Qual é o modelo
nosso
deprotected
boolean
setTileIfValid(int
x,
int
y,
int
value)
{
doku?
Su
int
Kles[]
=
getUsedTiles(x,
y);
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
Game.java
Não é problema
métodos
diferentes com
nomes iguais?
private
void
calculateUsedTiles()
{
for
(int
x
=
0;
x
<
9;
x++)
{
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];
//
horizontal
for
(int
i
=
0;
i
<
9;
i++)
{
if
(i
==
y)
con=nue;
int
t
=
getTile(x,
i);
if
(t
!=
0)
c[t
‐
1]
=
t;
}
//
verKcal
for
(int
i
=
0;
i
<
9;
i++)
{
if
(i
==
x)
con=nue;
int
t
=
getTile(i,
y);
if
(t
!=
0)
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++)
{
for
(int
j
=
starty;
j
<
starty
+
3;
j++)
{
if
(i
==
x
&&
j
==
y)
con=nue;
int
t
=
getTile(i,
j);
if
(t
!=
0)
c[t
‐
1]
=
t;
}
}
//
compress
int
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
nused++;
}
int
c1[]
=
new
int[nused];
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
c1[nused++]
=
t;
}
return
c1;
}
for
(int
i
=
0;
i
<
9;
i++)
{
if
(i
==
y)
conKnue;
int
t
=
getTile(x,
i);
if
(t
!=
0)
c[t
‐
1]
=
t;
}
Game.java
for
(int
i
=
0;
i
<
9;
i++)
{
if
(i
==
x)
conKnue;
int
t
=
getTile(i,
y);
if
(t
!=
0)
c[t
‐
1]
=
t;
}
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);
Como remover
if
(t
!=
0)
os zeros desse
c[t
‐
1]
=
t;
arranjo de
}
posições usadas?
}
private
int[]
calculateUsedTiles(int
x,
int
y)
{
int
c[]
=
new
int[9];
//
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++)
{
if
(i
==
x)
conKnue;
int
t
=
getTile(i,
y);
if
(t
!=
0)
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++)
{
for
(int
j
=
starty;
j
<
starty
+
3;
j++)
{
if
(i
==
x
&&
j
==
y)
conKnue;
int
t
=
getTile(i,
j);
if
(t
!=
0)
c[t
‐
1]
=
t;
}
}
//
compress
int
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
nused++;
}
int
c1[]
=
new
int[nused];
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
c1[nused++]
=
t;
}
return
c1;
}
Game.java
int
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
nused++;
}
int
c1[]
=
new
int[nused];
nused
=
0;
for
(int
t
:
c)
{
if
(t
!=
0)
c1[nused++]
=
t;
}
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
•  Precisamos
inicializar
o
tabuleiro
com
um
puzzle.
•  Este
pode
ser
fácil,
médio
ou
di‘cil.
•  Podemos
representar
esses
puzzles
como
strings.
private
final
String
easyPuzzle
=
"360000000004230800000004200"
+
"070460003820000014500013020"
+
"001900000007048300000000045";
private
final
String
mediumPuzzle
=
"650000070000506000014000005"
+
"007009000002314700000700800"
+
"500000630000201000030000097";
private
final
String
hardPuzzle
=
"009000000080605020501078000"
+
"000000700706040102004000000"
+
"000720903090301080000000600";
Game.java
O
Sudoku
Inicial
•  Por
que
representar
as
configurações
iniciais
de
puzzles
como
Strings,
e
não
como
arranjos
de
inteiros?
private
final
String
easyPuzzle
=
"360000000004230800000004200"
+
"070460003820000014500013020"
+
"001900000007048300000000045";
private
final
String
mediumPuzzle
=
"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:
puz
=
mediumPuzzle;
break;
case
DIFFICULTY_EASY:
default:
puz
=
easyPuzzle;
break;
}
return
fromPuzzleString(puz);
}
Agora, escreva um
método toPuzzleString,
que converta um arranjo
de inteiros, descrevendo
um puzzle, em uma
string.
Game.java
toPuzzleString
staKc
private
String
toPuzzleString(int[]
puz)
{
StringBuilder
buf
=
new
StringBuilder();
for
(int
element
:
puz)
{
buf.append(element);
}
Faça o caminho inverso
return
buf.toString();
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
i
esse projeto possu
ra.
falhas de arquitetu
Você conseguiria
identificá-las?
•  A
implementação
do
puzzle
está
muito
misturada
com
a
lógica
do
jogo.
•  Mas
isso
pode
não
ser
um
problema.
•  Reveja
Game.java
e
discuta
o
projeto

Download