Professional Documents
Culture Documents
Aula 6
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
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;
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);
}
}
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];
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