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

284 lines
9.8 KiB
Java

package sae.chuzzle;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
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
private int[][] grille = new int[NB_LIGNES][NB_COLONNES];
private boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES];
private boolean modeDaltonien = false;
// =========================================================
// État du glissement
// =========================================================
private Boolean animEstLigne = null;
private int animIndex = 0;
private float animDecalagePx = 0f;
// Outils de dessin
private final Paint pinceauCase = new Paint();
private final Paint pinceauSymbole = new Paint();
private final Paint pinceauSelection = new Paint();
private Bitmap imageChaine;
// 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);
pinceauSelection.setAntiAlias(true);
pinceauSelection.setStyle(Paint.Style.STROKE);
pinceauSelection.setStrokeWidth(12f);
pinceauSelection.setColor(0xFFFFFFFF);
// Chargement de l'image de la chaine
imageChaine = BitmapFactory.decodeResource(getResources(), R.drawable.chaine);
}
// API publique
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)
public void definirGlissement(boolean estLigne, int index, float decalagePx) {
this.animEstLigne = estLigne;
this.animIndex = index;
this.animDecalagePx = decalagePx;
invalidate();
}
public void annulerGlissement() {
this.animEstLigne = null;
invalidate();
}
/**
* Vérifie si la sélection actuelle (ligne ou colonne) contient au moins un verrou.
*/
private boolean estSelectionBloquee() {
if (animEstLigne == null) return false;
if (animEstLigne) {
for (int c = 0; c < NB_COLONNES; c++) {
if (verrous[animIndex][c]) return true;
}
} else {
for (int l = 0; l < NB_LIGNES; l++) {
if (verrous[l][animIndex]) return true;
}
}
return false;
}
// Dessin
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
// Mise à jour de la couleur du contour de sélection si bloqué
if (animEstLigne != null) {
if (estSelectionBloquee()) {
pinceauSelection.setColor(0xFF000000); // Noir
} else {
pinceauSelection.setColor(0xFFFFFFFF); // Blanc
}
}
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;
// - On limite le dessin à la zone de la grille
canvas.save();
canvas.clipRect(margeGauche, margeHaut,
margeGauche + NB_COLONNES * tailleCase,
margeHaut + NB_LIGNES * tailleCase);
pinceauSymbole.setTextSize(tailleCase * 0.4f);
for (int ligne = 0; ligne < NB_LIGNES; ligne++) {
for (int colonne = 0; colonne < NB_COLONNES; colonne++) {
float offsetX = 0f;
float offsetY = 0f;
if (animEstLigne != null) {
int nbCases = Math.round(animDecalagePx / tailleCase);
if (animEstLigne && ligne == animIndex) {
offsetX = nbCases * tailleCase;
} else if (!animEstLigne && colonne == animIndex) {
offsetY = nbCases * tailleCase;
}
}
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;
dessinerCase(canvas, ligne, colonne, x1, y1, x2, y2,
tailleCase, margeGauche, margeHaut, offsetX, offsetY);
}
}
canvas.restore();
}
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];
float largeurGrille = NB_COLONNES * tailleCase;
float hauteurGrille = NB_LIGNES * tailleCase;
// Dessin case principale
dessinerRectCase(canvas, type, ligne, colonne, x1, y1, x2, y2);
// --- Wrap-around (réapparition de l'autre côté) ---
if (offsetX != 0f) {
float bordD = margeGauche + largeurGrille;
float bordG = margeGauche;
if (x2 > bordD) {
dessinerRectCase(canvas, type, ligne, colonne, x1 - largeurGrille, y1, x2 - largeurGrille, y2);
} else if (x1 < bordG) {
dessinerRectCase(canvas, type, ligne, colonne, x1 + largeurGrille, y1, x2 + largeurGrille, y2);
}
}
if (offsetY != 0f) {
float bordB = margeHaut + hauteurGrille;
float bordH = margeHaut;
if (y2 > bordB) {
dessinerRectCase(canvas, type, ligne, colonne, x1, y1 - hauteurGrille, x2, y2 - hauteurGrille);
} else if (y1 < bordH) {
dessinerRectCase(canvas, type, ligne, colonne, x1, y1 + hauteurGrille, x2, y2 + hauteurGrille);
}
}
}
/**
* Dessine le rectangle coloré d'une case + verrou + symbole daltonien.
* Ajoute un contour gras si la case est sélectionnée.
*/
private void dessinerRectCase(Canvas canvas, int type,
int ligne, int colonne,
float x1, float y1, float x2, float y2) {
definirCouleur(type);
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase);
if (animEstLigne != null) {
if ((animEstLigne && ligne == animIndex) || (!animEstLigne && colonne == animIndex)) {
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauSelection);
}
}
// 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;
if (modeDaltonien) {
canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole);
}
// Dessin de l'image de la chaine si verrouillée
if (verrous[ligne][colonne] && imageChaine != null) {
float size = (x2 - x1);
float chainSize = size * 0.55f; // Taille de chaque morceau de chaine
// Dessin d'une chaine en haut à gauche
canvas.drawBitmap(imageChaine, null, new RectF(x1, y1, x1 + chainSize, y1 + chainSize), null);
// Dessin d'une chaine en bas à droite
canvas.drawBitmap(imageChaine, null, new RectF(x2 - chainSize, y2 - chainSize, x2, y2), null);
} else if (verrous[ligne][colonne]) {
// Fallback si l'image n'est pas trouvée
canvas.drawText("🔒", cx, cy, pinceauSymbole);
}
}
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;
}
}
}