尝试使用JLayeredPane拖动某些内容



我正试图将drag-n-drop操作放入我的程序中;我发现下面的例子说明了我要做的很多事情:

package sandbox;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

/**
* Example showing the use of a JLayeredPane to implement dragging an object
* across a JPanel containing other objects.
* <P>
* Basic idea: Create a JLayeredPane as a container, then put the JPanel containing
* the application's components or whatever in the JLayeredPane.DEFAULT_LAYER layer of that layered pane.
* The code is going to drag a JComponent object by calling JComponent.setPosition(x,y)
* on the component. When a mouse is clicked on the panel to start the dragging, put the
* component on the drag layer of the layered pane; as it is dragged, continue to call
* setPosition to move it. When the mouse is released, use the x.y position of the release
* to decide what to do with it next.   
* 
*/
public class ChessBoard extends JFrame implements MouseListener, MouseMotionListener
{
private static final long serialVersionUID = 1L;
JLayeredPane layeredPane;
JPanel chessBoard;
JLabel chessPiece;
int xAdjustment;
int yAdjustment;

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

//  Use a Layered Pane for this application

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

//debug
LayoutManager lm = layeredPane.getLayout();
System.out.println("Layered pane layout name is " + (lm == null? "<null>" : lm.getClass().getName()));

//  Add a chess board to the Layered Pane on the DEFAULT layer
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
// We use an 8x8 grid, and put a JPanel with BorderLayout on each square. 
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.gray : Color.white );
chessBoard.add( square );
}
}

// Add a few pieces to the board
// we do this with an ImageIcon that gets added to the square's panel.
ImageIcon duke = new ImageIcon("granary.gif");  // this is the image to add to each space.
addDuke(duke, 0);
addDuke(duke, 6);
addDuke(duke, 15);
addDuke(duke, 20);
}

private void addDuke(ImageIcon duke, int boardPosition)
{
JLabel pieceLabel = new JLabel(duke);
JPanel piecePanel = (JPanel)chessBoard.getComponent(boardPosition);
piecePanel.add(pieceLabel);
}

/*
**  Add the selected chess piece to the dragging layer so it can be moved
*/
public void mousePressed(MouseEvent e)
{
// get the component where the user pressed; iff that's not a panel,
// we'll put it on the dragging layer.
chessPiece = null;                                         // change1 swap the change1 lines
// chessPiece = new JLabel(new ImageIcon("house1x1.gif")); // change1
Component c =  chessBoard.findComponentAt(e.getX(), e.getY());

if (c instanceof JPanel) return;

// get the location of the panel containing the image panel, i.e.,
// the square's panel. we adjust the location to which we move the
// piece by this amount so the piece doesn't 'snap to' the cursor 
// location.
Point parentLocation = c.getParent().getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY();
chessPiece = (JLabel)c; // change2 - comment out
chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);

layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); // evidently this removes it from the default layer also.
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);   // evidently this works for whatever layer contains the piece.
// also, the layout manager of its new home is evidently not the same as lower layers.
}

/*
**  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);
Container parent = null;
if (c instanceof JLabel)
{
parent = c.getParent(); // there's a piece on the square already; remove it from the panel.
parent.remove(0);
}
else
{
parent = (Container)c;
}
parent.add( chessPiece );     // this adds the piece back to the default layer
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);
}
}

这就完成了棋盘的任务,即允许用户将棋盘上的任何棋子拖动到不同的正方形。

在我正在编写的应用程序中,直到用户单击启动拖动操作的东西,被拖动的项目才存在。我很难弄清楚如何创作并让它出现。

我当前的尝试是在标有"change1"one_answers"change2"的行上;用"change1"替换两行,并用"change2"注释掉其中一行。换句话说,在按下鼠标时创建JLabel,并(希望(拖动它。但当我运行它时,图像不会在按下或拖动时显示,而是在拖动结束时显示在正方形上。

我错过了什么?我对JLayeredPane有点困惑,javadoc说它将遵循布局规则,但不知道布局规则是应用于所有层上的所有组件,还是仅应用于底部,还是应用于所有图层,但分别应用,什么?我不认为这是布局问题,但我不知道出了什么问题。我需要在某个地方进行某种UI更新吗?我以为添加组件会使面板失效。

最初的代码是在您点击棋子的情况下编写的。

现在您要单击一个空单元格,该单元格需要进行以下更改。

  1. 棋盘由每个单元格中的J面板组成。一些单元格将包含一个JLabel,表示一个棋子。mousePressed事件中的当前逻辑要求您单击JLabel,否则将跳过一些处理

您需要删除:

//if (c instanceof JPanel) return;
  1. 默认情况下,Swing组件在创建时的大小为0

您需要给它一个大小:

chessPiece.setSize( chessPiece.getPreferredSize() );
  1. 标签的定位逻辑基于查找单击的组件相对于父组件的位置。由于没有标签,因此此逻辑现在基于相对于分层窗格的面板

您需要调整此逻辑,使其再次相对于父面板:

//Point parentLocation = c.getParent().getLocation();
Point parentLocation = c.getLocation();

我更新的mousePressed方法看起来像:

public void mousePressed(MouseEvent e)
{
// get the component where the user pressed; iff that's not a panel,
// we'll put it on the dragging layer.
//chessPiece = null;                                         // change1 swap the change1 lines
chessPiece = new JLabel(new ImageIcon("dukewavered.gif")); // change1
chessPiece.setSize( chessPiece.getPreferredSize() );
Component c =  chessBoard.findComponentAt(e.getX(), e.getY());
//if (c instanceof JPanel) return;
// get the location of the panel containing the image panel, i.e.,
// the square's panel. we adjust the location to which we move the
// piece by this amount so the piece doesn't 'snap to' the cursor
// location.
//Point parentLocation = c.getParent().getLocation();
Point parentLocation = c.getLocation();
xAdjustment = parentLocation.x - e.getX();
yAdjustment = parentLocation.y - e.getY();
//chessPiece = (JLabel)c; // change2 - comment out
chessPiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
layeredPane.add(chessPiece, JLayeredPane.DRAG_LAYER); // evidently this removes it from the default layer also.
layeredPane.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}

请注意,以上更改将打破旧的拖动现有标签的功能。如果您需要两者的功能,那么您的逻辑将取决于您是单击JLabel(在这种情况下使用旧逻辑(还是单击JPanel(在那种情况下使用新逻辑(。

最新更新