📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Especialização em Android Nativo. Recomendado revisar antes de começar.
Neste projeto, você vai subir de nível! O desafio é criar um Jogo da Memória Gamer clássico simplificado em formato de Prova de Conceito (POC) com 8 cartas (4 pares). Você aprenderá a lidar com lógica de comparação de estados e controle de tempo com atraso programado sem precisar de nenhuma imagem ou recurso externo.
Usaremos números (1, 2, 3, 4) para representar as cartas e o símbolo ? para representar as cartas viradas para baixo!
Antes de começar, certifique-se de já ter estudado:
Criar uma grade de 4 linhas por 2 colunas com botões exibindo o símbolo ? (verso da carta). O jogador deve clicar em dois botões:
? após 0.8 segundos (800ms), permitindo que o jogador tente memorizá-las.💡 O mesmo conceito em outra stack: este é o clássico Jogo da Memória — o mesmo desafio (comparar estados, temporizar) também aparece em Java Swing puro no curso Sistemas com Java.
graph TD
A[Início / Embaralhar] --> B[Clique na Carta 1]
B --> C[Revelar Número 1]
C --> D[Clique na Carta 2]
D --> E[Revelar Número 2]
E --> F{Os números são iguais?}
F -- Sim --> G[Travar as cartas & Somar Ponto]
F -- Não --> H[Aguardar 800ms & Voltar para ?]
G --> I{Todos os 4 pares encontrados?}
H --> B
I -- Sim --> J[🏆 Vitória Gamer!]
I -- Não --> B
Handler usado para adiar a execução de uma ação por uma quantidade específica de milissegundos.New Project.Memoria Gamer POCbr.com.curso.memoriaNossa interface usará um elegante tema escuro gamer (#1A1A1A), textos em ciano neon (#03DAC5) e uma grade de botões cinzas estilizados.
app > src > main > res > layout > activity_main.xml.<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="8dp"
android:background="#1A1A1A">
<!-- Título Gamer -->
<TextView
android:id="@+id/txt_titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MEMÓRIA GAMER"
android:textColor="#03DAC5"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="4dp" />
<!-- Texto de Status das Ações -->
<TextView
android:id="@+id/txt_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Encontre os pares!"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:layout_marginBottom="12dp" />
<!-- Grade 4x2 para 8 cartas -->
<GridLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="2"
android:rowCount="4"
android:layout_marginBottom="20dp">
<Button android:id="@+id/card1" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card2" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card3" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card4" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card5" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card6" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card7" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card8" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
</GridLayout>
<!-- Botão de Reiniciar -->
<Button
android:id="@+id/btn_reiniciar"
android:layout_width="180dp"
android:layout_height="50dp"
android:text="REINICIAR JOGO"
android:backgroundTint="#03DAC5"
android:textColor="#1A1A1A"
android:textStyle="bold" />
</LinearLayout>
Vamos implementar toda a mecânica de pareamento. A estrutura lógica funciona assim:
Button para mapear os botões da tela e uma lista de String (tags) para armazenar os valores correspondentes (números 1, 2, 3, 4 duplicados).findViewById, preenchemos a lista com as tags numéricas e chamamos Collections.shuffle(tags) para misturar as cartas aleatoriamente.setEnabled(false)) e incrementa o total de acertos.aguardando = true), aguarda 800ms usando um Handler para voltar os textos para ? e libera novos toques.Confirme se o arquivo gradle.properties na raiz do seu projeto possui estas duas propriedades de compatibilidade ativas:
android.useAndroidX=true
android.enableJetifier=true
Tamanhos de margens e padding (dp) no Android não aceitam casas decimais.
android:layout_margin="4.5dp"android:layout_margin="4dp"Veja como sua tela deve ficar ao final deste projeto:

