2010-04-01 10 views
9

Tengo una subclase de JLabel que forma parte de mi GUI. Implementé la capacidad de arrastrar y soltar el componente de un contenedor a otro, pero sin ningún efecto visual. Quiero que este JLabel siga el cursor durante el arrastre del artículo de un contenedor a otro. Pensé que podría simplemente crear un panel de vidrio y dibujarlo allí. Sin embargo, incluso después de agregar el componente al panel de vidrio, establecer el componente visible, establecer el panel de cristal como visible y configurar el panel de cristal como opaco, aún así no veo el componente. Sé que el componente funciona porque puedo agregarlo al panel de contenido y hacer que aparezca.Colocación del componente en el panel de vidrio

¿Cómo agrego un componente al cristal?


Finalmente se explicó cómo hacer funcionar el ejemplo simple. Gracias, @akf. Pude adaptar esta solución a mi problema original, lo que me permitió eliminar ~ 60 líneas de código Java2D que representaban manualmente una 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

Ha intentado establecer el componente como el GlassPane en lugar de añadirlo a la GlassPane? – willcodejavaforfood

+0

Este componente representa un mosaico en un juego de palabras, por lo que solo tiene 37 * 37 y usa bordes para el efecto. No veo cómo podría transformar esto en un panel de vidrio, ya que interferiría con las dimensiones del mosaico. –

+0

¿Podemos ver algún código? Específicamente, cómo agrega el componente al panel de vidrio y lo establece visible. –

Respuesta

3

Además de los punteros a los ejemplos LayerPane ya proporcionada, el problema con sus centros de código original de todo el ajuste del tamaño preferido de la etiqueta. Usted lo configuró antes de que el JLabel haya sido dimensionado, entonces su:

l.setPreferredSize(l.getSize()); 

es ineficaz. Si, por otro lado, realiza esa llamada después de realizar su llamada al setBounds, verá los resultados deseados. Con esto en mente, cambiar el orden siguiente:

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

a tener este aspecto:

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

El siguiente código de ejemplo muestra cómo arrastrar una pieza de ajedrez alrededor de un tablero de ajedrez. Utiliza JLayeredPane en lugar de un panel de vidrio, pero estoy seguro de que los conceptos serían los mismos. Es decir:

a) añadir el panel de vidrio para el panel raíz
b) hacer que el panel de vidrio visibles
c) añadir el componente a la placa de vidrio asegurándose de que los límites son válidos
d) utilizar setLocation () para animar el arrastre del componente

Editar: se ha añadido código para fijar 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); 

al utilizar controladores de distribución que nunca usa la setSize() o() setBounds métodos. En su caso, simplemente establece el tamaño preferido en (0, 0) ya que este es el tamaño predeterminado de todos los componentes.

Funciona cuando agrega la etiqueta al marco porque el administrador de diseño predeterminado para el panel de contenido del marco es un diseño de borde, por lo tanto, se ignora el tamaño preferido de la etiqueta y el tamaño del marco .

Sin embargo, de forma predeterminada, un JPanel usa un FlowLayout que respeta el tamaño preferido del componente. Como el tamaño preferido es 0, no hay nada que pintar.

Además, el panel de vidrio debe hacerse visible para poder pintarlo.

Le sugiero que lea el Swing tutorial. Hay una sección sobre cómo funcionan los administradores del diseño y sobre cómo funcionan los paneles de vidrio y cada sección tiene ejemplos de trabajo.

Editar: Ejemplo de código añade a continuación:

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

Tengo problemas para cambiar el JLayeredPane porque utilizo GridBagLayout para el diseño de mi formulario principal. Esto complica el DRAG_LAYER ya que se requiere tener el mismo administrador de diseño que el resto del panel de contenido, pero no quiero un administrador de diseño, ya que solo se usará para arrastrar. –

+0

Mi ejemplo utiliza un GridLayout y el DRAG_LAYER no necesita usar el mismo administrador de diseño. De hecho, no usa gestores de diseño ya que está posicionando manualmente los componentes. No estoy sugiriendo que necesite usar un panel en capas solo para que los conceptos sean los mismos. Es decir, dado que un panel de vidrio tampoco usa un administrador de disposición, usted es responsable de establecer los límites del componente de forma adecuada. De todos modos, no puedo ayudar más. Te di el código de trabajo, no sé cómo tu código es diferente. – camickr

+3

Comience por crear un SSCCE simple (http://sscce.org) que muestre un panel de vidrio con un JLabel en una posición específica. Una vez que domine eso, pasará a animar la ubicación de la etiqueta mientras la arrastra con el mouse. Si no puede hacer que SSCCE funcione, tiene un código simple para publicar y podemos brindarle sugerencias más concretas. – camickr

3

Desde que había estado siguiendo los blogs de Romain Guy en el oscilación por un largo tiempo. Tengo un enlace que podría interesarte. Lanzó la fuente, que usaba un GlassPane para efectos DnD.

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

yo nunca utilizó un efervescente animación/efecto sobre dnd, así que no puedo comentar más lejos: - |

+0

Desafortunadamente, eso usa imágenes dibujadas directamente sobre el cristal en lugar de componentes hechos por Swing. –

+0

No debería importar que el ejemplo dibuje imágenes. Para usar componentes, solo agrega el componente al cristal y asegúrate de haber establecido los límites correctamente. – camickr

+0

El problema era que estaba _ agregando el componente al panel de cristal y estableciendo sus límites, pero aún no se mostraba. Lo único que puedo entender es que no tengo idea de cómo establecer los límites correctamente en un componente. –

11

Aunque tangencial a la pregunta, la JLayeredPaneexample citado por @camickr admite la adaptación siguiente, que pone de relieve el efecto de mouseReleased() más de un componente existente.

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: Más sobre la personalización de los glifos [aquí] (http://stackoverflow.com/q/18686199/230513). – trashgod

Cuestiones relacionadas