Ce billet fait suite à celui-ci dans lequel j'avais promis plus d'explications.

Première étape : choisir une classe d'où partir, car il ne s'agit pas de réinventer la roue. Pour faire ce que nous voulons (disposer les cellules les unes par rapport aux autres), nous avons choisi d'utiliser un AbsoluteLayout, et pour pouvoir faire défiler notre liste, nous l'encapsulerons dans une ScrollView plus tard.

Il nous faudra également représenter chaque cellule avec une vue, nous créerons donc une nouvelle classe HiveCell, qui étend la classe View.

Maintenant, posons nous la question de ce que nous voulons afficher : En effet, pour la vue en mode portrait, des lignes de 3 cellules semblent un bon choix (même s'il reste envisageable de faire usage de lignes de plus de cellules dans des cas particuliers), mais en mode paysage, ne mettre que 3 cellules n'est pas ergonomique.

Une solution à ce problème est de ne pas choisir le nombre de cellules que l'on souhaite mettre, mais plutôt d'imposer un critère sur la taille d'une cellule et de laisser le Layout s'occuper du reste.


La solution retenue ici sera d'imposer une taille maximale en pixels pour la longueur d'un côté de cellule.


La structure retenue est une structure dite en « nid d'abeilles », constituée d'hexagones imbriqués afin de maximiser l'espace utilisable. Les hexagones ont un grand nombre de propriétés mathématiques intéressantes ( http://fr.wikipedia.org/wiki/Hexagone ).

On constate que l'information la plus importante est la taille du « côté », que nous appellerons désormais size. Quand on regarde la structure que l'on souhaite créer, on voit aussi qu'une autre donnée qui va beaucoup servir est la demi-hauteur d'un hexagone, qui est utilisée pour décaler un rang par rapport à un autre.

Avec des mathématiques rudimentaires on obtient que la largeur de l'hexagone est de 2*size, et la demi-hauteur (avec Pythagore) halfHeight = Sqrt[size² – (size/2)²]

On voit également que si le premier hexagone nécessite 2*size, chaque hexagone supplémentaire nécessite 1.5*size supplémentaire (il faut donc que la largeur de l'écran soit de 5*size pour afficher 3 hexagones en largeur, par exemple). Il nous faut donc mettre en place un mécanisme pour déterminer le nombre d'hexagones dès que l'on connait la largeur de l'écran.

Solution retenue : Au démarrage, à partir de la largeur de l'écran et de la taille minimale désirée pour un hexagone, on calcule combien d'hexagones on va pouvoir mettre, et on en déduit size.

nb = 0.5f; do {nb = nb + 1.5f;} while (width / nb > WANTED_MIN_SIZE);

Calcul du nombre d'Hexagones en resultant :

nbHexa = (int) ((nb - 0.5f) / 1.5f);

Calcul de la taille :

size = (int) ((float) width / nb);

Calcul de la demi-hauteur :

halfHeight = ((int) Math.sqrt((double) (size * size - (size / 2) * (size / 2))));

Maintenant que nous avons ces informations, nous aimerions bien définir les « zones » dans lequelles nos HiveCells se dessineront, afin de faire ceci, il nous faudra garder pendant le dessin une information au sujet du nombre de cellules déjà insérées pour connaître notre position, que nous stockerons dans une variable cellCount


Nous avons maintenant assez d'informations pour définir la position exacte d'une cellule à partir de son numéro :

LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);

int k = cellCount % nbHexa; // Dans quelle colonne se situe la cellule

lp.x = 2 * k + size * k * 3 / 2;

2*k permet de laisser un « espace » entre les cellules pour laisser les hexagones se distinguer

if (k % 2 == 0)

lp.y = cellCount / nbHexa + (cellCount / nbHexa) * 2 * halfHeight;

else

lp.y = cellCount / nbHexa + (cellCount / nbHexa) * 2 * halfHeight+halfHeight;

cellcount / nbHexa permet aussi de laisser un « espace » entre les cellules.

Attention, ces positions sont les positions de début du dessin des cellules, elles sont matérialisées par les points verts sur la dernière image.

Voilà nous avons maintenant tous les éléments du HiveLayout, il nous faut maintenant créer la HiveCell qui sera ajoutée à ces coordonnées dans l'AbsoluteLayout, mais ce sera l'objet d'un billet ultérieur.