Si leur méthode relocate() provoque bien des modifications sur les propriétés de positionnement layoutX et layoutY, il n'en est pas de même pour leur méthode resize() ; en effet, cette méthode définie dans la classe parente Node... est vide est ne fait rien. Cette méthode est bien implementée dans les nœuds qui héritent de Region mais généralement pas dans les autres types de nœuds plus basiques. Ce qui est normal ; puisque, par défaut, les nœuds ne sont pas redimensionnables : dans la classe Node, la méthode isResizable() retourne false. Or, les gestionnaires de mise en page vont avoir tendance à invoquer sur les nœuds redimensionnables des variantes de layoutInArea() ou positionInArea() qui vont elles mêmes invoquer resize().
Donc, du coup, Canvas, ImageView, MediaView, etc. n’étant pas redimensionnables, cette invocation n'a jamais lieu. Et même si elle avait lieu, cela ne ferait rien puisque l’implémentation par défaut de cette méthode est vide. Et donc ces nœuds ne sont jamais redimensionnés lors des phases de mise en page.
Note : resizeRellocate() invoque tout simplement successivement resize() suivie de rellocate().
Il existe plusieurs manières de rendre ces nœuds redimensionnables ; comme par exemple ce que j'ai écrit dans la FAQ concernant MediaView en bindant ses propriétés fitWidth et fitHeight sur celles de son conteneur parent.
Code Java : | Sélectionner tout |
1 2 3 4 | [...] mediaView.fitWidthProperty().bind(root.widthProperty()); mediaView.fitHeightProperty().bind(root.heightProperty()); [...] |
Une autre méthode assez rapide à mettre en place est de créer un nouveau type de contrôle qui contient le nœud désiré et qui le met en page correctement lorsqu'il se redimensionne en surchargeant layoutChildren(). Voici, par exemple, une implémentation chargée d'afficher un Canvas :
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public final class CanvasPane extends Region { private final Canvas canvas; public CanvasPane() { super(); setId("canvasPane"); getStyleClass().add("canvas-pane"); canvas = new Canvas(); getChildren().setAll(canvas); } @Override protected void layoutChildren() { super.layoutChildren(); final double width = getWidth(); final double height = getHeight(); final Insets insets = getInsets(); final double contentX = insets.getLeft(); final double contentY = insets.getTop(); final double contentW = Math.max(0, width - (insets.getLeft() + insets.getRight())); final double contentH = Math.max(0, height - (insets.getTop() + insets.getBottom())); canvas.relocate(contentX, contentY); canvas.setWidth(contentW); canvas.setHeight(contentH); } } |
Ici, pour Canvas on utilise ses propriétés width et height mais pour ImageView ou MediaView on utilisera fitWidth et fitHeight. De plus, vous voudrez sans doute tenir compte du ratio largeur x hauteur de l'image ou de la vidéo pour le conserver ce qui demandera un petit peu plus de calculs de positionnements et de redimensionnement par rapport aux dimensions du conteneur parent. Mais bon, rien de bien critique...
Sinon, l’intérêt de calculer le positionnement par rapport à l'Insets, c'est de tout simplement tenir compte des dimensions potentielles de la bordure qu'on peut rajouter autour de la région parente via les CSS. Comme cela le contrôle peut toujours être décoré sans que son contenu ne cache la bordure.
Après, il suffit de rajouter les bonnes méthodes et propriétés sur cette région customisée pour pouvoir interagir avec le Canvas contenu dedans et c'est tout bon. Et du coup, ça permet aussi de manipuler directement un Canvas redimensionnable depuis un fichier FXML sans trop de difficulté : il suffit d'importer le package approprié dans l’entête du FXML et que le contrôle soit sur le CLASSPATH lors du chargement du fichier.
Code XML : | Sélectionner tout |
1 2 3 4 5 | <StackPane id="root> <children> <CanvasPane id="myCanvas" /> </children> </StackPane> |
Importer le contrôle dans SceneBuilder 2.0/8.0 demandera par contre un petit peu plus de travail.