1. Faire ses propres composants Faire une interface graphique avec Java2d et Swing (suite et fin) Définir le rendu graphique d’un composant 2. Programmer l’interaction Définir le rendu graphique d’un composant • • Le rendu graphique des composants est assuré par le thread awt (cf. cours suivant). Pour assurer la fluidité, Java2D utilise un mécanisme de double-buffering. • Le thread awt se charge d’appeler repaint() sur les composants quand cela est nécessaire : le composant devient visible, il est redimensionné…repaint() appelle paint() dès que c’est possible. On ne force pas un appel à paint(), on appelle repaint(). • Pour les composants standard, le rendu déjà défini par Swing, on le redéfinit pas en général. • Pour définir le rendu graphique d’un composant, on fait généralement une sous-classe de JComponent ou JPanel dans laquelle on redéfinit la méthode paint. • La méthode paint(Graphics g) permet de récupérer un contexte graphique dans lequel dessiner Il faut caster en Graphics2D pour disposer de toutes les fonctionnalités de la librairie Java2D public class MyComponent extends JPanel { public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; … } } Dessiner des primitives graphiques Java2D public class MyComponent extends JPanel { Image logo; public MyComponent() { super(); Toolkit tk = Toolkit.getDefaultToolkit(); logo = tk.getImage("UPS11-logo.jpg"); try { MediaTracker tracker = new MediaTracker(this); tracker.addImage(logo, 0); tracker.waitForID(0); } catch(Exception e) { e.printStackTrace(); } } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.fillOval(150, 250, 200, 100); g2d.drawRect(200, 40, 100, 200); g2d.drawString("Hello", 30, 300); g2d.drawImage(logo, 0, 0, this); } (0, 0) Le contexte du graphics2D Le contexte graphique est défini par : • des attributs de style : - java.awt.Paint (ex: java.awt.Color…) - java.awt.Stroke (ex: java.awt.BasicStroke…) - java.awt.Font • des attributs géométriques : - java.awt.geom.AffineTransform • des attributs définissant la qualité : - java.awt.RenderingHints } Le contexte du graphics2D Des primitives plus évoluées interface java.awt.Shape public void paint(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.setColor(Color.RED); g2d.fillOval(150, 250, 200, 100); g2d.setStroke(new BasicStroke(4)); g2d.drawRect(200, 40, 100, 200); Ex : Ellipse, Rectangle, Segments, Polylignes… g2d.setColor(Color.BLACK); g2d.setFont(new Font("verdana", Font.ITALIC, 24)); g2d.drawString("Hello", 30, 300); AffineTransform transf = new AffineTransform(); transf.translate(10, 20); transf.rotate(Math.PI/4); g2d.setTransform(transf); g2d.drawImage(logo, 0, 0, this); } GeneralPath gp = new GeneralPath(); gp.moveTo(60, 30); gp.lineTo(90, 60); gp.lineTo(30, 60); gp.closePath(); g2d.fill(gp); Les évènements 1. Faire ses propres composants 2. Programmer l’interaction Comment capturer les actions de l’utilisateur (souris, clavier…) et exécuter les traitements correspondants? Programmation évènementielle en utilisant des objets écoutant les évènements arrivant sur un composant graphique. Ecouteur Composant graphique écoute AWT vs. Glut • Glut : Une fonction de rappel (callback) par type d’événement pour toute la fenêtre AWT vs. Glut • Glut : "approche callback" abcgudiew • AWT : Une fonction de rappel (callback) par écouteur et par type d’événement. Plusieurs écouteurs peuvent être attachés à un même composant. Un écouteur peut être attaché sur plusieurs composants graphiques. En cas de clic: selectObjet Callback de touche clavier Si c’est la touche suppr : Si je suis dans la partie haute: effacer forme sélectionnée Si je suis dans la partie basse: effacer la dernière lettre • AWT : "approche événements" Ecouteur1 de touche clavier écoute En cas de touche suppr: effacer forme sélectionnée Ecouteur2 de touche clavier abcgudiew écoute En cas de touche suppr: effacer la dernière lettre Les écouteurs en Java • Ce sont des instances de classes implémentant les interfaces XListener : – Les évènements bas niveau (souris, clavier) Ex : MouseListener, MouseMotionListener, MouseWheelListener, KeyListener – Les évènements haut niveau (bouton logiciel enclenché, case à cocher sélectionnée..) Ex : ActionListener, ItemListener… Les écouteurs bas niveau • MouseListener – – – – – Press : appui d’un bouton Release : relâchement d’un bouton Click : appui+relâchement d’un même bouton Enter : entrée du curseur dans un composant graphique Leave : sortie du curseur d’un composant graphique • MouseMotionListener – Move : mouvement de souris sans bouton appuyé – Drag : mouvement de souris avec bouton appuyé • MouseWheelListener – Wheel : roulement de la roulette de la souris • KeyListener • …que l’on attache à des composants graphiques : composant.addMouseListener(ecouteur) – Press : appui sur une touche de clavier – Release : relâchement d’une touche de clavier – Type : caractère tapé Remarque : Avec Glut, nous n’avions que les événements de bas niveau Exemple simple Exemple simple class MyListener implements MouseListener { public void mouseClicked(MouseEvent evt) { System.out.println("clic en ("+evt.getX()+","+evt.getY()+")"); } public void mousePressed(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseEntered(MouseEvent evt) { } public void mouseLeaved(MouseEvent evt) { } } class MyListener implements MouseListener { public void mouseClicked(MouseEvent evt) { System.out.println("clic en ("+evt.getX()+","+evt.getY()+")"); } public void mousePressed(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseEntered(MouseEvent evt) { } public void mouseLeaved(MouseEvent evt) { } } public class Application { public static void main(String[] args) { JFrame frame = new JFrame("Example"); JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(400, 400)); panel.addMouseListener(new MyListener()); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } public class Application { public static void main(String[] args) { JFrame frame = new JFrame("Example"); JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(400, 400)); panel.addMouseListener(new MyListener()); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } On peut éviter d’écrire ces lignes XListener / XAdapter Pour la plupart des interfaces XListener, il existe des classes abstraites XAdapter. Xadapter est une classe abstraite implémentant les méthodes de XListener avec des corps vides. Il suffit de sous-classer et de redéfinir les méthodes qui ont de l’intérêt. Un paradigme de programmation souvent utilisé Fusionner le composant graphique et l’écouteur class MyListener implements MouseListener { public void mouseClicked(MouseEvent evt) { System.out.println("clic en ("+evt.getX()+","+evt.getY()+")"); } public void mousePressed(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseEntered(MouseEvent evt) { } public void mouseLeaved(MouseEvent evt) { } } écoute En cas de clic: selectObjet DEVIENT class MyListener extends MouseAdapter { public void mouseClicked(MouseEvent evt) { System.out.println("clic en ("+evt.getX()+","+evt.getY()+")"); } } Exemple class MyPanel extends JPanel implements MouseListener { public MyPanel() { On ne peut pas utiliser super(); la classe abstraite addMouseListener(this); MouseAdapter } public void mouseClicked(MouseEvent evt) { System.out.println("clic en ("+evt.getX()+","+evt.getY()+")"); } public void mousePressed(MouseEvent evt) { } public void mouseReleased(MouseEvent evt) { } public void mouseEntered(MouseEvent evt) { } public void mouseLeaved(MouseEvent evt) { } } public class Application { public static void main(String[] args) { JFrame frame = new JFrame("Example"); JPanel panel = new MyPanel(); panel.setPreferredSize(new Dimension(400, 400)); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } Classes anonymes On a souvent besoin que d’une instance d’un écouteur. On n’a pas besoin de nommer une nouvelle classe, on utilise le mécanisme de classe anonyme. a est l’unique instance de A’ A a = new A() { public void methodA() { … } Sous-classe de A = A’ }; Exemple Un formalisme pour décrire l’interaction: les machines à états • Forme graphique public class Application { public static void main(String[] args) { JFrame frame = new JFrame("Example"); JPanel panel = new JPanel(); panel.setPreferredSize(new Dimension(400, 400)); panel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent evt) { System.out.println( "clic en ("+evt.getX()+","+evt.getY()+")"); } }); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } Les machines à états : définition Les machines à états sont une façon simple de décrire précisément l'interaction. Elles sont basées sur les automates à états finis. Une machine est constituée d'un ensemble d'états (cercles), dont un état initial qui est l'état de départ. Le passage d'un état à un autre est décrit par des transitions (flèches), qui sont déclenchées par l'arrivée d'évènements tels que le déplacement de la souris, l'appui sur un bouton... A chaque transition peuvent être associées : - une garde = une condition qui, si elle n’est pas vraie, empêche la transition d’être déclenchée. - une action = du code exécuté lorsque la transition est déclenchée. A chaque état peuvent être associées : - une action d'entrée = du code exécuté lorsque l'état devient l’état courant. une action de sortie = du code exécuté lorsque l'état cesse d’être l'état courant. Click() CreateEllipse() PressOn(ellipse) Select(ellipse) drag START Drag() Move(ellipse) Release() Deselect(ellipse) • Forme textuelle Etat start { Transition Transition } Etat drag { Transition Transition } PressOn(ellipse) => drag {Select(ellipse)} Click => start {CreateEllipse()} Move => drag {Move(ellipse)} Release => start {Deselect(ellipse)} Du bas niveau au haut niveau JButton button = new JButton("Ok"); button.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me1){ System.out.println("OK"); MouseReleased } Invoke }); START MouseEntered invokable MousePre ssed Mo use En Mo use Lea ved in MouseReleased Cancel tere d Le bouton est déclenché out par une séquence d’événements de bas niveau plus complexe qu’un simple clic et qui se produit sur un certain type de composant graphique (un bouton logiciel). Évènements de Haut Niveau Exemple du JButton • Quand un bouton est invoqué JButton button = new JButton("Ok"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt){ System.out.println("bouton "+evt.getSource()+"invoqué"); } }); • Quand une case est cochée • Quand un item de menu est sélectionné •… Exemple du JCheckbox Renvoie l’objet source de l’événement " getComponent() renvoie le composant graphique sur lequel l’événement a eu lieu (Sur cet exemple, getSource et getComponent renvoie la même résultat car c’est le composant graphique lui-même qui émet l’évènement Ecouteurs de fenêtre Remarque : Le programme Java ne termine pas quand on ferme la fenêtre. JCheckBox checkBox = new JCheckBox("Select"); button.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt){ if (evt.getStateChange() == ItemEvent.DESELECTED) System.out.println("déselectionné"); else System.out.println("selectionné"); } }); On peut écouter les évènements de la fenêtre (WindowListener) et quitter le programme quand la fenêtre se ferme. !Un raccourci syntaxique utile frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE); Questions diverses