2010-04-01 3 views
9

J'ai une sous-classe de JLabel qui forme un composant de mon interface graphique. J'ai implémenté la possibilité de faire glisser et déposer le composant d'un conteneur à un autre, mais sans effets visuels. Je veux que ce JLabel suive le curseur pendant le déplacement de l'article d'un conteneur à l'autre. J'ai pensé que je pourrais juste créer une vitre et dessiner là-bas. Cependant, même après avoir ajouté le composant à la vitre, mettre le composant visible, et mettre la vitre visible, et définir la vitre comme opaque, je ne vois toujours pas le composant. Je sais que le composant fonctionne parce que je peux l'ajouter au volet de contenu et l'afficher.Placer un composant sur le volet de verre

Comment ajouter un composant à la vitre?


Enfin figuré comment obtenir l'exemple simple de travail. Merci, @akf. J'ai été capable d'adapter cette solution à mon problème original, ce qui m'a permis d'enlever ~ 60 lignes de code Java2D qui rendaient manuellement une représentation du JLabel.

package test; 

import java.awt.Color; 

import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.border.LineBorder; 

public class MainFrame extends JFrame { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     MainFrame mf = new MainFrame(); 
     mf.setSize(400, 400); 
     mf.setLocationRelativeTo(null); 
     mf.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
     mf.setGlassPane(new JPanel()); 

     JLabel l = new JLabel(); 
     l.setText("Hello"); 
     l.setBorder(new LineBorder(Color.BLACK, 1)); 
     l.setBounds(10, 10, 50, 20); 
     l.setBackground(Color.RED); 
     l.setOpaque(true); 
     l.setPreferredSize(l.getSize()); 

     //mf.add(l); 
     ((JPanel)mf.getGlassPane()).add(l); 
     mf.getGlassPane().setVisible(true); 

     mf.setVisible(true); 
    } 
} 
+0

Avez-vous essayé de placer votre appareil comme GlassPane au lieu d'ajouter à la GlassPane? – willcodejavaforfood

+0

Ce composant représente une tuile dans un jeu de mots, donc il est seulement 37 * 37 et utilise des bordures pour l'effet. Je ne vois pas comment je pourrais transformer cela en une vitre car cela nuirait aux dimensions de la tuile. –

+0

Pouvons-nous voir du code? Spécifiquement comment vous ajoutez le composant à la vitre et le placez visible. –

Répondre

3

Outre les pointeurs vers les exemples de LayerPane déjà fourni, le problème avec vos centres de code d'origine dans le cadre de la taille préférée de votre étiquette. Vous l'avez défini avant que le JLabel ne soit dimensionné, ainsi:

l.setPreferredSize(l.getSize()); 

est inefficace. Si, d'autre part, vous faites cet appel après avoir appelé setBounds, vous verrez les résultats souhaités. Dans cet esprit, réordonner ceci:

l.setPreferredSize(l.getSize()); 
l.setBounds(10, 10, 50, 20); 

à ressembler à ceci:

l.setBounds(10, 10, 50, 20); 
l.setPreferredSize(l.getSize()); 
13

L'exemple de code ci-dessous montre comment faire glisser une pièce d'échecs autour d'un échiquier. Il utilise JLayeredPane au lieu d'un volet en verre, mais je suis sûr que les concepts seraient les mêmes. C'est:

a) ajouter la vitre dans le volet racine
b) faire la vitre visible
c) ajouter le composant à la vitre assurant que les limites sont valables
d) utiliser de setLocation () pour animer l'entraînement du composant

Edit: code ajouté pour fixer SSCCE

JLabel l = new JLabel(); 
l.setText("Hello"); 
l.setBorder(new LineBorder(Color.BLACK, 1)); 
// l.setPreferredSize(l.getSize()); 
// l.setBounds(10, 10, 50, 20); 
((JPanel)mf.getGlassPane()).add(l); 

mf.setVisible(true); 
mf.getGlassPane().setVisible(true); 

Lorsque vous utilisez des gestionnaires de mise en page que vous ne jamais utiliser les setSize() ou setBounds(). Dans votre cas, il vous suffit de définir la taille préférée à (0, 0), car il s'agit de la taille par défaut de tous les composants. Il fonctionne lorsque vous ajoutez l'étiquette au cadre car le gestionnaire de disposition par défaut pour le volet de contenu du cadre est une disposition de bordure. Par conséquent, la taille préférée de l'étiquette est ignorée et l'étiquette est la taille de l'image. .

