🧠 P02: Jogo da Memória Gamer POC (Guia Completo)
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!
✅ Pré-requisitos
Antes de começar, certifique-se de já ter estudado:
- 📘 Cap 01: Seu Primeiro App (Activity, XML e Views)
- 📘 Cap 02: Sorte e Decisão em Java (Variáveis, Operadores e Random)
- 🏗️ Projeto anterior: P01: Dado RPG (D20)
🎯 Objetivo do Jogo
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:
- Se forem iguais (mesmo número), as cartas são travadas e o jogador ganha um ponto.
- Se forem diferentes, as cartas revelam seus números temporariamente e voltam a exibir
?após 0.8 segundos (800ms), permitindo que o jogador tente memorizá-las. - Quando os 4 pares forem encontrados, o jogo exibe uma mensagem triunfal de vitória!
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
📖 Dicionário do Projeto
- GridLayout: Um tipo de layout que organiza os componentes da tela em formato de grade ou tabela (linhas e colunas). Ideal para tabuleiros.
- Handler: Uma classe do Android que nos permite enviar e processar mensagens ou executar blocos de código com agendamento de tempo (delays).
- postDelayed: Método do
Handlerusado para adiar a execução de uma ação por uma quantidade específica de milissegundos. - setTag / getTag: Mecanismo que permite anexar uma etiqueta invisível (como uma String contendo o número associado à carta) a um botão da tela. Usaremos isso para comparar se os números são iguais de forma simples e direta.
- Button: Componente visual clássico do Android usado para interagir por clique e exibir textos ou números dinâmicos.
🛠️ Passo 1: Configurando o Projeto no Android Studio
- Abra o Android Studio e clique em
New Project. - Escolha o template Empty Views Activity.
- Configure as informações do projeto:
- Name:
Memoria Gamer POC - Package Name:
br.com.curso.memoria - Language: Java (O clássico).
- Minimum SDK: API 24 (Android 7.0) ou superior.
- Name:
- Clique em Finish e aguarde a sincronização inicial do Gradle.
🎨 Passo 2: Desenhando o Tabuleiro (XML)
Nossa interface usará um elegante tema escuro gamer (#1A1A1A), textos em ciano neon (#03DAC5) e uma grade de botões cinzas estilizados.
- Abra o arquivo
app > src > main > res > layout > activity_main.xml. - Substitua o conteúdo pelo código XML a seguir:
<?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>
🧠 Passo 3: Programando as Regras e o Jogo (Java)
Vamos implementar toda a mecânica de pareamento. A estrutura lógica funciona assim:
- Declaração de Listas: Criamos uma lista de
Buttonpara mapear os botões da tela e uma lista deString(tags) para armazenar os valores correspondentes (números1,2,3,4duplicados). - Inicialização e Embaralhamento: Fazemos as conexões via
findViewById, preenchemos a lista com as tags numéricas e chamamosCollections.shuffle(tags)para misturar as cartas aleatoriamente. - Lógica do Clique:
- Exibe o número contido na tag da carta selecionada.
- Se for a primeira carta, guarda a referência.
- Se for a segunda carta, realiza a verificação de igualdade.
- Se forem iguais: Desabilita ambas as cartas (
setEnabled(false)) e incrementa o total de acertos. - Se forem diferentes: Ativa a flag de bloqueio (
aguardando = true), aguarda 800ms usando umHandlerpara voltar os textos para?e libera novos toques.
🛠️ Requisitos Críticos de Configuração
1. Flag do AndroidX
Confirme se o arquivo gradle.properties na raiz do seu projeto possui estas duas propriedades de compatibilidade ativas:
android.useAndroidX=true
android.enableJetifier=true
2. Formato de Dimensões em XML
Tamanhos de margens e padding (dp) no Android não aceitam casas decimais.
- ❌ Incorreto:
android:layout_margin="4.5dp" - ✅ Correto:
android:layout_margin="4dp"
📸 Resultado Esperado
Veja como sua tela deve ficar ao final deste projeto:

🏆 Desafios para você (Upgrade!)
Se você terminou de programar a mecânica base e tudo está funcionando, experimente estes upgrades:
- Contador de Cliques/Tentativas: Adicione um novo
TextViewna tela para mostrar quantas jogadas o usuário levou para vencer. - Tamanho do Tabuleiro: Aumente a grade para 12 botões (6 pares) mudando as colunas no XML e as tags correspondentes no Java.
📖 Gabarito Oficial de Código (Para Conferência)
Use os códigos completos abaixo para validar sua implementação ou corrigir problemas de compilação.
📄 Layout Completo (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>
📄 Lógica Java Completa (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();
}
}