Se você terminou de programar a mecânica base e tudo está funcionando, experimente estes upgrades:
TextView na tela para mostrar quantas jogadas o usuário levou para vencer.Use os códigos completos abaixo para validar sua implementação ou corrigir problemas de compilação.
activity_main.xml)Disponível em: app/src/main/res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="8dp"
android:background="#1A1A1A">
<TextView
android:id="@+id/txt_titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MEMÓRIA GAMER"
android:textColor="#03DAC5"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="4dp" />
<TextView
android:id="@+id/txt_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Encontre os pares!"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:layout_marginBottom="12dp" />
<!-- Grade 4x2 para 8 cartas -->
<GridLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="2"
android:rowCount="4"
android:layout_marginBottom="20dp">
<Button android:id="@+id/card1" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card2" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card3" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card4" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card5" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card6" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card7" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
<Button android:id="@+id/card8" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="4dp" android:backgroundTint="#2C2C2C" android:textColor="#03DAC5" android:textSize="32sp" android:textStyle="bold" android:text="?" />
</GridLayout>
<Button
android:id="@+id/btn_reiniciar"
android:layout_width="180dp"
android:layout_height="50dp"
android:text="REINICIAR JOGO"
android:backgroundTint="#03DAC5"
android:textColor="#1A1A1A" />
</LinearLayout>
MainActivity.java)Localizado em: app/src/main/java/br/com/curso/memoria/MainActivity.java
package br.com.curso.memoria;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MainActivity extends AppCompatActivity {
// Controles de estado das seleções de cartas
private Button primeiraCarta = null;
private Button segundaCarta = null;
private boolean aguardando = false; // Bloqueia cliques rápidos durante o delay
private int paresEncontrados = 0;
private TextView txtStatus;
// Listas para gerenciar dinamicamente as referências de tela e valores
private final List<Button> cards = new ArrayList<>();
private final List<String> tags = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Vinculando componentes de texto e ações
txtStatus = findViewById(R.id.txt_status);
Button btnReiniciar = findViewById(R.id.btn_reiniciar);
btnReiniciar.setOnClickListener(v -> reiniciarJogo());
// 1. Inicializando as referências dos 8 botões (cartas) do tabuleiro
cards.add(findViewById(R.id.card1));
cards.add(findViewById(R.id.card2));
cards.add(findViewById(R.id.card3));
cards.add(findViewById(R.id.card4));
cards.add(findViewById(R.id.card5));
cards.add(findViewById(R.id.card6));
cards.add(findViewById(R.id.card7));
cards.add(findViewById(R.id.card8));
// 2. Definindo a lista de números (4 pares = 8 elementos)
tags.add("1");
tags.add("2");
tags.add("3");
tags.add("4");
tags.add("1");
tags.add("2");
tags.add("3");
tags.add("4");
// Dá o pontapé inicial no jogo
iniciarJogo();
}
/**
* Reseta as pontuações, embaralha o tabuleiro e redefine as cartas
*/
private void iniciarJogo() {
paresEncontrados = 0;
txtStatus.setText("Encontre os pares!");
// Embaralha os números aleatoriamente
Collections.shuffle(tags);
// Configura cada Button individualmente
for (int i = 0; i < cards.size(); i++) {
Button card = cards.get(i);
// Atribui o número (tag) para a carta
card.setTag(tags.get(i));
card.setEnabled(true); // Garante que a carta possa ser clicada
card.setText("?"); // Mostra a interrogação (verso)
// Configura o ouvinte de clique
card.setOnClickListener(v -> aoClicarNaCarta(card));
}
}
/**
* Executa a regra ao clicar em uma carta
*/
private void aoClicarNaCarta(Button carta) {
// Ignora cliques se o jogo estiver aguardando o delay de erro
// ou se o usuário clicar duas vezes seguidas na mesma carta
if (aguardando || carta == primeiraCarta) return;
revelarCarta(carta);
if (primeiraCarta == null) {
// Primeiro clique da jogada
primeiraCarta = carta;
} else {
// Segundo clique da jogada: realiza a verificação
segundaCarta = carta;
verificarPar();
}
}
/**
* Revela o número do Button lendo-o a partir da tag associada
*/
private void revelarCarta(Button carta) {
String tag = (String) carta.getTag();
carta.setText(tag);
}
/**
* Verifica se os dois cards revelados possuem a mesma tag numérica
*/
private void verificarPar() {
if (primeiraCarta.getTag().equals(segundaCarta.getTag())) {
// SUCESSO: Par encontrado!
paresEncontrados++;
// Trava as cartas para não receberem mais cliques
primeiraCarta.setEnabled(false);
segundaCarta.setEnabled(false);
Toast.makeText(this, "Par encontrado! 🎉", Toast.LENGTH_SHORT).show();
resetarSelecao();
verificarVitoria();
} else {
// ERRO: Cartas diferentes. Aguarda 800ms antes de virá-las de volta
aguardando = true;
new Handler().postDelayed(() -> {
// Vira de volta para a interrogação
primeiraCarta.setText("?");
segundaCarta.setText("?");
resetarSelecao();
aguardando = false; // Libera novos cliques
}, 800);
}
}
/**
* Valida se todos os pares foram achados
*/
private void verificarVitoria() {
if (paresEncontrados == 4) {
txtStatus.setText("🏆 VOCÊ É UM MESTRE!");
Toast.makeText(this, "Incrível! Você encontrou todos os pares! 🎮", Toast.LENGTH_LONG).show();
}
}
/**
* Limpa as referências das cartas selecionadas na rodada
*/
private void resetarSelecao() {
primeiraCarta = null;
segundaCarta = null;
}
/**
* Reinicia a partida do zero
*/
private void reiniciarJogo() {
resetarSelecao();
aguardando = false;
iniciarJogo();
}
}
—`
No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd android_p02_memoria_poc
./gradlew build
./gradlew test
# ou abrir no Android Studio e clicar em Run (Shift+F10)
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar: cd proj_aplicacoes_full_stack/projetos/android_p02_memoria_poc