Cependant, par défaut, un JPanel utilise un FlowLayout qui respecte la taille préférée du composant. Puisque la taille préférée est 0, il n'y a rien à peindre.

De plus, la vitre doit être visible pour pouvoir être peinte.

Je vous suggère de lire le Swing tutorial. Il y a une section sur le fonctionnement des gestionnaires de disposition et sur le fonctionnement des vitres et chaque section a des exemples pratiques.

Edit: Exemple de code ajouté ci-dessous:

import java.awt.*; 
import java.awt.event.*; 
import java.util.*; 
import javax.swing.*; 

public class ChessBoard extends JFrame implements MouseListener, MouseMotionListener 
{ 
    JLayeredPane layeredPane; 
    JPanel chessBoard; 
    JLabel chessPiece; 
    int xAdjustment; 
    int yAdjustment; 

    public ChessBoard() 
    { 
     Dimension boardSize = new Dimension(600, 600); 

     // Use a Layered Pane for this this application 

     layeredPane = new JLayeredPane(); 
     layeredPane.setPreferredSize(boardSize); 
     layeredPane.addMouseListener(this); 
     layeredPane.addMouseMotionListener(this); 
     getContentPane().add(layeredPane); 

     // Add a chess board to the Layered Pane 

     chessBoard = new JPanel(); 
     chessBoard.setLayout(new GridLayout(8, 8)); 
     chessBoard.setPreferredSize(boardSize); 
     chessBoard.setBounds(0, 0, boardSize.width, boardSize.height); 
     layeredPane.add(chessBoard, JLayeredPane.DEFAULT_LAYER); 

     // Build the Chess Board squares 

     for (int i = 0; i < 8; i++) 
     { 
      for (int j = 0; j < 8; j++) 
      { 
       JPanel square = new JPanel(new BorderLayout()); 
       square.setBackground((i + j) % 2 == 0 ? Color.red : Color.white); 
       chessBoard.add(square); 
      } 
     } 

     // Add a few pieces to the board 

     ImageIcon duke = new ImageIcon("dukewavered.gif"); // add an image here 

     JLabel piece = new JLabel(duke); 
     JPanel panel = (JPanel)chessBoard.getComponent(0); 
     panel.add(piece); 
     piece = new JLabel(duke); 
     panel = (JPanel)chessBoard.getComponent(15); 
     panel.add(piece); 
    } 

    /* 
    ** Add the selected chess piece to the dragging layer so it can be moved 
    */ 
    public void mousePressed(MouseEvent e) 
    { 
     chessPiece = null; 
     Component c = chessBoard.findComponentAt(e.getX(), e.getY()); 

     if (c instanceof JPanel) return; 

     Point parentLocation = c.getParent().getLocation(); 
     xAdjustment = parentLocation.x - e.getX(); 
     yAdjustment = parentLocation.y - e.getY(); 
     chessPiece = (JLabel)c; 
     chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment); 

     layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); 
     layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 
    } 

    /* 
    ** Move the chess piece around 
    */ 
    public void mouseDragged(MouseEvent me) 
    { 
     if (chessPiece == null) return; 

     // The drag location should be within the bounds of the chess board 

     int x = me.getX() + xAdjustment; 
     int xMax = layeredPane.getWidth() - chessPiece.getWidth(); 
     x = Math.min(x, xMax); 
     x = Math.max(x, 0); 

     int y = me.getY() + yAdjustment; 
     int yMax = layeredPane.getHeight() - chessPiece.getHeight(); 
     y = Math.min(y, yMax); 
     y = Math.max(y, 0); 

     chessPiece.setLocation(x, y); 
    } 

    /* 
    ** Drop the chess piece back onto the chess board 
    */ 
    public void mouseReleased(MouseEvent e) 
    { 
     layeredPane.setCursor(null); 

     if (chessPiece == null) return; 

     // Make sure the chess piece is no longer painted on the layered pane 

     chessPiece.setVisible(false); 
     layeredPane.remove(chessPiece); 
     chessPiece.setVisible(true); 

     // The drop location should be within the bounds of the chess board 

     int xMax = layeredPane.getWidth() - chessPiece.getWidth(); 
     int x = Math.min(e.getX(), xMax); 
     x = Math.max(x, 0); 

     int yMax = layeredPane.getHeight() - chessPiece.getHeight(); 
     int y = Math.min(e.getY(), yMax); 
     y = Math.max(y, 0); 

     Component c = chessBoard.findComponentAt(x, y); 

     if (c instanceof JLabel) 
     { 
      Container parent = c.getParent(); 
      parent.remove(0); 
      parent.add(chessPiece); 
      parent.validate(); 
     } 
     else 
     { 
      Container parent = (Container)c; 
      parent.add(chessPiece); 
      parent.validate(); 
     } 
    } 

    public void mouseClicked(MouseEvent e) {} 
    public void mouseMoved(MouseEvent e) {} 
    public void mouseEntered(MouseEvent e) {} 
    public void mouseExited(MouseEvent e) {} 

    public static void main(String[] args) 
    { 
     JFrame frame = new ChessBoard(); 
     frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
     frame.setResizable(false); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 
} 
+0

