Files
SAE41_2025/src/main/java/sae/chuzzle/VueGrille.java
T

307 lines
11 KiB
Java
Raw Normal View History

2026-03-19 10:22:48 +01:00
package sae.chuzzle;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
public class VueGrille extends View {
// =========================================================
// Constantes
// =========================================================
private static final int NB_LIGNES = 6;
private static final int NB_COLONNES = 6;
private static final int NB_TYPES = 7;
/** Symboles pour le mode daltonien, un par type. */
private static final String[] SYMBOLES = {"", "", "", "", "", "", ""};
// =========================================================
// Données métier
// =========================================================
/** Grille reçue depuis MainActivity (tableau brut). */
private int[][] grille = new int[NB_LIGNES][NB_COLONNES];
/** Verrous reçus depuis MainActivity. */
private boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES];
private boolean modeDaltonien = false;
// =========================================================
// État de l'animation de glissement
// =========================================================
/**
* true = on anime une ligne,
* false = on anime une colonne,
* null = pas d'animation en cours.
*/
private Boolean animEstLigne = null;
/** Index de la ligne ou colonne en cours de glissement. */
private int animIndex = 0;
/**
* Décalage courant en pixels (positif = droite/bas,
* négatif = gauche/haut).
*/
private float animDecalagePx = 0f;
// =========================================================
// Outils de dessin
// =========================================================
private final Paint pinceauCase = new Paint();
private final Paint pinceauSymbole = new Paint();
// =========================================================
// Constructeurs
// =========================================================
public VueGrille(Context contexte) {
super(contexte);
initPinceaux();
}
public VueGrille(Context context, AttributeSet attrs) {
super(context, attrs);
initPinceaux();
}
private void initPinceaux() {
pinceauCase.setAntiAlias(true);
pinceauCase.setStyle(Paint.Style.FILL);
pinceauSymbole.setAntiAlias(true);
pinceauSymbole.setColor(0xFF000000);
pinceauSymbole.setTextAlign(Paint.Align.CENTER);
}
// =========================================================
// API publique
// =========================================================
/**
* Reçoit une copie de la grille depuis MainActivity.
* VueGrille ne sait pas d'où viennent ces données,
* elle sait juste les dessiner.
*/
public void definirGrille(int[][] nouvelleGrille) {
for (int l = 0; l < NB_LIGNES; l++) {
System.arraycopy(nouvelleGrille[l], 0, grille[l], 0, NB_COLONNES);
}
invalidate();
}
public void definirModeDaltonien(boolean actif) {
this.modeDaltonien = actif;
invalidate();
}
public void definirVerrous(boolean[][] nouveauxVerrous) {
for (int l = 0; l < NB_LIGNES; l++) {
System.arraycopy(nouveauxVerrous[l], 0, verrous[l], 0, NB_COLONNES);
}
invalidate();
}
// =========================================================
// API animation de glissement (appelée par GestionnaireTactile)
// =========================================================
/**
* Déclenche l'animation de glissement en temps réel.
*
* @param estLigne true = on déplace une ligne, false = une colonne
* @param index index de la ligne ou colonne (0-based)
* @param decalagePx décalage courant en pixels (peut être négatif)
*/
public void definirGlissement(boolean estLigne, int index, float decalagePx) {
this.animEstLigne = estLigne;
this.animIndex = index;
this.animDecalagePx = decalagePx;
invalidate();
}
/**
* Annule l'animation de glissement et revient à l'affichage normal.
*/
public void annulerGlissement() {
this.animEstLigne = null;
invalidate();
}
// =========================================================
// Dessin
// =========================================================
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
int largeur = getWidth();
int hauteur = getHeight();
float tailleCase = Math.min(
largeur / (float) NB_COLONNES,
hauteur / (float) NB_LIGNES
);
float margeGauche = (largeur - tailleCase * NB_COLONNES) / 2f;
float margeHaut = (hauteur - tailleCase * NB_LIGNES) / 2f;
pinceauSymbole.setTextSize(tailleCase * 0.4f);
for (int ligne = 0; ligne < NB_LIGNES; ligne++) {
for (int colonne = 0; colonne < NB_COLONNES; colonne++) {
// --------------------------------------------------
// Calcul de l'offset d'animation pour cette case
// --------------------------------------------------
float offsetX = 0f;
float offsetY = 0f;
if (animEstLigne != null) {
if (animEstLigne && ligne == animIndex) {
// Décalage horizontal de toute la ligne
offsetX = animDecalagePx;
} else if (!animEstLigne && colonne == animIndex) {
// Décalage vertical de toute la colonne
offsetY = animDecalagePx;
}
}
// --------------------------------------------------
// Position de base de la case
// --------------------------------------------------
float x1 = margeGauche + colonne * tailleCase + 6 + offsetX;
float y1 = margeHaut + ligne * tailleCase + 6 + offsetY;
float x2 = margeGauche + (colonne + 1) * tailleCase - 6 + offsetX;
float y2 = margeHaut + (ligne + 1) * tailleCase - 6 + offsetY;
// --------------------------------------------------
// Dessin avec wrap-around (la case qui sort d'un
// côté réapparaît de l'autre côté)
// --------------------------------------------------
dessinerCase(canvas, ligne, colonne, x1, y1, x2, y2,
tailleCase, margeGauche, margeHaut, offsetX, offsetY);
}
}
}
/**
* Dessine une case à la position donnée.
* Si la case sort des bords (wrap-around), elle est aussi dessinée
* du côté opposé de la grille.
*/
private void dessinerCase(Canvas canvas,
int ligne, int colonne,
float x1, float y1, float x2, float y2,
float tailleCase,
float margeGauche, float margeHaut,
float offsetX, float offsetY) {
int type = grille[ligne][colonne];
// Bornage de la grille (en pixels)
float borneGaucheGrille = margeGauche;
float bordDroiteGrille = margeGauche + NB_COLONNES * tailleCase;
float borneHautGrille = margeHaut;
float bordBasGrille = margeHaut + NB_LIGNES * tailleCase;
// Largeur totale de la ligne / hauteur totale de la colonne en pixels
float largeurGrille = NB_COLONNES * tailleCase;
float hauteurGrille = NB_LIGNES * tailleCase;
// Dessine la case principale
dessinerRectCase(canvas, type, ligne, colonne, x1, y1, x2, y2);
// Wrap-around horizontal (pour les lignes)
if (offsetX != 0f) {
float wrapX1 = x1, wrapX2 = x2;
if (x2 > bordDroiteGrille) {
// La case déborde à droite → réapparaît à gauche
wrapX1 = x1 - largeurGrille;
wrapX2 = x2 - largeurGrille;
dessinerRectCase(canvas, type, ligne, colonne, wrapX1, y1, wrapX2, y2);
} else if (x1 < borneGaucheGrille) {
// La case déborde à gauche → réapparaît à droite
wrapX1 = x1 + largeurGrille;
wrapX2 = x2 + largeurGrille;
dessinerRectCase(canvas, type, ligne, colonne, wrapX1, y1, wrapX2, y2);
}
}
// Wrap-around vertical (pour les colonnes)
if (offsetY != 0f) {
float wrapY1 = y1, wrapY2 = y2;
if (y2 > bordBasGrille) {
// La case déborde en bas → réapparaît en haut
wrapY1 = y1 - hauteurGrille;
wrapY2 = y2 - hauteurGrille;
dessinerRectCase(canvas, type, ligne, colonne, x1, wrapY1, x2, wrapY2);
} else if (y1 < borneHautGrille) {
// La case déborde en haut → réapparaît en bas
wrapY1 = y1 + hauteurGrille;
wrapY2 = y2 + hauteurGrille;
dessinerRectCase(canvas, type, ligne, colonne, x1, wrapY1, x2, wrapY2);
}
}
}
/**
* Dessine le rectangle coloré d'une case + verrou + symbole daltonien.
*/
private void dessinerRectCase(Canvas canvas, int type,
int ligne, int colonne,
float x1, float y1, float x2, float y2) {
// Couleur de fond
definirCouleur(type);
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase);
// Assombrir si verrouillée
if (verrous[ligne][colonne]) {
pinceauCase.setARGB(120, 0, 0, 0);
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase);
}
float cx = (x1 + x2) / 2f;
float cy = (y1 + y2) / 2f
- (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
// Symbole daltonien
if (modeDaltonien) {
canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole);
}
// Cadenas par-dessus si verrouillée
if (verrous[ligne][colonne]) {
canvas.drawText("🔒", cx, cy, pinceauSymbole);
}
}
// =========================================================
// Utilitaire privé
// =========================================================
private void definirCouleur(int type) {
switch (type % NB_TYPES) {
case 0: pinceauCase.setARGB(255, 200, 200, 200); break;
case 1: pinceauCase.setARGB(255, 255, 105, 180); break;
case 2: pinceauCase.setARGB(255, 90, 230, 200); break;
case 3: pinceauCase.setARGB(255, 100, 170, 255); break;
case 4: pinceauCase.setARGB(255, 255, 220, 90); break;
case 5: pinceauCase.setARGB(255, 255, 140, 90); break;
case 6: pinceauCase.setARGB(255, 255, 90, 90); break;
}
}
}