我有一个JLabel的子类,它构成了我GUI的一个组件。我已经实现了将组件从一个容器拖放到另一个容器的能力,但没有任何视觉效果。我想让这个JLabel在项目从一个容器拖动到另一个容器期间跟随光标。我想我可以创建一个玻璃窗格并在那里绘制它。但是,即使我将组件添加到玻璃窗格,将组件设置为可见,并将玻璃窗格设置为可见,并将玻璃窗格设置为不透明,我仍然看不到该组件。我知道该组件有效,因为我可以将其添加到内容窗格并显示它。
如何将组件添加到玻璃窗格?
最后想出了如何让简单的例子工作。谢谢,@ akf。我能够使这个解决方案适应我原来的问题,允许我删除大约60行Java2D代码,这些代码手动呈现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 :(得分:13)
下面的示例代码显示了如何在棋盘上拖动棋子。它使用JLayeredPane而不是玻璃窗格,但我确信这些概念是相同的。那就是:
a)将玻璃窗格添加到根窗格中 b)使玻璃板可见 c)将组件添加到玻璃板,确保边界有效 d)使用setLocation()来动画拖动组件
编辑:添加了修复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);
使用布局管理器时,您永远不会使用setSize()或setBounds()方法。在您的情况下,您只需将首选大小设置为(0,0),因为这是所有组件的默认大小。
当您将标签添加到框架时,它会起作用,因为框架内容窗格的默认布局管理器是边框布局,因此将忽略标签的首选大小,并使标签成为框架的大小。
但是,默认情况下,JPanel使用的FlowLayout确实尊重组件的首选大小。由于首选大小为0,因此无需绘画。
此外,玻璃窗格需要可见才能进行涂漆。
我建议你阅读Swing tutorial。有关布局管理器如何工作以及玻璃窗格如何工作以及每个部分都有工作示例的部分。
编辑:下面添加的示例代码:
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);
}
}
答案 1 :(得分:12)
虽然与问题相关,但@camickr引用的JLayeredPane
example承认以下适应性,这突出了mouseReleased()
对现有组件的影响。
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);
}
答案 2 :(得分:3)
因为我在Swing上关注了Romain Guy的博客很长一段时间。我有一个你可能感兴趣的链接。他发布了源代码 - 它使用了GlassPane用于DnD效果。
http://jroller.com/gfx/entry/drag_and_drop_effects_the
我自己从来没有对DnD使用过汽水动画/效果,所以不能再评论: - |
答案 3 :(得分:3)
除了指向已提供的LayerPane示例的指针外,原始代码的问题还在于设置标签的首选大小。你在JLabel大小之前设置它,所以你的:
l.setPreferredSize(l.getSize());
无效。另一方面,如果您在拨打setBounds
电话后拨打该电话,您将看到所需的结果。考虑到这一点,请重新订购:
l.setPreferredSize(l.getSize());
l.setBounds(10, 10, 50, 20);
看起来像这样:
l.setBounds(10, 10, 50, 20);
l.setPreferredSize(l.getSize());