J'ai un peu de mal à changer le JLayeredPane depuis que j'utilise GridBagLayout pour la mise en page de mon formulaire principal. Cela gâte le DRAG_LAYER, car il est nécessaire d'avoir le même gestionnaire de disposition que le reste du volet de contenu, mais je ne veux pas de gestionnaire de disposition car cela ne servira qu'à faire glisser. –

+0

Mon exemple utilise un GridLayout et le DRAG_LAYER n'a pas besoin d'utiliser le même gestionnaire de disposition. En fait, il n'utilise pas les gestionnaires de disposition puisque vous positionnez manuellement les composants. Je ne suggère pas que vous avez besoin de swith et d'utiliser un volet en couches seulement que les concepts devraient être les mêmes. C'est parce qu'un volet en verre n'utilise pas non plus un gestionnaire de disposition, vous êtes responsable de définir correctement les limites du composant. De toute façon je ne peux pas aider plus loin. Je vous ai donné le code de travail, je ne sais pas comment votre code est différent. – camickr

+3

Commencez par créer un simple SSCCE (http://sscce.org) qui affiche un volet en verre avec un JLabel à une position spécifique. Une fois que vous maîtrisez cela, vous passez à l'animation de l'emplacement de l'étiquette que vous faites glisser avec la souris. Si vous ne pouvez pas faire fonctionner le SSCCE, vous avez du code simple à afficher et nous pouvons vous fournir des suggestions plus concrètes. – camickr

3

Depuis que j'avait suivi les blogs de Romain Guy sur la balançoire pour longtemps. J'ai un lien qui pourrait vous intéresser. Il a sorti la source - qui utilisait un GlassPane pour les effets DnD.

http://jroller.com/gfx/entry/drag_and_drop_effects_the

Je me suis jamais a fait utiliser une animation gazeuse/effet DnD, donc je ne peux plus loin: - |

+0

Malheureusement, cela utilise des images dessinées directement sur la vitre plutôt que des composants rendus par Swing. –

+0

Il ne devrait pas importer que l'exemple dessine des images. Pour utiliser les composants, il vous suffit d'ajouter le composant au volet de verre et de vous assurer que vous avez correctement défini les limites. – camickr

+0

Le problème était que je _ajas_ ajoutant le composant à la vitre et définissant ses limites, mais il ne s'affichait toujours pas. La seule chose que je peux comprendre est que je n'ai aucune idée de comment définir les limites correctement sur un composant. –

11

Bien que tangent à la question, le JLayeredPaneexample cité par @camickr admet l'adaptation suivante, qui met en évidence l'effet de mouseReleased() sur un composant existant.

public ChessBoard() { 
    ... 
    // Add a few pieces to the board 
    addPiece(3, 0, "♛"); 
    addPiece(4, 0, "♚"); 
    addPiece(3, 7, "♕"); 
    addPiece(4, 7, "♔"); 
} 

static Font font = new Font("Sans", Font.PLAIN, 72); 

private void addPiece(int col, int row, String glyph) { 
    JLabel piece = new JLabel(glyph, JLabel.CENTER); 
    piece.setFont(font); 
    JPanel panel = (JPanel) chessBoard.getComponent(col + row * 8); 
    panel.add(piece); 
} 
+0

@DavidKroukamp: Plus sur la personnalisation des glyphes [ici] (http://stackoverflow.com/q/18686199/230513). – trashgod

Questions connexes