2026-03-29 00:30:52 +01:00
|
|
|
package sae.chuzzle;
|
|
|
|
|
|
2026-03-19 10:22:48 +01:00
|
|
|
import android.content.Context;
|
2026-03-23 12:03:12 +01:00
|
|
|
import android.graphics.Bitmap;
|
|
|
|
|
import android.graphics.BitmapFactory;
|
2026-03-19 10:22:48 +01:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Paint;
|
2026-03-23 12:03:12 +01:00
|
|
|
import android.graphics.RectF;
|
2026-03-19 10:22:48 +01:00
|
|
|
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 = {"●", "■", "▲", "✚", "★", "♦", "✿"};
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
// Données nécessaires au dessin
|
|
|
|
|
|
|
|
|
|
private int[][] grille = new int[NB_LIGNES][NB_COLONNES];
|
2026-03-19 10:22:48 +01:00
|
|
|
private boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES];
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
private Boolean animEstLigne = null;
|
2026-03-19 10:22:48 +01:00
|
|
|
private int animIndex = 0;
|
|
|
|
|
private float animDecalagePx = 0f;
|
|
|
|
|
|
|
|
|
|
// Outils de dessin
|
|
|
|
|
|
2026-03-23 12:03:12 +01:00
|
|
|
private final Paint pinceauCase = new Paint();
|
|
|
|
|
private final Paint pinceauSymbole = new Paint();
|
|
|
|
|
private final Paint pinceauSelection = new Paint();
|
|
|
|
|
private Bitmap imageChaine;
|
2026-03-19 10:22:48 +01:00
|
|
|
|
|
|
|
|
// 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);
|
2026-03-23 12:03:12 +01:00
|
|
|
|
|
|
|
|
pinceauSelection.setAntiAlias(true);
|
|
|
|
|
pinceauSelection.setStyle(Paint.Style.STROKE);
|
|
|
|
|
pinceauSelection.setStrokeWidth(12f);
|
|
|
|
|
pinceauSelection.setColor(0xFFFFFFFF);
|
|
|
|
|
|
|
|
|
|
imageChaine = BitmapFactory.decodeResource(getResources(), R.drawable.chaine);
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
// -
|
2026-03-19 10:22:48 +01:00
|
|
|
|
|
|
|
|
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 definirVerrous(boolean[][] nouveauxVerrous) {
|
|
|
|
|
for (int l = 0; l < NB_LIGNES; l++) {
|
|
|
|
|
System.arraycopy(nouveauxVerrous[l], 0, verrous[l], 0, NB_COLONNES);
|
|
|
|
|
}
|
|
|
|
|
invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
// animation de glissement
|
2026-03-23 12:03:12 +01:00
|
|
|
|
2026-03-19 10:22:48 +01:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dessin
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onDraw(@NonNull Canvas canvas) {
|
|
|
|
|
super.onDraw(canvas);
|
|
|
|
|
|
2026-03-23 12:03:12 +01:00
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
int largeur = getWidth() - getPaddingLeft() - getPaddingRight();
|
|
|
|
|
int hauteur = getHeight() - getPaddingTop() - getPaddingBottom();
|
2026-03-19 10:22:48 +01:00
|
|
|
|
|
|
|
|
float tailleCase = Math.min(
|
|
|
|
|
largeur / (float) NB_COLONNES,
|
|
|
|
|
hauteur / (float) NB_LIGNES
|
|
|
|
|
);
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
float margeGauche = getPaddingLeft() + (largeur - tailleCase * NB_COLONNES) / 2f;
|
|
|
|
|
float margeHaut = getPaddingTop() + (hauteur - tailleCase * NB_LIGNES) / 2f;
|
2026-03-19 10:22:48 +01:00
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
// Limite le dessin à la zone de la grille
|
2026-03-23 12:03:12 +01:00
|
|
|
canvas.save();
|
2026-03-29 00:30:52 +01:00
|
|
|
canvas.clipRect(margeGauche, margeHaut,
|
|
|
|
|
margeGauche + NB_COLONNES * tailleCase,
|
2026-03-23 12:03:12 +01:00
|
|
|
margeHaut + NB_LIGNES * tailleCase);
|
|
|
|
|
|
2026-03-19 10:22:48 +01:00
|
|
|
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) {
|
2026-03-23 12:03:12 +01:00
|
|
|
int nbCases = Math.round(animDecalagePx / tailleCase);
|
2026-03-19 10:22:48 +01:00
|
|
|
if (animEstLigne && ligne == animIndex) {
|
2026-03-23 12:03:12 +01:00
|
|
|
offsetX = nbCases * tailleCase;
|
2026-03-19 10:22:48 +01:00
|
|
|
} else if (!animEstLigne && colonne == animIndex) {
|
2026-03-23 12:03:12 +01:00
|
|
|
offsetY = nbCases * tailleCase;
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-23 12:03:12 +01:00
|
|
|
canvas.restore();
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
|
|
|
|
float largeurGrille = NB_COLONNES * tailleCase;
|
2026-03-29 00:30:52 +01:00
|
|
|
float hauteurGrille = NB_LIGNES * tailleCase;
|
2026-03-19 10:22:48 +01:00
|
|
|
|
2026-03-23 12:03:12 +01:00
|
|
|
// Dessin case principale
|
2026-03-29 00:30:52 +01:00
|
|
|
dessinerRectCase(canvas, ligne, colonne, x1, y1, x2, y2);
|
2026-03-19 10:22:48 +01:00
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
// Réapparition de l'autre côté (wrap)
|
2026-03-19 10:22:48 +01:00
|
|
|
if (offsetX != 0f) {
|
2026-03-23 12:03:12 +01:00
|
|
|
float bordD = margeGauche + largeurGrille;
|
|
|
|
|
float bordG = margeGauche;
|
|
|
|
|
if (x2 > bordD) {
|
2026-03-29 00:30:52 +01:00
|
|
|
dessinerRectCase(canvas, ligne, colonne, x1 - largeurGrille, y1, x2 - largeurGrille, y2);
|
2026-03-23 12:03:12 +01:00
|
|
|
} else if (x1 < bordG) {
|
2026-03-29 00:30:52 +01:00
|
|
|
dessinerRectCase(canvas, ligne, colonne, x1 + largeurGrille, y1, x2 + largeurGrille, y2);
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (offsetY != 0f) {
|
2026-03-23 12:03:12 +01:00
|
|
|
float bordB = margeHaut + hauteurGrille;
|
|
|
|
|
float bordH = margeHaut;
|
|
|
|
|
if (y2 > bordB) {
|
2026-03-29 00:30:52 +01:00
|
|
|
dessinerRectCase(canvas, ligne, colonne, x1, y1 - hauteurGrille, x2, y2 - hauteurGrille);
|
2026-03-23 12:03:12 +01:00
|
|
|
} else if (y1 < bordH) {
|
2026-03-29 00:30:52 +01:00
|
|
|
dessinerRectCase(canvas, ligne, colonne, x1, y1 + hauteurGrille, x2, y2 + hauteurGrille);
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Dessine le rectangle coloré d'une case + verrou + symbole daltonien.
|
2026-03-23 12:03:12 +01:00
|
|
|
* Ajoute un contour gras si la case est sélectionnée.
|
2026-03-19 10:22:48 +01:00
|
|
|
*/
|
2026-03-29 00:30:52 +01:00
|
|
|
private void dessinerRectCase(Canvas canvas,
|
2026-03-19 10:22:48 +01:00
|
|
|
int ligne, int colonne,
|
|
|
|
|
float x1, float y1, float x2, float y2) {
|
|
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
int type = grille[ligne][colonne];
|
|
|
|
|
|
2026-03-19 10:22:48 +01:00
|
|
|
definirCouleur(type);
|
|
|
|
|
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase);
|
|
|
|
|
|
2026-03-23 12:03:12 +01:00
|
|
|
if (animEstLigne != null) {
|
2026-03-29 00:30:52 +01:00
|
|
|
if ((animEstLigne && ligne == animIndex)
|
|
|
|
|
|| (!animEstLigne && colonne == animIndex)) {
|
2026-03-23 12:03:12 +01:00
|
|
|
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauSelection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 10:22:48 +01:00
|
|
|
// 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;
|
2026-03-23 12:03:12 +01:00
|
|
|
float cy = (y1 + y2) / 2f - (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
|
2026-03-19 10:22:48 +01:00
|
|
|
|
2026-03-29 00:30:52 +01:00
|
|
|
canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole);
|
2026-03-28 16:01:01 +01:00
|
|
|
|
2026-03-23 12:03:12 +01:00
|
|
|
// Dessin de l'image de la chaine si verrouillée
|
|
|
|
|
if (verrous[ligne][colonne] && imageChaine != null) {
|
2026-03-29 00:30:52 +01:00
|
|
|
float size = (x2 - x1);
|
|
|
|
|
float chainSize = size * 0.55f;
|
2026-03-23 12:03:12 +01:00
|
|
|
canvas.drawBitmap(imageChaine, null, new RectF(x1, y1, x1 + chainSize, y1 + chainSize), null);
|
|
|
|
|
canvas.drawBitmap(imageChaine, null, new RectF(x2 - chainSize, y2 - chainSize, x2, y2), null);
|
2026-03-19 10:22:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-29 00:30:52 +01:00
|
|
|
|
|
|
|
|
/** Vérifie si la ligne/colonne en cours de glissement contient 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;
|
|
|
|
|
}
|
2026-03-23 12:03:12 +01:00
|
|
|
}
|