IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 Java Discussion :

Responsive en Swing


Sujet :

Java

  1. #1
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut Responsive en Swing
    Bonjour tout le monde,

    Je suis à la recherche d'une solution pour adapter la taille des éléments à afficher à la taille de ma fenêtre.
    J'ai imaginer faire une classe qui hérite de Graphics2D et redéfinir ces méthodes avec celles de DebugGraphics,
    pour au final compléter le tout par un ajustement des dimensions et position relativement à la taille de ma fenêtre.

    Mais ce fut un échec.
    L'architecture du code ne permet visiblement pas d'être modifier.

    Si quelqu'un à une autre idée pour ne pas avoir à donner une position et une taille relative à la main à chaque fois que je créé un nouvel objet à afficher.

  2. #2
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Salut,

    Le responsive n'est pas un changement d'echelle, mais une adaptation d'une GUI à la taille (et la forme implicite) de l'écran, et de l'orientation éventuellement. Il n'y a pas qu'un problème échelle, mais aussi de placement (relatif) des composants, voire même des changements dans le type même des composants (on pourra cacher un bloc pour le transformer en popin). Et la mise à l'échelle concerne la taille des composants, mais pas le facteur d'échelle des textes, par exemple.
    Les LayoutManager permettent de faire du responsive naturellement en Swing pour ce qui des composants Swing? Il faudra éventuellement faire des layout manager custom pour gérer certains cas automatiquement (par exemple, pour un changement vertical/horizontal).

    Si tu parles d'adapter la taille (ou l'angle éventuellement) d'un contenu purement graphique (dessiné directement en Java2D , avec des vecteurs ou des images), utilise les transformées affines.

    Démo avec les transformées affines :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.Image;
    import java.awt.Insets;
    import java.awt.geom.AffineTransform;
    import java.awt.image.ImageObserver;
    import java.util.function.BiConsumer;
     
    import javax.imageio.ImageIO;
    import javax.swing.BorderFactory;
    import javax.swing.JCheckBox;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class ScaleAdaptablePanel extends JPanel {
     
    	private boolean proportional;
    	private boolean adaptable;
    	private final BiConsumer<Graphics2D,ImageObserver> painter;
    	private final Dimension drawsize;
     
    	public ScaleAdaptablePanel(BiConsumer<Graphics2D,ImageObserver> painter, int width, int height) {
    		this.painter=painter;
    		this.proportional=true;
    		this.adaptable=true;
    		this.drawsize = new Dimension(width, height);
    	}
     
    	public void setProportional(boolean proportional) {
    		boolean old = this.proportional;
    		this.proportional=proportional;
    		if ( old!=proportional ) {
    			firePropertyChange("PROPORTIONAL", old, proportional);
    			repaint();
    		}
    	}
     
    	public boolean isProportional() {
    		return proportional;
    	}
     
    	public void setAdaptable(boolean adaptable) {
    		boolean old = this.adaptable;
    		this.adaptable=adaptable;
    		if ( old!=adaptable ) {
    			firePropertyChange("ADAPTABLE", old, adaptable);
    			repaint();
    		}
    	}
     
    	public boolean isAdaptable() {
    		return adaptable;
    	}
     
    	private Dimension getInnerDimension(Dimension dimension) {
    		Insets insets=getInsets();
    		return new Dimension(Math.max(0, dimension.width-insets.left-insets.right), 
    				Math.max(0, dimension.height-insets.top-insets.bottom));
    	}
     
    	@Override
    	public void paint(Graphics g) {
    		super.paint(g);
     
    		if ( painter!=null) {
     
    			Graphics2D g2D = (Graphics2D) g.create();		
     
    			Insets insets=getInsets();
    			g2D.transform(AffineTransform.getTranslateInstance(insets.left, insets.top));
     
    			if ( adaptable ) {
    				adapt(g2D);
    			}
    			painter.accept(g2D,this);
     
    			g2D.dispose();
     
    		}
     
    	}
     
    	private void adapt(Graphics2D g2d) {
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getPreferredSize()));
    		if ( proportional ) {
    			adaptProportional(g2d);
    		}
    		else {
    			affineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    		}
    	}
     
    	private void adaptProportional(Graphics2D g2d) {
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    	}
     
    	private void affineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth();
    		double sy = tosize.getHeight()/fromsize.getHeight();
    		g2d.transform(AffineTransform.getScaleInstance(sx, sy));
    	}
     
    	private void proportionalAffineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth();
    		double height = fromsize.getHeight()*sx;
    		if ( height > tosize.getHeight() ) {
    			sx = tosize.getHeight()/fromsize.getHeight();
    //			double width = tosize.getWidth()*sx;
    //			if ( width> tosize.getWidth() ) {
    //				System.out.println(sx);
    //				//sx = tosize.getWidth()/fromsize.getWidth();
    //			}
    		}
    		g2d.transform(AffineTransform.getScaleInstance(sx, sx));
    	}
     
    	// démo
     
    	public static void main(String[] args) {
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel main = new JPanel(new BorderLayout());
     
    		Dimension dimension = new Dimension(260,180);
    		ScaleAdaptablePanel panel = new ScaleAdaptablePanel(ScaleAdaptablePanel::paint,dimension.width,dimension.height);
    		panel.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10,panel.getBackground()));
    		panel.setBackground(Color.WHITE);
    		panel.setPreferredSize(dimension);
    		main.add(panel);
     
    		JPanel ui = new JPanel(new GridBagLayout());
     
    		JCheckBox checkboxProportional = new JCheckBox("Adaptation proportionelle");
    		checkboxProportional.setSelected(panel.isProportional());
    		checkboxProportional.addActionListener(e->panel.setProportional(checkboxProportional.isSelected()));
    		ui.add(checkboxProportional);
     
    		JCheckBox checkboxAdaptable = new JCheckBox("Adaptation");
    		checkboxAdaptable.setSelected(panel.isAdaptable());
    		checkboxAdaptable.addActionListener(e->panel.setAdaptable(checkboxAdaptable.isSelected()));
    		ui.add(checkboxAdaptable);
     
    		main.add(ui, BorderLayout.SOUTH);
     
    		frame.add(main);
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
    	private static Image image = loadImage();
    	private static Image loadImage() {
    		try {
    			return ImageIO.read(ScaleAdaptablePanel.class.getResource("suricate.jpg"));
    		} catch (Throwable e) {
    			return null;
    		}
    	}
    	private static Font font = new Font("SansSerif",Font.PLAIN,40);
    	private static void paint(Graphics2D g, ImageObserver observable) {
    		/*g.setColor(Color.LIGHT_GRAY);
    		g.drawRect(0,0,240,160);*/
    		g.setColor(Color.BLUE);
    		g.setFont(font);
    		g.drawString("Hello", 20,30);
    		g.setColor(Color.RED);
    		g.drawRect(120, 80, 100, 40);
    		g.setColor(Color.ORANGE);
    		g.fillOval(130, 20, 50, 50);
    		if( image!=null) {
    			g.drawImage(image,10,50,observable);
    		}
    	}
     
    	// fin démo
     
    }

  3. #3
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut
    Super, merci pour ta réponse.

    Je ne comprend pas très bien cette partie : "firePropertyChange("ADAPTABLE", old, adaptable);".
    A quoi ça sert?

  4. #4
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Pecose Voir le message
    Super, merci pour ta réponse.

    Je ne comprend pas très bien cette partie : "firePropertyChange("ADAPTABLE", old, adaptable);".
    A quoi ça sert?
    En Swing, les changements des propriétés peuvent être écoutés (voir méthode addPropertyChangeListener()). C'est juste que j'ai ajouté pour mes propriétés perso le moyen d'écouter leur changement de la même façon que n'importe quelle autre propriété Swing. C'est une bonne habitude à prendre qui ne coûte pas grand chose et qui peut être utile.

  5. #5
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut
    A quoi sert ImageObserver?
    Si tu met null ça donne la même chose, non?

    Citation Envoyé par joel.drigo Voir le message
    En Swing, les changements des propriétés peuvent être écoutés (voir méthode addPropertyChangeListener()). C'est juste que j'ai ajouté pour mes propriétés perso le moyen d'écouter leur changement de la même façon que n'importe quelle autre propriété Swing. C'est une bonne habitude à prendre qui ne coûte pas grand chose et qui peut être utile.
    Dans ce cas il y a déjà le JCheckBox qui est écouté. c'est pas un peu superflu?

    Si je modifie la variable est-ce qu'il y a un Event qui est créé et comment je le récupère?

  6. #6
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Pecose Voir le message
    A quoi sert ImageObserver?
    Si tu met null ça donne la même chose, non?
    ça ne sert pas avec les images static. mais pour les images non static c'est indispensable (par exemple avec un gif animé).

    Citation Envoyé par Pecose Voir le message
    Dans ce cas il y a déjà le JCheckBox qui est écouté. c'est pas un peu superflu?
    ça n'a strictement aucun rapport. Le checkbox est un composant de l'ui qui permet de modifier la propriété. Cela n'empêche pas d'avoir d'autres composants qui pourraient avoir besoin de savoir que la propriété à changer. On peut aussi faire un programme qui utilise le composant sans checkbox dans l'ui d'ailleurs, et changer une propriété par programme. Si on veut un composant qui réagit au changement d'une propriété, il utilisera l'écouteur.

    Citation Envoyé par Pecose Voir le message
    Si je modifie la variable est-ce qu'il y a un Event qui est créé et comment je le récupère?
    Oui il y a un événement qui ést créé et diffusé à tous les écouteurs. Pour le récupérer, utiliser la méthode addPropertyChangeListener().
    Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // pour écouter spécifiquement un changement sur proportional
    panel.addPropertyChangeListener("PROPORTIONAL",e->System.out.println("La propriété PROPORTIONAL a changé de " + e.getOldValue() + " à " + e.getNewValue()));
    // pour écouter un changement sur n'importe quelle propriété
    panel.addPropertyChangeListener(e->System.out.println("La propriété "+e.getPropertyName()+" a changé de " + e.getOldValue() + " à " + e.getNewValue()));
    Tiens, au passage, j'ai fait une erreur de copier/coller dans ma démo :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    JCheckBox checkboxAdaptable = new JCheckBox("Adaptation");
    checkboxAdaptable.setSelected(panel.isAdaptable());
    checkboxAdaptable.addActionListener(e->panel.setAdaptable(checkboxAdaptable.isSelected()));
    ui.add(checkboxAdaptable);

  7. #7
    Membre habitué Avatar de Pecose
    Homme Profil pro
    Batiment
    Inscrit en
    Février 2013
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Batiment
    Secteur : Bâtiment

    Informations forums :
    Inscription : Février 2013
    Messages : 310
    Points : 194
    Points
    194
    Par défaut
    Ok, j'ai pas réussi à intégrer la gestion de la souris dans ton code.
    Et dans mon code ça marche mais pas comme il faudrait.

    Déjà, je gère le déplacement de ma souris avec ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    public int getX(){ return MouseInfo.getPointerInfo().getLocation().x - frame.getComponent(0).getX() - frame.getX(); };
    public int getY(){ return MouseInfo.getPointerInfo().getLocation().y - frame.getComponent(0).getY() - frame.getY(); };
    Je n'utilise pas MouseMotionListener parce que ça ne fonctionne pas lorsque le curseur est en dehors de ma fenêtre. Peut-être que je l'utilise mal?

    Ensuite voila mon panel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    package display;
     
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Insets;
    import java.awt.geom.AffineTransform;
     
    import javax.swing.BorderFactory;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
     
    public class Panel extends JPanel{  
    	public Frame frame;
    	private boolean proportional = true;
    	private Dimension drawsize;
     
    	public Panel(PecEngine pecEngine){
    		Panel p = this;
     
    		SwingUtilities.invokeLater(new Runnable() {
    			@Override
    			public void run(){
    				p.frame = new Frame(pecEngine, p);
    				p.drawsize = new Dimension(frame.getScreenWidth(), frame.getScreenHeight());
    			}
    		});
     
    		this.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10,this.getBackground()));
    		this.setBackground(Color.WHITE);
    		this.setPreferredSize(new Dimension(300, 300));
    	}
     
    	@Override
    	protected void paintComponent(Graphics g) {
    		super.paintComponent(g);
    		try{
    			Graphics2D g2 = (Graphics2D) g.create();
    			Insets insets = getInsets();
    			g2.transform(AffineTransform.getTranslateInstance(insets.left, insets.top));
     
    			if (proportional) { adapt(g2); }
    			frame.pecEngine.display(this, g2);
    			g2.dispose();
    			repaint();
    		}catch(Exception e){} 
    	}
     
    	//::::::::::::::::::::::::::::::::::::::::::::::: Ton code
     
    		private Dimension getInnerDimension(Dimension dimension) {
    			Insets insets = getInsets();
    			return new Dimension(Math.max(0, dimension.width-insets.left-insets.right), 
    					Math.max(0, dimension.height-insets.top-insets.bottom));
    		}
     
    		private void proportionalAffineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    			double sx = tosize.getWidth()/fromsize.getWidth();
    			double height = fromsize.getHeight()*sx;
    			if ( height > tosize.getHeight() ) {
    				sx = tosize.getHeight()/fromsize.getHeight();
    			}
    			g2d.transform(AffineTransform.getScaleInstance(sx, sx));
    		}
     
    		private void adapt(Graphics2D g2d) {
    			proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    		}
     
    		//:::::::::::::::::::::::::::::::::::::::: Fin de ton code
    }
    Je t'avoue que je ne comprend rien à "ton code" (ci-dessus), j'aurai bien besoin d'une traduction parce que là j'ai pas le niveau.

    Et au final, j'ai un truc qui marche mais la position de la souris aussi s'adapte à la taille de ma fenêtre.
    Du coup sa position dans la fenêtre ne correspond pas à sa position sur l'écran.

  8. #8
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Pecose Voir le message
    Je n'utilise pas MouseMotionListener parce que ça ne fonctionne pas lorsque le curseur est en dehors de ma fenêtre. Peut-être que je l'utilise mal?
    ça fonctionne uniquement pour un mouseDragged et je ne vois pas pourquoi ça devrait fonctionner en dehors du composant émetteur pour un autre cas. Le but de la souris c'est d'interargir en général avec ce qui est dans la fenêtre. Il y a peut être certains cas particuliers (par exemple, un tracé de ligne du curseur vers un point du panel), mais c'est plutôt rare. Maintenant, les deux méthodes existent.


    Pour adapter le dessin, on détermine la transformée affine qui permet de passer d'un dessin dans une taille fixe donnée (donnée au ScaleAdaptablePanel par les paramètre width et heigth) à une taille d'affichage qui est celle du JPanel (et à une position qui peut être décalée si on met un Border). En plus, il faut tenir compte du fait que les deux ne sont pas forcément de dimensions proportionnelles, donc il faut éventuellement corriger. En résumé on détermine plusieurs transformées affines, qui font chacune une transformation et on les composes. Je te mettrai plus de détail en commentant le code plus tard (pas le temps maintenant).
    Pour gérer la souris il faut juste faire la transformée inverse.

    [edit]Dans cet exemple j'ai juste implémenté le mouseMove, il faut faire les autres pour généraliser, mais c'est facile, le code pour tous les évenements est sensiblement le même mise à part la méthode du listener à invoquer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    import java.awt.AlphaComposite;
    import java.awt.BasicStroke;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Cursor;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.Image;
    import java.awt.Insets;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.Shape;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.event.MouseMotionListener;
    import java.awt.event.MouseWheelEvent;
    import java.awt.event.MouseWheelListener;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.NoninvertibleTransformException;
    import java.awt.image.ImageObserver;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.function.BiConsumer;
     
    import javax.imageio.ImageIO;
    import javax.swing.BorderFactory;
    import javax.swing.JCheckBox;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.event.EventListenerList;
     
    public class ScaleAdaptablePanel extends JPanel {
     
    	private static final long serialVersionUID = 1L;
     
    	private boolean proportional;
    	private boolean adaptable;
    	private final BiConsumer<Graphics2D,ImageObserver> painter;
    	private final Dimension drawsize;
    	private EventListenerList eventlisteners;
     
    	public ScaleAdaptablePanel(BiConsumer<Graphics2D,ImageObserver> painter, int width, int height) {
    		this.eventlisteners = new EventListenerList();
    		this.painter=painter;
    		this.proportional=true;
    		this.adaptable=true;
    		this.drawsize = new Dimension(width, height);
    		MouseAdapter mouseAdapter = new MouseAdapter() { 
     
    			@Override
    			public void mouseClicked(MouseEvent e) {
    				propagate(e, getMouseListeners(), MouseListener::mouseClicked);
    			}
     
    			@Override
    			public void mousePressed(MouseEvent e) {
    				propagate(e, getMouseListeners(), MouseListener::mousePressed);
    			}
     
    			@Override
    			public void mouseReleased(MouseEvent e) {
    				propagate(e, getMouseListeners(), MouseListener::mousePressed);
    			}
     
    			@Override
    			public void mouseEntered(MouseEvent e) {
    				propagate(e, getMouseListeners(), MouseListener::mouseEntered);
    			}
     
    			@Override
    			public void mouseExited(MouseEvent e) {
    				propagate(e, getMouseListeners(), MouseListener::mouseExited);
    			}
     
    			@Override
    			public void mouseMoved(MouseEvent e) {
    				propagate(e, getMouseMotionListeners(), MouseMotionListener::mouseMoved);
    			}
     
    			@Override
    			public void mouseDragged(MouseEvent e) {
    				propagate(e, getMouseMotionListeners(), MouseMotionListener::mouseDragged);
    			}
     
    			@Override
    			public void mouseWheelMoved(MouseWheelEvent e) {
    				propagate(e, getMouseWheelListeners(), MouseWheelListener::mouseWheelMoved);
    			}
     
    			private void propagate(MouseEvent mouseEvent, MouseListener[] listeners, BiConsumer<MouseListener, MouseEvent> distatcher) {
    				Point mousePoint = mouseEvent.getPoint();
    				try {
    					convertMouse(mousePoint); 
    					MouseEvent newEvent = createMouseEvent(mouseEvent, mousePoint);
    					synchronized (ScaleAdaptablePanel.this) {
    						Arrays.stream(listeners).forEach(listener->distatcher.accept(listener,newEvent));
    					}
    				} catch (NoninvertibleTransformException e1) {
    					e1.printStackTrace();
    				}
    			}
    			private void propagate(MouseEvent mouseEvent, MouseMotionListener[] listeners, BiConsumer<MouseMotionListener, MouseEvent> distatcher) {
    				Point mousePoint = mouseEvent.getPoint();
    				try {
    					convertMouse(mousePoint); 
    					MouseEvent newEvent = createMouseEvent(mouseEvent, mousePoint);
    					synchronized (ScaleAdaptablePanel.this) {
    						Arrays.stream(listeners).forEach(listener->distatcher.accept(listener,newEvent));
    					}
    				} catch (NoninvertibleTransformException e1) {
    					e1.printStackTrace();
    				}
    			}
    			private void propagate(MouseWheelEvent mouseEvent, MouseWheelListener[] listeners, BiConsumer<MouseWheelListener, MouseWheelEvent> distatcher) {
    				Point mousePoint = mouseEvent.getPoint();
    				try {
    					convertMouse(mousePoint); 
    					MouseWheelEvent newEvent = createMouseEvent(mouseEvent, mousePoint);
    					synchronized (ScaleAdaptablePanel.this) {
    						Arrays.stream(listeners).forEach(listener->distatcher.accept(listener,newEvent));
    					}
    				} catch (NoninvertibleTransformException e1) {
    					e1.printStackTrace();
    				}
    			}
     
    			private MouseEvent createMouseEvent(MouseEvent e, Point p) {
    				return new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), p.x, p.y, e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(),  e.getButton());
    			}
    			private MouseWheelEvent createMouseEvent(MouseWheelEvent e, Point p) {
    				return new MouseWheelEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), p.x, p.y, e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(),  e.getScrollType(), e.getScrollAmount(), e.getWheelRotation(), e.getPreciseWheelRotation());
    			}
    		};
    		super.addMouseListener(mouseAdapter);
    		super.addMouseMotionListener(mouseAdapter);
    		super.addMouseWheelListener(mouseAdapter);
    	}
     
    	public void addComponentMouseListener(MouseListener listener) {
    		super.addMouseListener(listener);
    	}
     
    	public void removeComponentMouseListener(MouseListener l) {
    		super.removeMouseListener(l);
    	}
     
    	public void addComponentMotionListener(MouseMotionListener listener) {
    		super.addMouseMotionListener(listener);
    	}
     
    	public void removeComponentMotionListener(MouseMotionListener listener) {
    		super.removeMouseMotionListener(listener);
    	}
     
    	public void addComponentMouseWheelListener(MouseWheelListener listener) {
    		super.addMouseWheelListener(listener);
    	}
    	public void removeComponentMouseWheelListener(MouseWheelListener listener) {
    		super.removeMouseWheelListener(listener);
    	}
     
    	@Override
    	public synchronized MouseListener[] getMouseListeners() {
    		return eventlisteners.getListeners(MouseListener.class);
    	}
     
    	@Override
    	public synchronized void addMouseListener(MouseListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.add(MouseListener.class, l);
    	}
     
    	@Override
    	public synchronized void removeMouseListener(MouseListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.remove(MouseListener.class, l);
    	}
     
    	@Override
    	public synchronized MouseMotionListener[] getMouseMotionListeners() {
    		return eventlisteners.getListeners(MouseMotionListener.class);
    	}
     
    	@Override
    	public synchronized void addMouseMotionListener(MouseMotionListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.add(MouseMotionListener.class, l);
    	}
     
    	@Override
    	public synchronized void removeMouseMotionListener(MouseMotionListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.remove(MouseMotionListener.class, l);
    	}
     
    	@Override
    	public synchronized MouseWheelListener[] getMouseWheelListeners() {
    		return eventlisteners.getListeners(MouseWheelListener.class);
    	} 
     
    	@Override
    	public synchronized void addMouseWheelListener(MouseWheelListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.add(MouseWheelListener.class, l);
    	}
     
    	@Override
    	public synchronized void removeMouseWheelListener(MouseWheelListener l) {
    		if (l == null) {
                return;
            }
    		eventlisteners.remove(MouseWheelListener.class, l);
    	}
     
    	public void setProportional(boolean proportional) {
    		boolean old = this.proportional;
    		this.proportional=proportional;
    		if ( old!=proportional ) {
    			firePropertyChange("PROPORTIONAL", old, proportional);
    			repaint();
    		}
    	}
     
    	public boolean isProportional() {
    		return proportional;
    	}
     
    	public Point getAdapterMousePoint() {
    		Point point = getMousePosition();
    		try {
    			convertMouse(point);
    		} catch (NoninvertibleTransformException e) {
    			e.printStackTrace();
    		}
    		return point;
    	}
     
    	public void setAdaptable(boolean adaptable) {
    		boolean old = this.adaptable;
    		this.adaptable=adaptable;
    		if ( old!=adaptable ) {
    			firePropertyChange("ADAPTABLE", old, adaptable);
    			repaint();
    		}
    	}
     
    	public boolean isAdaptable() {
    		return adaptable;
    	}
     
    	private Dimension getInnerDimension(Dimension dimension) {
    		Insets insets=getInsets();
    		return new Dimension(Math.max(0, dimension.width-insets.left-insets.right), 
    				Math.max(0, dimension.height-insets.top-insets.bottom));
    	}
     
    	@Override
    	public void paint(Graphics g) {
    		super.paint(g);
     
    		if ( painter!=null) {
     
    			Graphics2D g2D = (Graphics2D) g.create();		
     
    			Insets insets=getInsets();
    			g2D.transform(AffineTransform.getTranslateInstance(insets.left, insets.top));
     
    			if ( adaptable ) {
    				adapt(g2D);
    			}
    			painter.accept(g2D,this);
     
    			g2D.dispose();
     
    		}
     
    	}
     
    	private void adapt(Graphics2D g2d) {
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getPreferredSize()));
    		if ( proportional ) {
    			adaptProportional(g2d);
    		}
    		else {
    			affineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    		}
    	}
     
    	private void adaptProportional(Graphics2D g2d) {
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    	}
     
    	private void affineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) { 
    		g2d.transform(affineTransform(fromsize, tosize));
    	}
     
    	private AffineTransform affineTransform(Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth();
    		double sy = tosize.getHeight()/fromsize.getHeight();
    		return AffineTransform.getScaleInstance(sx, sy);
    	}
     
    	private void proportionalAffineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    		g2d.transform(proportionalAffineTransform(fromsize,tosize));
    	}
     
    	private AffineTransform proportionalAffineTransform(Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth();
    		double height = fromsize.getHeight()*sx;
    		if ( height > tosize.getHeight() ) {
    			sx = tosize.getHeight()/fromsize.getHeight(); 
    		}
    		return AffineTransform.getScaleInstance(sx, sx);
    	}
     
    	protected void convertMouse(Point point) throws NoninvertibleTransformException {
    		Insets insets=getInsets();
    		AffineTransform transform = AffineTransform.getTranslateInstance(insets.left, insets.top);
    		if ( adaptable ) {
    			transform.concatenate(proportionalAffineTransform(getInnerDimension(drawsize), getInnerDimension(getPreferredSize())));
    			if ( proportional ) {
    				transform.concatenate(proportionalAffineTransform(getInnerDimension(drawsize), getInnerDimension(getSize())));
    			}
    			else {
    				transform.concatenate(affineTransform(getInnerDimension(drawsize), getInnerDimension(getSize())));
    			}
    		}  
    		transform.inverseTransform(point, point); 
    	}
     
    	// démo
     
    	public static void main(String[] args) {
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel main = new JPanel(new BorderLayout());
     
    		Dimension dimension = new Dimension(260,180);
    		ScaleAdaptablePanel panel = new ScaleAdaptablePanel(ScaleAdaptablePanel::paint,dimension.width,dimension.height);
    		panel.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10,panel.getBackground()));
    		panel.setBackground(Color.WHITE);
    		panel.setPreferredSize(dimension);
     
    		panel.setEnabled(true);
    		MouseAdapter mouseAdapter = new MouseAdapter() {
     
    			Rectangle rectangle = new Rectangle(120, 80, 100, 40);
    			Ellipse2D oval = new Ellipse2D.Double(130, 20, 50, 50);
    			Rectangle imageBounds = image==null?null:new Rectangle(10,50, image.getWidth(panel), image.getHeight(panel));
     
    			@Override
    			public void mouseMoved(MouseEvent e) {
    				rectangleHover = rectangle.contains(e.getPoint());
    				ovalHover = oval.contains(e.getPoint());
    				int cursor;
    				if ( rectangleHover ) {
    					cursor = Cursor.HAND_CURSOR;
    				}
    				else if ( ovalHover ) {
    					cursor = Cursor.HAND_CURSOR;
    				}
    				else if ( imageBounds!=null && imageBounds.contains(e.getPoint()) ) {
    					cursor = Cursor.HAND_CURSOR;
    				}
    				else {
    					cursor = Cursor.getDefaultCursor().getType();
    				}
    				panel.setCursor(Cursor.getPredefinedCursor(cursor));
    				panel.repaint();
    			}
     
    			@Override
    			public void mouseClicked(MouseEvent e) {
    				if ( e.getClickCount()==1 && e.getButton()==MouseEvent.BUTTON1 ) {
    					toggleSelection(rectangle, e);
    					toggleSelection(oval, e);
    					toggleSelection(imageBounds, e);
    					panel.repaint();
    				}
    			}
     
    			private void toggleSelection(Shape shape, MouseEvent e) {
    				if ( shape!=null && shape.contains(e.getPoint()) ) {
    					if ( selection.contains(shape) ) {
    						selection.remove(shape);
    					}
    					else {
    						selection.add(shape);
    					}
    				} 
    			}
     
     
    		};
    		panel.addMouseListener(mouseAdapter);
    		panel.addMouseMotionListener(mouseAdapter);
     
    		main.add(panel);
     
    		JPanel ui = new JPanel(new GridBagLayout());
     
    		JCheckBox checkboxProportional = new JCheckBox("Adaptation proportionelle");
    		checkboxProportional.setSelected(panel.isProportional());
    		checkboxProportional.addActionListener(e->panel.setProportional(checkboxProportional.isSelected()));
    		ui.add(checkboxProportional);
     
    		JCheckBox checkboxAdaptable = new JCheckBox("Adaptation");
    		checkboxAdaptable.setSelected(panel.isAdaptable());
    		checkboxAdaptable.addActionListener(e->panel.setAdaptable(checkboxAdaptable.isSelected()));
    		ui.add(checkboxAdaptable);
     
    		main.add(ui, BorderLayout.SOUTH);
     
    		frame.add(main);
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
    	private static Image image = loadImage();
    	private static Image loadImage() {
    		try {
    			return ImageIO.read(ScaleAdaptablePanel.class.getResource("suricate.jpg"));
    		} catch (Throwable e) {
    			return null;
    		}
    	}
    	private static Font font = new Font("SansSerif",Font.PLAIN,40);
    	private static boolean rectangleHover;
    	private static boolean ovalHover;
    	private static Set<Shape> selection = new HashSet<>();
    	private static void paint(Graphics2D g, ImageObserver observable) {
    		/*g.setColor(Color.LIGHT_GRAY);
    		g.drawRect(0,0,240,160);*/
    		g.setColor(Color.BLUE);
    		g.setFont(font);
    		g.drawString("Hello", 20,30);
    		g.setColor(Color.RED);
    		g.drawRect(120, 80, 100, 40);
    		if ( !ovalHover ) {
    			g.setColor(Color.ORANGE);
    			g.fillOval(130, 20, 50, 50);
    		}
     
    		if( image!=null) {
    			g.drawImage(image,10,50,observable);
    		}
    		if ( !selection.isEmpty() ) {
    			g.setColor(Color.MAGENTA);
    			AlphaComposite alpha = AlphaComposite.SrcOver.derive(0.5f);
    			Graphics2D g2d = (Graphics2D) g.create();
    			try {
    				g2d.setStroke(new BasicStroke(3));
    				for(Shape selected : selection) {
    					g2d.draw(selected);
    				}
    				g2d.setComposite(alpha);
    				for(Shape selected : selection) {
    					g2d.fill(selected);
    				}
    			}
    			finally {
    				g2d.dispose();
    			}
    		}
    		if ( rectangleHover ) {
    			g.setColor(Color.YELLOW);
    			g.fillRect(120+1, 80+1, 100-2, 40-2);
    		}
     
    		if ( ovalHover ) {
    			g.setColor(Color.GREEN);
    			g.fillOval(130, 20, 50, 50);
    		}
    	}
     
    	// fin démo
     
    }

  9. #9
    Modérateur
    Avatar de joel.drigo
    Homme Profil pro
    Ingénieur R&D - Développeur Java
    Inscrit en
    Septembre 2009
    Messages
    12 430
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur R&D - Développeur Java
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2009
    Messages : 12 430
    Points : 29 131
    Points
    29 131
    Billets dans le blog
    2
    Par défaut
    J'ai ajouté des commentaires à mon premier code pour les explications :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.Image;
    import java.awt.Insets;
    import java.awt.geom.AffineTransform;
    import java.awt.image.ImageObserver;
    import java.util.function.BiConsumer;
     
    import javax.imageio.ImageIO;
    import javax.swing.BorderFactory;
    import javax.swing.JCheckBox;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    /**
     * Cette classe permet de dessiner dans le fond du panel en adaptant le dessin à la taille du panel
     */
    public class ScaleAdaptablePanel extends JPanel {
     
    	private boolean proportional; // lorsque cette variable est à true, l'adaptation respecte le rapport hauteur/largeur voulu du dessin, à l'inverse lorsqu'elle est à false, la largeur et la hauteur sont adaptée à la dimension correspondante sans respecter le rapport hauteur/largeur du dessin
    	private boolean adaptable; // lorsque cette variable est à true l'échelle du dessin est adaptée à la taille du panel, sinon le dessin est fait dans les dimensions voulues du dessin
    	private final BiConsumer<Graphics2D,ImageObserver> painter; // le composant chargé du dessin
    	private final Dimension drawsize; // la taille voulue du dessin à l'échelle 1:1
     
            /**
              * @param painter le composant chargé du dessin 
              * @param width la largeur du dessin à l'échelle 1
              * @param height la hauteur du dessin à l'échelle 1
              */
    	public ScaleAdaptablePanel(BiConsumer<Graphics2D,ImageObserver> painter, int width, int height) {
    		this.painter=painter;
    		this.proportional=true; // adaptation proportionnelle par défaut
    		this.adaptable=true; // adaptation par défaut
    		this.drawsize = new Dimension(width, height);
    	}
     
            /**
              * @param proportional true pour adaptation proportionelle, false sinon
              */
    	public void setProportional(boolean proportional) {
    		boolean old = this.proportional;
    		this.proportional=proportional;
    		if ( old!=proportional ) { // si la valeur change
    			firePropertyChange("PROPORTIONAL", old, proportional); // on envoie un événement pour indiquer que la propriété a changé
    			repaint(); // on redessine pour tenir compte du changement
    		}
    	}
     
            /**
              * return true si l'adaptation est actuellement proportionnelle, ou false sinon 
              */
    	public boolean isProportional() {
    		return proportional;
    	}
     
            /**
              * @param adaptable true pour adaptation, false sinon
              */
    	public void setAdaptable(boolean adaptable) {
    		boolean old = this.adaptable;
    		this.adaptable=adaptable;
    		if ( old!=adaptable ) { // si la valeur change
    			firePropertyChange("ADAPTABLE", old, adaptable); // on envoie un événement pour indiquer que la propriété a changé
    			repaint(); // on redessine pour tenir compte du changement
    		}
    	}
     
            /**
              * return true si l'adaptation est actuellement active, ou false sinon 
              */
    	public boolean isAdaptable() {
    		return adaptable;
    	}
     
            /**
              * Détermine la taille actuelle du panel sans les bordures
              */
    	private Dimension getInnerDimension(Dimension dimension) {
    		Insets insets=getInsets();
    		return new Dimension(Math.max(0, dimension.width-insets.left-insets.right), 
    				Math.max(0, dimension.height-insets.top-insets.bottom));
    	}
     
            /**
              * Dessine le fond du composant à l'aider du painter
              */
    	@Override
    	public void paint(Graphics g) {
    		super.paint(g);
     
    		if ( painter!=null) { // si un painter a été fourni
     
    			Graphics2D g2D = (Graphics2D) g.create(); // on créé un nouveau contexte graphique pour éviter de modifier le contexte principal et d'avoir à le restaurer (comme on va changer l'échelle, on ne risquera pas d'impacter le reste éventuel du dessin de composants par Swing)		
     
                            // on tient compte du décalage dû à la bordure du composant (composant Border)
    			Insets insets=getInsets();
    			g2D.transform(AffineTransform.getTranslateInstance(insets.left, insets.top)); // on compose avec une translation pour se placer en haut à gauche de l'intérieur des bordures
     
    			if ( adaptable ) { // si l'adapdation est active
    				adapt(g2D); // on adapte
    			}
    			painter.accept(g2D,this); // on provoque le dessin en invoquant le painter
     
    			g2D.dispose(); // on libère les ressources créées pour le contexte graphique
     
    		}
     
    	}
     
            /**
              * Adapte l'échelle du dessin en tenant compte des dimensions voulues et des dimensions actuelles du panel, et des dimensions préférentielles
              */ 
    	private void adapt(Graphics2D g2d) {
                    // adapte de façon proportionnelle le dessin de la taille voulue à la taille préférentielle, en éliminant les bordures éventuelles (sinon on se superposerait aux bordures)
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getPreferredSize()));
    		if ( proportional ) { // si l'adaptation est proportionnelle, on s'adapte proportionnellement à la taille actuelle du composant
    			adaptProportional(g2d); // adaptation proportionnelle
    		}
    		else {
    			affineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize())); // on adapte la largeur et la hauteur indépendamment (non proportionelle )
    		}
    	}
     
            /**
               * adaptation proportionnelle de la taille du dessin à la taille du composant
               */
    	private void adaptProportional(Graphics2D g2d) {
    		proportionalAffineTransform(g2d, getInnerDimension(drawsize), getInnerDimension(getSize()));
    	}
     
            /**
               * détermine la transformation d'échelle nécessaire pour qu'un composant de dimension fromsize à l'échelle 1 se dessine à la dimension tosize, sans respecter le rapport largeur/hauteur voulu
               */
    	private void affineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth(); // le facteur d'échelle horizontal est le rapport entre la largeur cible et la largeur d'origine
    		double sy = tosize.getHeight()/fromsize.getHeight(); // le facteur d'échelle vertical est le rapport entre la hauteur cible et la hauteur d'origine
    		g2d.transform(AffineTransform.getScaleInstance(sx, sy)); // on créé une transformée affine d'échelle et on la compose avec la transformée actuelle affectée au contexte graphique
    	}
     
            /**
               * détermine la transformation d'échelle nécessaire pour qu'un composant de dimension fromsize à l'échelle 1 se dessine à la dimension tosize, en respectant le rapport largeur/hauteur voulu
               */
    	private void proportionalAffineTransform(Graphics2D g2d, Dimension fromsize, Dimension tosize) {
    		double sx = tosize.getWidth()/fromsize.getWidth(); // le facteur d'échelle horizontal est le rapport entre la largeur cible et la largeur d'origine
    		double height = fromsize.getHeight()*sx; // on calcule la hauteur proportionnelle avec le facteur d'échelle horizontal
    		if ( height > tosize.getHeight() ) { // si la hauteur transformée est trop grande pour la hauteur cible, on adapte verticalement
    			sx = tosize.getHeight()/fromsize.getHeight(); // le facteur d'échelle vertical est le rapport entre la hauteur cible et la hauteur d'origine (pour que le dessin ne dépasse pas la hauteur)
    		}
    		g2d.transform(AffineTransform.getScaleInstance(sx, sx)); // on applique un changement d'échelle de même facteur horizontal et vertical pour respecter le rapport largeur/hauteur voulu
    	}
     
    	// démo
     
    	public static void main(String[] args) {
    		JFrame frame = new JFrame();
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     
    		JPanel main = new JPanel(new BorderLayout());
     
                    Dimension dimension = new Dimension(260,180); // dimensions de notre dessin (dimensions maximales de ce qu'on va dessiner)
    		ScaleAdaptablePanel panel = new ScaleAdaptablePanel(ScaleAdaptablePanel::paint,dimension.width,dimension.height);
    		panel.setBorder(BorderFactory.createMatteBorder(10, 10, 10, 10,panel.getBackground()));
    		panel.setBackground(Color.WHITE);
    		panel.setPreferredSize(dimension); // on donne comme taille préférentielle la taille de notre dessin (sans adaptation, on veut que le layout permette de voir tout ce qu'on dessine). on n'a pas besoin de tenir compte des bordures parce que le composant ScaleAdaptablePanel adapte toujours le dessin à l'intérieur des bordures
    		main.add(panel);
     
    		JPanel ui = new JPanel(new GridBagLayout());
     
    		JCheckBox checkboxProportional = new JCheckBox("Adaptation proportionelle");
    		checkboxProportional.setSelected(panel.isProportional());
    		checkboxProportional.addActionListener(e->panel.setProportional(checkboxProportional.isSelected()));
    		ui.add(checkboxProportional);
     
    		JCheckBox checkboxAdaptable = new JCheckBox("Adaptation");
    		checkboxAdaptable.setSelected(panel.isAdaptable());
    		checkboxAdaptable.addActionListener(e->panel.setAdaptable(checkboxAdaptable.isSelected()));
    		ui.add(checkboxAdaptable);
     
    		main.add(ui, BorderLayout.SOUTH);
     
    		frame.add(main);
    		frame.pack();
    		frame.setLocationRelativeTo(null);
    		frame.setVisible(true);
     
    	}
     
    	private static Image image = loadImage();
    	private static Image loadImage() {
    		try {
    			return ImageIO.read(ScaleAdaptablePanel.class.getResource("suricate.jpg"));
    		} catch (Throwable e) {
    			return null;
    		}
    	}
    	private static Font font = new Font("SansSerif",Font.PLAIN,40);
    	private static void paint(Graphics2D g, ImageObserver observable) {
    		/* // pour debug : dessine un cadre autour du dessin
                    g.setColor(Color.LIGHT_GRAY);
    		g.drawRect(0,0,240,160);*/
    		g.setColor(Color.BLUE);
    		g.setFont(font);
    		g.drawString("Hello", 20,30);
    		g.setColor(Color.RED);
    		g.drawRect(120, 80, 100, 40);
    		g.setColor(Color.ORANGE);
    		g.fillOval(130, 20, 50, 50);
    		if( image!=null) {
    			g.drawImage(image,10,50,observable);
    		}
    	}
     
    	// fin démo
     
    }
    Il faut comprendre que l'utilisattion de la méthode Graphics.transform() permet de composer (les cumuler si tu préfères) les différentes transformées affines que l'on affecte, dans l'ordre des appels. Ainsi, on peut procéder par étapes :
    1. une translation pour tenir compte de la bordure éventuelle (donc par rapport aux insets, top et left (haut et gauche))
    2. une adaptation d'échelle proportionnelle de la taille (maximal) du dessin à la taille préférentielle du composant (getPreferredSize()) // on pourrait s'en passer, dans ce cas, le dessin pourrait dépasser du composant à l'échelle 1
    3. une adaptation d'échelle proportionnelle ou pas (selon la propriété proportional) à la taille actuelle du composant (getSize())

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Swing]JTable- Dimensionner les colonnes
    Par loutfi dans le forum Composants
    Réponses: 12
    Dernier message: 01/07/2011, 08h50
  2. swing et jbuilder
    Par lassale dans le forum JBuilder
    Réponses: 3
    Dernier message: 31/10/2003, 19h28
  3. [swing] probleme de memoire
    Par leBigouden dans le forum AWT/Swing
    Réponses: 6
    Dernier message: 23/05/2003, 14h19
  4. [SWING]jTable + Focus
    Par chady dans le forum Composants
    Réponses: 5
    Dernier message: 27/02/2003, 14h51
  5. [SWING][FONT] choisir la police par défaut
    Par narmataru dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 21/02/2003, 10h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo