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

2D Java Discussion :

[Java2D] ajouter des éléments graphiques à un objet existant


Sujet :

2D Java

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut [Java2D] ajouter des éléments graphiques à un objet existant
    Bonjour,

    Je dispose d'un JTextPane, et je souhaiterais dessiner un beau carré rose, par exemple au-dessus du 10ème caractère (ce n'est qu'un exemple). Evidemment, ce carré doit suivre la vie du JTextPane (agrandissement, scrollpane, etc...) [EDIT]

    Je pourrais bien sûr créer une classe fille et redéfinir son paintComponent(). Le problème est que ce JTextPane est créé par un autre module de mon application, que je ne contrôle pas (j'essaie de constituer une librairie) [EDIT]. Je ne peux donc pas "modifier" son paintComponent() ni créer une classe fille.

    [EDIT] En d'autres termes, je cherche à construire une méthode qui prend un JTextPane en paramètre, et qui modifie l'affichage de ce dernier, de telle manière que, à chaque fois que le JTextPane est redessiné, un carré rose soit ajouté au-dessus du 10ème caractère (ce n'est qu'un exemple).

    Existe-t-il néanmoins un moyen de modifier son affichage (rajout d'une forme géométrique) ?
    Mes premières recherches m'ont mené vers :
    - getGraphics(),
    - ComponentUI
    Dois-je creuser ces pistes ou existe-t-il une autre méthode mieux adaptée ?

    Merci d'avance pour votre aide,
    Cordialement,

    Nicolas

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    Une troisième piste peut-être :
    - mettre un listener sur la méthode paintComponent() du JTextPane avec l'API introspection [mais est-ce techniquement faisable ? Je n'ai pas encore regardé], de manière à rajouter le carré rose manuellement à la suite de chaque appel à paintComponent()

  3. #3
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Ce n'est pas strictement impossible, mais ce n'est vraiment pas pratique de faire ça en java.

    Il n'existe pas de Listener sur la méthode paint. Tu peux instrumenter, comme on dit, le byte code pour y caler un appel externe. Mais ce n'est vraiment pas pratique.

    Tu peux pister divers listeners, avec lesquels tu supputes qu'il sera probable qu'il y ait un appel au paint. Par exemple s'il y a un changement de la taille du composant, il est plus que probable qu'il y aura un appel au paint. Le problème de cette voie est qu'elle est techniquement casse-gueule pour repérer la position des composants, mais que de plus tu ne gères pas l'ordre d'appel des listeners ; il se peut très bien que le tien soit appelé avant celui qui redessine "vraiment" l'interface.

    Cete voie est toutefois bonne lorsque le truc que tu veux rajouter correspond à l'événement écouté. Par exemple, lors d'un redimensionnement de fenêtre, il est parfaitement adapté de placer sur cette fenêtre un petit rectangle montrant la dimension de cette fenêtre.

    Pour le reste, ne parlons pas des divers problèmes que posent la prise en compte du look and feel.

    Donc, ma position est qu'il vaut mieux s'abstenir.

    Si on y est obligé, une voie sympa est de trouver le moyen de placer un JPanel transparent par dessus le JPanel principal du composant (ou par dessous) (enfin... où tu peux), et d'y dessiner dessus ce qu'il faut. Ce n'est pas évident, parce qu'il faut composer avec le layout du composant.

    Par cette approche le problème de l'appel au paint est résolu par la mécanique swing, le problème de l'ordre d'appel des listeners aussi, il ne te reste plus qu'à espérer tomber juste dans le positionnement de tes ornementations

    Il arrive aussi souvent que les composants élaborés donnent accès à l'un de leurs sous-composants. Tu peux avoir plus de facilité à travailler à partir de là, qu'à partir du composant général.

    Tu peux également intervenir par le biais du composantUI, mais cette voie m'a toujours décue. Le look and feel est bien adapté pour concevoir de grands et beaux interfaces utilisateurs, qui prennent un an de développement à une équipe de 30 personnes ; il n'est pas bien adapté aux bidouillages de secours.

    Tu peux également tenter de profiter du Glass Pane (utilisé pour les popups, les tooltips, etc) (si, bien sûr, il n'y a pas de popup ou de tooltips perturbants).

    Et ainsi de suite.
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    Un grand merci pour ton message, qui confirme, de manière solide et structurée, ce qui n'était chez moi que de vagues intuitions.

    Encore merci,

    Nicolas

  5. #5
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Si tu ne peux pas hériter de JTextPane parce que tu ne contrôles pas la création, tu peux toujours l'encapsuler dans un JComponent, et dans ce JComponent tu affiches le JTextPane puis tes petits carrés roses !

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    Bonjour ®om,

    Merci de ton intervention.
    J'ai essayé de mettre en oeuvre ton idée.
    Le problème réside dans le fait que je fais clignoter mon carré rose (avec un Timer).
    Le carré rose est dessiné sur le JComponent.
    Le JComponent est "derrière" le JTextPane qu'il contient.
    Pour que le carré rose soit visible, je dois donc rendre le JTextPane() en setOpaque(false).
    Au moment du clignotement du carré rose, le contenu du JTextPane passe du noir au grisé : il clignote également.
    Ou bien y a-t-il quelquechose que je n'ai pas implémenté correctement...

    Cordialement,

    Nicolas

  7. #7
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Effectivement... quelqu'un sait-il pourquoi?

    Il faut donc utiliser un JLayeredPane, j'ai écrit un petit test qui marche.

    Cependant, je n'arrive pas à utiliser des layouts pour chaque couche du jlayeredpane... Donc j'ai fait le test avec des valeurs absolues :

    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
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
     
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JLayeredPane;
    import javax.swing.JTextPane;
    import javax.swing.Timer;
     
    public class TestLayer {
     
        static class MonComposant extends JLayeredPane {
     
            static class MonCarre extends JComponent {
     
                private boolean carre;
     
                public MonCarre() {
     
                    new Timer(500, new ActionListener() {
     
                        public void actionPerformed(ActionEvent e) {
                            setCarre(!carre);
                        }
                    }).start();
                }
     
                protected void paintComponent(Graphics g) {
                    if (carre) {
                        g.setColor(Color.RED);
                        g.fillRect(0, 0, getWidth(), getHeight());
                    }
                }
     
                private void setCarre(boolean carre) {
                    this.carre = carre;
                    repaint();
                }
            }
     
            private JTextPane txt;
     
            public MonComposant() {
                txt = new JTextPane();
                txt.setText("Exemple");
                txt.setBounds(0, 0, 100, 100);
     
                MonCarre carre = new MonCarre();
                carre.setBounds(40, 40, 50, 50);
     
                add(txt, DEFAULT_LAYER);
                add(carre, PALETTE_LAYER);
            }
        }
     
        public static void main(String[] args) {
            JFrame frame = new JFrame();
            frame.setBounds(100, 100, 150, 150);
            frame.getContentPane().add(new MonComposant());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        }
     
    }

  8. #8
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Citation Envoyé par ®om
    Effectivement... quelqu'un sait-il pourquoi?
    Heu... j'ai l'impression d'être le seul à ne pas comprendre cette solution d'encapsuler un carré rose... (peut être mon esprit s'arrête-t-il automatiquement sur certaines évocations annexes)

    Une hypothèse du clignotement intempestif du texte est que, dans le paint du carré rose, il faut réduire le clip au carré rose (ahum), sinon le paint redessine n'importe quoi partout ; ou alors il faut bien penser à tout couvrir en transparent (ahum ahum ) partout ailleurs. Bref...

    Citation Envoyé par ®om
    Cependant, je n'arrive pas à utiliser des layouts pour chaque couche du jlayeredpane...
    Oui, tout à fait normal : layers et layouts ne se comprennent pas. Soit tu prends un layout classique, et les composants sont bien séparés les uns des autres (ce qui est tout de même le rôle fondamental des layouts classiques) soit tu te fais ton layout autorisant les recouvrements de composants.

    De mon point de vue, il est préférable de se caler sur le composant de base (ici le JText), et de régler à la main la position du composant rose (esprit, reste avec moi).

    Décidemment, pas si facile...je vous l'avais dit
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  9. #9
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par gifffftane
    Heu... j'ai l'impression d'être le seul à ne pas comprendre cette solution d'encapsuler un carré rose... (peut être mon esprit s'arrête-t-il automatiquement sur certaines évocations annexes)
    Vu qu'il ne peut pas hériter du JTextPane, on pourrait imaginer un JComponent qui contienne le JTextPane et qui dessine un carré rose par dessus.
    Par exemple un JComponent avec une méthode paintComponent comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); //je croyais que ceci dessinait le JTextPane, car il a été ajouté avec add(...)
        g.setColor(Color.RED);
        g.fillRect(10,10,10,10); // malheureusement ceci est dessiné en dessous du JTextPane
    }
    Qu'en penses-tu?

    Quelque chose me semble mystérieux dans swing là...

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    J'ai essayé de creuser l'idée de ®om en clippant le carré rose comme le suggérait gifffftane, mais le texte en-dessous (en fait "au-dessus") clignote maintenant complètement.

    Je manque de temps et essaierai le JLayeredPane un peu plus tard. Merci beaucoup pour cette nouvelle idée, qui se rapproche plus de ce que je voulais faire : dessiner "au-dessus" du texte.

    Nicolas

  11. #11
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Citation Envoyé par ®om
    Vu qu'il ne peut pas hériter du JTextPane, on pourrait imaginer un JComponent qui contienne le JTextPane et qui dessine un carré rose par dessus.
    Qu'en penses-tu?

    Quelque chose me semble mystérieux dans swing là...
    Je ne vois pas pourquoi lui en particulier ne pourrait pas faire d'héritage du JTextPane, et j'avais compris sa question dans un sens un peu différent : il aurait un composant qui lui serait imposé (un JTextPane, donc) (ou autre chose), qu'il serait obligé d'utiliser, et dont il voudrait modifier le comportement en y plaquant un carré rose.

    Par conséquent ta solution serait impraticable. Impraticable par hypothèse, et non pas par conclusion. Or, elle semble séduire notre interlocuteur. C'est pour ça que je me montre surpris ; mais bien sûr, si ça marche, c'est très bien.

    Quand à savoir si ta solution pourrait marcher par conclusion, à mon avis, oui. Mais, comme je le dis et le répète, c'est plus de la chance qu'autre chose. La difficulté vient de coordonner un composant qu'on ne maîtrise pas avec un composant qu'on maitrise.

    Pour Nicolas_75, peux-tu nous faire un SSCCE, de façon à ce que l'on voye où tu en es sur du code, et qu'on résolve rapidement ton problème ?
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  12. #12
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par gifffftane
    Je ne vois pas pourquoi lui en particulier ne pourrait pas faire d'héritage du JTextPane
    Si tu récupères un objet déjà créé, tu ne peux pas en hériter...
    Par exemple, si tu récupères une instance de JTextPane par un factory createJTextPane(), tu ne peux pas récupérer autre chose.


    Mais même en dehors de cet exemple, je ne comprends pas pourquoi faire un super.paintComponent(g); suivi d'un g.fillRect(...) ne paint pas le rectangle au-dessus...

  13. #13
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    ®om, j'ai quelque mal à comprendre ta proposition exemple.

    La mécanique paint de swing peind d'abord le composant, puis les composants contenus dans le composant.

    Donc ton g.fillRect dans paintComponent ne sert normalement à rien. Il est redondant avec le paint du composant carré lui même.

    Ensuite, je ne vois pas où tu mets le boolean carre à true.

    Il doit être possible de faire fonctionner ton exemple, mais je préfère travailler sur l'exemple que nous fournira Nicolas_75, si cela ne te fait rien, puisque c'est lui qui semble avoir le souci pratique.
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  14. #14
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    [QUOTE=gifffftane]®om, j'ai quelque mal à comprendre ta proposition exemple.

    Citation Envoyé par gifffftane
    La mécanique paint de swing peind d'abord le composant, puis les composants contenus dans le composant.
    Oui, ça ok, mais à quel endroit il peint les composants contenus?

    Moi je croyais que c'était dans le paintComponent(Graphics) de JComponent (à la fin de la méthode), qu'il faisait les appels aux composants contenus...
    Apparemment non...

    Citation Envoyé par gifffftane
    Donc ton g.fillRect dans paintComponent ne sert normalement à rien. Il est redondant avec le paint du composant carré lui même.
    C'était dans l'hypothèse où l'on n'a pas de composant Carre... Mais juste un JComponent qui contient un JTextPane, sur lequel on ajoute un carré dans paintComponent.

    Citation Envoyé par gifffftane
    Ensuite, je ne vois pas où tu mets le boolean carre à true.
    setCarre(!carre);

    Citation Envoyé par gifffftane
    Il doit être possible de faire fonctionner ton exemple, mais je préfère travailler sur l'exemple que nous fournira Nicolas_75, si cela ne te fait rien, puisque c'est lui qui semble avoir le souci pratique.
    Oui, oui, pas de problème, d'ailleurs l'exemple que je fournis marche, donc y'a rien à modifier...
    Mais je me demande comment faire autrement, c tout

  15. #15
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Citation Envoyé par ®om
    Moi je croyais que c'était dans le paintComponent(Graphics) de JComponent (à la fin de la méthode), qu'il faisait les appels aux composants contenus...
    C'est un peu compliqué, mais à ce que je comprends c'est dans la méthode paint de JComponent qu'il appelle d'abord paintComponent, puis paintChildren, enfants qu'il dessine à l'endroit où ils sont, c'est à dire à l'endroit où le layout les a déjà placé.

    Voici l'extrait de code copirit sun de la méthode paint de JComponent :
    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
     
    if (!paintCompleted) {
     // Will ocassionaly happen in 1.2, especially when printing.
     if (clipRect == null) {
        co.setClip(clipX, clipY, clipW, clipH);
     }
     if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) {
        if (!printing) {
    	paintComponent(co);
    	paintBorder(co);
        }
        else {
    	printComponent(co);
    	printBorder(co);
        }
     }
     if (!printing) {
        paintChildren(co);
     }
     else {
        printChildren(co);
     }
    }
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  16. #16
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    Bonjour,

    Désolé du silence, j'étais en déplacement professionnel.

    Ci-dessous trois SSCCE.

    SSCCE 1 : ce que je veux obtenir : clignotement d'un carré rouge sur le 11ème caractère. Ce résultat est obtenu "en trichant", c'est-à-dire en contrôlant la création du JTextPane, ce qui m'est en fait interdit.

    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
    // appeler le fichier SSCCE1.java
    // par Nicolas_75
    // 25 mars 2007
    // http://www.developpez.net/forums/showthread.php?p=1861724
     
    import java.awt.*;
    import java.awt.geom.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
     
    public class SSCCE1 extends JTextPane {
     
        // flag pour le clignotement :
        private boolean flag;
     
        // Constructeur :
        public SSCCE1() {
            // on met un texte court dans le JTextPane :
            try {
                this.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
            } catch (BadLocationException ble) { ble.printStackTrace(); }
            // mise en place du Timer pour le clignotement :
            java.util.Timer timer = new java.util.Timer();
            TimerTask timerTask = new TimerTask() {
                public void run() {
                    flag = !flag; // on bascule le flag
                    SSCCE1.this.repaint(); // on repaint le composant
                }
            };
            timer.schedule(timerTask, 0, 300);
        }
     
        // on dessine le carré rouge sur le 10ème caractère du JTextPane
        @Override protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (flag) { // uniquement si flag est true... (pour le clignotement)
                Graphics2D g2 = (Graphics2D) g;
                try {
                    Rectangle r1 = this.modelToView(10);
                    Rectangle r2 = this.modelToView(11);
                    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
                    g2.setColor(Color.red);
                    Rectangle2D.Double r = new Rectangle2D.Double(r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
                    g2.fill(r);
                } catch (BadLocationException ble) { ble.printStackTrace(); }
            }
        }
     
        // point d'entrée du programme (mise en place de la JFrame englobante) :
        public static void main(String[] args) {
            JFrame myFrame = new JFrame();
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.add(new SSCCE1());
            myFrame.pack();
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);
        }    
    }
    Maintenant essayons à partir d'un JTextPane pré-existant...

    SSCCE 2 : mise en oeuvre de l'idée ci-dessus. Inclusion du JTextPane dans un JComponent, sur lequel on dessine le carré rouge. Problème : le reste du texte clignote aussi (de la couleurs noire à un grisé).

    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
    // appeler le fichier SSCCE2.java
    // par Nicolas_75
    // 25 mars 2007
    // http://www.developpez.net/forums/showthread.php?p=1861724
     
    import java.awt.*;
    import java.awt.geom.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
     
    public class SSCCE2 extends JComponent {
     
        private boolean flag;
     
        private JTextPane textpane;
     
        // Constructeur
        public SSCCE2(JTextPane textpane0) {
            this.textpane = textpane0;
            this.setPreferredSize(this.textpane.getPreferredSize());
            this.setLayout(new BorderLayout());
            // on inclut le JTextPane dans le composant :
            this.add(this.textpane, BorderLayout.CENTER);
            this.textpane.setOpaque(false);
            // timer pour le clignotement :
            java.util.Timer timer = new java.util.Timer();
            TimerTask timerTask = new TimerTask() {
                public void run() {
                    flag = !flag; // on bascule le flag
                    SSCCE2.this.repaint(); // on repaint le composant
                }
            };
            timer.schedule(timerTask, 0, 300);
        }
     
        // on dessine le carré rouge sur le 10ème caractère du JTextPane
        @Override protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (flag) { // uniquement si flag est true... (pour le clignotement)
                Graphics2D g2 = (Graphics2D) g;
                try {
                    Rectangle r1 = this.textpane.modelToView(10);
                    Rectangle r2 = this.textpane.modelToView(11);
                      g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
                    g2.setColor(Color.red);
                    Rectangle2D.Double r = new Rectangle2D.Double(r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
                    g2.fill(r);
                } catch (BadLocationException ble) { ble.printStackTrace(); }
            }
        }
     
        // point d'entrée du programme (mise en place de la JFrame englobante) :
        public static void main(String[] args) {
            JFrame myFrame = new JFrame();
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JTextPane textpane0 = new JTextPane();
            try {
                textpane0.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
            } catch (BadLocationException ble) { ble.printStackTrace(); }
            myFrame.add(new SSCCE2(textpane0));
            myFrame.pack();
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);
        }  
    }
    SSCCE 3 : mise en oeuvre de l'idée ci-dessus d'inclusion du JTextPane dans un JLayeredPane. Cela semble fonctionner, à coups de setBounds(). Problème : l'icône de la souris n'est pas l'habituelle "barre" des composants texte, mais la flèche, probablement en raison de la couche supérieur.

    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
    // appeler le fichier SSCCE3.java
    // par Nicolas_75
    // 25 mars 2007
    // http://www.developpez.net/forums/showthread.php?p=1861724
     
    import java.awt.*;
    import java.awt.geom.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
     
    public class SSCCE3 extends JLayeredPane {
     
        private JTextPane textpane;
     
        private class MonCarre extends JComponent {
     
            private boolean flag;
     
            public MonCarre() {
                // timer pour le clignotement :
                java.util.Timer timer = new java.util.Timer();
                TimerTask timerTask = new TimerTask() {
                    public void run() {
                        flag = !flag; // on bascule le flag
                        MonCarre.this.repaint(); // on repaint le composant
                    }
                };
                timer.schedule(timerTask, 0, 300);
            }
     
            @Override protected void paintComponent(Graphics g) {
                if (flag) { // uniquement si flag est true... (pour le clignotement)
                    Graphics2D g2 = (Graphics2D) g;
                    try {
                        Rectangle r1 = SSCCE3.this.textpane.modelToView(10);
                        Rectangle r2 = SSCCE3.this.textpane.modelToView(11);
                        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
                        g2.setColor(Color.red);
                        Rectangle2D.Double r = new Rectangle2D.Double(r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
                        g2.fill(r);
                    } catch (BadLocationException ble) { ble.printStackTrace(); }
                }
            }
        }
     
        // Constructeur
        public SSCCE3(JTextPane textpane0) {
            this.textpane = textpane0;
            this.setPreferredSize(this.textpane.getPreferredSize());
            this.textpane.setBounds(0, 0, (int) this.textpane.getPreferredSize().getWidth(), (int) this.textpane.getPreferredSize().getHeight());
            this.add(this.textpane, JLayeredPane.DEFAULT_LAYER);
            MonCarre carre = new MonCarre();
            carre.setBounds(0, 0, (int) this.textpane.getPreferredSize().getWidth(), (int) this.textpane.getPreferredSize().getHeight());
            this.add(carre, JLayeredPane.PALETTE_LAYER);
        }
     
     
        // point d'entrée du programme (mise en place de la JFrame englobante) :
        public static void main(String[] args) {
            JFrame myFrame = new JFrame();
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JTextPane textpane0 = new JTextPane();
            try {
                textpane0.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
            } catch (BadLocationException ble) { ble.printStackTrace(); }
            myFrame.add(new SSCCE3(textpane0));
            myFrame.pack();
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);
        }
    }
    Merci pour votre aide,

    Nicolas

  17. #17
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Merveilleux ! Avec ça au moins, on peut travailler !

    Je te propose 2 réponses : la première est un SSCCE 2 qui fonctionne. La deuxième est une autre approche, qui fonctionne aussi.

    Au niveau de ton SSCCE 2, la solution est le contrôle du clip, comme je te l'avais suggéré. Je sais bien que ce n'est pas bien documenté, et que l'on peut donc considérer que c'est à la limite du bug JDK, mais une fois qu'on a compris qu'il fallait contrôler et profiter du clip, tout se met à fonctionner à merveille.

    Voici :
    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
     
    // appeler le fichier SSCCE2.java
    // par Nicolas_75
    // 25 mars 2007
    // http://www.developpez.net/forums/showthread.php?p=1861724
     
    package praline;
     
    import java.awt.*;
    import java.awt.geom.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
     
    public class SSCCE2 extends JComponent {
     
        private boolean flag;
     
        private JTextPane textpane;
     
        // Constructeur
        public SSCCE2(JTextPane textpane0) {
            this.textpane = textpane0;
            this.setPreferredSize(this.textpane.getPreferredSize());
            this.setLayout(new BorderLayout());
            // on inclut le JTextPane dans le composant :
            this.add(this.textpane, BorderLayout.CENTER);
            this.textpane.setOpaque(false);
            // timer pour le clignotement :
            java.util.Timer timer = new java.util.Timer();
            TimerTask timerTask = new TimerTask() {
                public void run() {
     
                    flag = !flag; // on bascule le flag
                    SSCCE2.this.repaint();
                }
            };
            timer.schedule(timerTask, 0, 1000);
        }
     
        // on dessine le carré rouge sur le 10ème caractère du JTextPane
        @Override protected void paintComponent(Graphics g)
        {
          if (flag)
          { // uniquement si flag est true... (pour le clignotement)
            Rectangle2D rouge;
            Rectangle bounds;
     
            rouge = rectangleRouge();
            bounds = null;
            if (rouge != null)
              bounds = rouge.getBounds();
     
            if (bounds != null)
            {
              Graphics2D g2;
     
              g2 = (Graphics2D) g.create(bounds.x, bounds.y, bounds.width, bounds.height);
              g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
              g2.setColor(Color.red);
              g2.fill(g2.getClipBounds());
              g2.dispose();
            }
          }
        }
     
        // point d'entrée du programme (mise en place de la JFrame englobante) :
        public static void main(String[] args) {
            JFrame myFrame = new JFrame();
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JTextPane textpane0 = new JTextPane();
            try {
                textpane0.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
            } catch (BadLocationException ble) { ble.printStackTrace(); }
            myFrame.add(new SSCCE2(textpane0));
            myFrame.pack();
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);
        }
     
        Rectangle2D rectangleRouge()
        {
          Rectangle2D rouge;
     
          rouge = null;
          try
          {
            Rectangle r1;
            Rectangle r2;
     
            r1 = this.textpane.modelToView(10);
            r2 = this.textpane.modelToView(11);
            if ((r1 != null)  && (r2 != null))
              rouge = new Rectangle2D.Double(
                    r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
          }
          catch (BadLocationException bad)
          {
            bad.printStackTrace();
          }
          return rouge;
        }
    }
    Aussi fait gaffe que j'ai supprimé l'appel à super.paintComponent, qui me semble inutile ici. Mais il peut redevenir nécessaire, pour je ne sais quel look and feel, ou telle ou telle raison.

    Je te propose également une autre approche, qui ne nécessite pas d'écrire une nouvelle méthode paintComponent, et est donc applicable pour tous les composants.

    La difficulté de ce genre d'exercice que tu nous proposes est de se caler sur le bon événement. Or, ici, à cause du fait qu'on fait clignoter un truc, il n'y a pas vraiment de bon événement. Et même, si on en rate un, le prochain sera le bon, comme le clignotement est par essence un peu rapide (sinon c'est pas un clignotement). Le bon événement est donc le timer clignotant lui même.

    Donc, il est jouable de dessiner le carré rouge depuis le timer lui même, rendant inutile une intervention sur paintComponent, et permettant une simplification générale du code. Voici :
    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
     
    /*
     * RougeDirect.java
     *
     * Created on 25 mars 2007, 14:43
     *
     * To change this template, choose Tools | Template Manager
     * and open the template in the editor.
     */
     
    package praline;
     
    import java.awt.*;
    import java.awt.geom.Rectangle2D;
    import java.util.TimerTask;
    import javax.swing.JFrame;
    import javax.swing.JTextPane;
    import javax.swing.text.BadLocationException;
     
    /**
     *
     * @author herve
     */
    public class RougeDirect extends javax.swing.JComponent
    {
        private boolean flag;
     
        private JTextPane textpane;
     
        // Constructeur
        public RougeDirect(JTextPane textpane0) {
            this.textpane = textpane0;
            this.setPreferredSize(this.textpane.getPreferredSize());
            this.setLayout(new BorderLayout());
            // on inclut le JTextPane dans le composant :
            this.add(this.textpane, BorderLayout.CENTER);
            this.textpane.setOpaque(false);
            // timer pour le clignotement :
            java.util.Timer timer = new java.util.Timer();
            TimerTask timerTask = new TimerTask()
            {
              public void run()
              {
                Graphics2D graph;
     
                graph = (Graphics2D)textpane.getGraphics();
                graph.setClip(rectangleRouge());
     
                flag = !flag; // on bascule le flag
                if (flag)
                {
                  graph.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
                  graph.setColor(Color.red);
                  graph.fill(graph.getClipBounds());
                  graph.dispose();
                }
                else
                  paint(graph);
                graph.dispose();
              }
            };
            timer.schedule(timerTask, 0, 1000);
        }
     
        public static void main(String[] args) throws Exception
        {
          EventQueue.invokeLater(new java.lang.Runnable()
          {
            public void run()
            {
              JFrame myFrame = new JFrame();
              myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              JTextPane textpane0 = new JTextPane();
              try
              {
                textpane0.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
              }
              catch (BadLocationException ble)
              { ble.printStackTrace(); }
              myFrame.add(new SSCCE2(textpane0));
              myFrame.pack();
              myFrame.setLocationRelativeTo(null);
              myFrame.setVisible(true);
            }
          });
        }
     
        Rectangle2D rectangleRouge()
        {
          Rectangle2D rouge;
     
          rouge = null;
          try
          {
            Rectangle r1;
            Rectangle r2;
     
            r1 = this.textpane.modelToView(10);
            r2 = this.textpane.modelToView(11);
            if ((r1 != null)  && (r2 != null))
              rouge = new Rectangle2D.Double(
                    r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
          }
          catch (BadLocationException bad)
          {
            bad.printStackTrace();
          }
          return rouge;
        }
    }
    Cette solution n'est pas à mettre dans toutes les mains, puisque j'y fais fi de toutes les précautions usuelles à la programmation swing (thread awt, pas de dessin en dehors de paint, etc...) Mais ici, tout ces bidouillages me semblent justifiés. Donc, profitons-en.

    ... et j'ai diminué la fréquence du timer, car chez moi il clignotait en même temps que le curseur, ce qui me perturbait.

    ... et merci pour cet excellent petit exercice.
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

  18. #18
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    C'est plutôt moi qui te remercie !!
    Je prends le temps d'analyser tout cela, et je reviens ici...

  19. #19
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    429
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 429
    Points : 475
    Points
    475
    Par défaut
    Me revoilà...

    SSCCE 3 : un simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
    permet de régler le problème du curseur

    SSCCE 2 : c'est un peu bête, mais je ne parviens pas à changer la couleur du fond du composant texte. Tout idée serait la bienvenue...

    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
    // appeler le fichier Exemple094_Clignotement_SSCCE2.java
    // http://www.developpez.net/forums/showthread.php?p=1861724
    // http://www.developpez.net/forums/showthread.php?t=297865
     
    // Méthode avec clip
     
    import java.awt.*;
    import java.awt.geom.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
     
    public class Exemple094_Clignotement_SSCCE2 extends JComponent {
     
        private boolean flag;
     
        private JTextPane textpane;
     
     
        // Constructeur
        public Exemple094_Clignotement_SSCCE2(JTextPane textpane0) {
            this.textpane = textpane0;
            this.setPreferredSize(this.textpane.getPreferredSize());
            this.setLayout(new BorderLayout());
            // on inclut le JTextPane dans le composant :
            this.add(this.textpane, BorderLayout.CENTER);
            this.textpane.setOpaque(false);
            this.setBackground(Color.red); // NE FONCTIONNE PAS
            java.util.Timer timer = new java.util.Timer();
            TimerTask timerTask = new TimerTask() {
                public void run() {
                    flag = !flag; // on bascule le flag
                    Exemple094_Clignotement_SSCCE2.this.repaint(); // on repaint le composant
                }
            };
            timer.schedule(timerTask, 0, 300);
        }
     
        // on dessine le carré rouge sur le 10ème caractère du JTextPane
        @Override protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (flag) { // uniquement si flag est true... (pour le clignotement)
     
                Rectangle2D rouge = null;
                try {
                    Rectangle r1 = this.textpane.modelToView(10);
                    Rectangle r2 = this.textpane.modelToView(11);
                    if ((r1 != null)  && (r2 != null))
                        rouge = new Rectangle2D.Double(
                                r1.getX(), r1.getY(), r2.getX()-r1.getX(), r1.getHeight());
                } catch (BadLocationException bad) {
                    bad.printStackTrace();
                }
     
                Rectangle bounds = null;
                if (rouge != null)
                    bounds = rouge.getBounds();
     
                if (bounds != null) {
                    Graphics2D g2;
                    g2 = (Graphics2D) g.create(bounds.x, bounds.y, bounds.width, bounds.height);
                    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f)); // transparence partielle
                    g2.setColor(Color.red);
                    g2.fill(g2.getClipBounds());
                    g2.dispose();
                }
            }
        }
     
        // point d'entrée du programme (mise en place de la JFrame englobante) :
        public static void main(String[] args) {
            JFrame myFrame = new JFrame();
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JTextPane textpane0 = new JTextPane();
            try {
                textpane0.getStyledDocument().insertString(0, "Bonjour à tous ! :-) ", null);
            } catch (BadLocationException ble) { ble.printStackTrace(); }
            myFrame.add(new Exemple094_Clignotement_SSCCE2(textpane0));
            myFrame.pack();
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);
        }
    }
    Merci encore !

    Nicolas

  20. #20
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Mais... si tu mets un arrière plan rouge et que tu fais clignoter des petits rectangles à l'avant plan, que va-t-il rester de visible ??

    À part ça, le JTextPane est un composant un peu élaboré, et les choses qui paraissent simples, comme setOpaque, sont mal gérées en swing avec les composants élaborés. En tous les cas, là, cela ne marche pas. Et ton setBackground sur le composant du fond est masqué par le JTextPane.

    Par contre si tu fais un setBackground sur le JTextPane, ça fonctionne. setOpaque ne fonctionne pas, mais setBackground fonctionne, il y a des mystères dans le monde à swing...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
            this.textpane.setBackground(Color.BLUE);
    ... par exemple.
    Mieux que Google, utilisez Sur Java spécialisé sur la plate-forme java !
    Pour réaliser vos applications Java dans le cadre de prestations, forfait, conseil, contactez-moi en message privé.

Discussions similaires

  1. [JSON] ajouter des éléments à un objet
    Par beegees dans le forum jQuery
    Réponses: 5
    Dernier message: 25/07/2014, 20h45
  2. Réponses: 5
    Dernier message: 29/07/2010, 09h00
  3. Ajouter des éléments graphiques à un fichier GEF
    Par caro_caro dans le forum Eclipse Platform
    Réponses: 0
    Dernier message: 12/02/2009, 16h34
  4. Réponses: 3
    Dernier message: 25/01/2009, 17h32

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