Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Rendre Canvas, ImageView, MediaView, etc. redimensionnables,
Par bouye

Le , par bouye

0PARTAGES

Il semble que certaines personnes soient un peu bloquées sur le fait que Canvas, ImageView, MediaView, SubScene ou même SwingNode pour ne citer qu'eux ne sont pas des contrôles et donc, du coup, ne sont pas automatiquement redimensionnés lorsqu'on les place dans des layouts dont la taille peut varier lorsqu'on redimensionne la fenêtre de l’application ou lorsqu'on modifie l'agencement interne (SplitPane, Accordion, animation, etc.). C'est normal : ces nœuds graphiques ne sont pas des régions, pas des contrôles et donc ils ne disposent pas des propriétés nécessaires pour que les gestionnaires de mise en page puissent les agrandir ou le rétrécir en cas de changement de dimensions.

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.

Une erreur dans cette actualité ? Signalez-le nous !