我有一个游戏板,有8个我希望能够将它们移动到jpanel上的任何地方。目前,我只能进行流量或网格布局,但这不会产生预期的结果。长期目标是能够独立地点击一块并将其拖动到所需的位置/位置。 (包括在其他部分之上)
任何建议或意见都是最受欢迎的...(感谢Hovercraft Full Of Eels,MadProgrammer,peeskillet,Andrew Thompson的早期建议)....
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Image;
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;
public class FireflyGameBoard extends JFrame implements MouseListener,
MouseMotionListener
{
JLayeredPane layeredPane;
JPanel gameBoard;
// JPanel background;
JLabel gamePiece;
int xAdjustment;
int yAdjustment;
ImageIcon bgicon;
Image bg;
String Rimagepath = "/Users/yournamehere/Documents/workspace/MotionGraphicsTest/resources/";
public FireflyGameBoard()
{
Dimension boardSize = new Dimension(1920, 1080);
bgicon = new ImageIcon(Rimagepath + "Backdroptest.png");
bg = bgicon.getImage();
ImagePanel background = new ImagePanel(new ImageIcon(Rimagepath
+ "Backdroptest.png").getImage());
// Use a Layered Pane for this this application
layeredPane = new JLayeredPane();
getContentPane().add(layeredPane);
layeredPane.setPreferredSize(boardSize);
layeredPane.addMouseListener(this);
layeredPane.addMouseMotionListener(this);
// Add a chess board to the Layered Pane
gameBoard = new ImagePanel(bg);
layeredPane.add(background, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(gameBoard, JLayeredPane.MODAL_LAYER);
gameBoard.setLayout(new FlowLayout());
gameBoard.setPreferredSize(boardSize);
gameBoard.setBounds(0, 0, boardSize.width, boardSize.height);
// for (int i = 0; i < 64; i++)
// {
// JPanel square = new JPanel(new BorderLayout());
// gameBoard.add(square);
//
// // square.setBackground(null);
// }
// Add a few pieces to the board
JLabel piece = new JLabel(new ImageIcon(Rimagepath + "alliance.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece2.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece3.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece4.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "reaper.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece6.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece7.png"));
gameBoard.add(piece);
piece = new JLabel(new ImageIcon(Rimagepath + "piece8.png"));
gameBoard.add(piece);
}
@Override
public void mousePressed(MouseEvent e)
{
gamePiece = null;
Component c = gameBoard.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();
gamePiece = (JLabel) c;
gamePiece.setLocation(e.getX() + xAdjustment, e.getY() + yAdjustment);
gamePiece.setSize(gamePiece.getWidth(), gamePiece.getHeight());
layeredPane.add(gamePiece, JLayeredPane.DRAG_LAYER);
}
// Move the chess piece around
@Override
public void mouseDragged(MouseEvent me)
{
if (gamePiece == null)
return;
gamePiece.setLocation(me.getX() + xAdjustment, me.getY() + yAdjustment);
}
// Drop the chess piece back onto the chess board
@Override
public void mouseReleased(MouseEvent e)
{
if (gamePiece == null)
return;
gamePiece.setVisible(false);
Component c = gameBoard.findComponentAt(e.getX(), e.getY());
if (c instanceof JLabel)
{
Container parent = c.getParent();
parent.remove(0);
parent.add(gamePiece);
}
else
{
Container parent = (Container) c;
parent.add(gamePiece);
}
gamePiece.setVisible(true);
}
@Override
public void mouseClicked(MouseEvent e)
{
}
@Override
public void mouseMoved(MouseEvent e)
{
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
public static void main(String[] args)
{
JFrame frame = new FireflyGameBoard();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
P.S。这不是学校作业,我的g / f是一个狂热的棋盘游戏玩家,我想让她成为她最喜欢的数字版本......
答案 0 :(得分:2)
一种方法是尝试生成自包含的工作单元,即该部分负责管理它自己的拖动,并且单元/网格负责管理丢弃。
Piece
是一个可移动的游戏块,可以拖动到新的位置。
该作品本身负责管理用于初始化拖动过程的DragGestureRecognizer
......
因为我想在作品中显示一个图标,所以我选择覆盖JLabel
,因为它为此提供了核心功能......
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.image.BufferedImage;
import javax.swing.JLabel;
public class PieceLabel extends JLabel {
private DragGestureHandler dragGestureHandler;
private DragGestureRecognizer dgr;
public PieceLabel() {
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
}
@Override
public void addNotify() {
super.addNotify();
if (dgr == null) {
dragGestureHandler = new DragGestureHandler(this);
dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, dragGestureHandler);
}
}
@Override
public void removeNotify() {
if (dgr != null) {
dgr.removeDragGestureListener(dragGestureHandler);
dragGestureHandler = null;
}
dgr = null;
super.removeNotify();
}
public static class DragGestureHandler implements DragGestureListener, DragSourceListener {
private PieceLabel piece;
private Container parent;
public DragGestureHandler(PieceLabel child) {
this.piece = child;
}
public PieceLabel getPiece() {
return piece;
}
protected void setParent(Container parent) {
this.parent = parent;
}
protected Container getParent() {
return parent;
}
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
// When the drag begins, we need to grab a reference to the
// parent container so we can return it if the drop
// is rejected
Container parent = getPiece().getParent();
setParent(parent);
// Remove the panel from the parent. If we don't do this, it
// can cause serialization issues. We could over come this
// by allowing the drop target to remove the component, but that's
// an argument for another day
parent.remove(getPiece());
// Update the display
parent.invalidate();
parent.repaint();
// Create our transferable wrapper
Transferable transferable = new PieceTransferable(getPiece());
// Start the "drag" process...
DragSource ds = dge.getDragSource();
// ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);
BufferedImage image = BoardDrag.createBufferedImage(piece.getIcon(), piece);
Point pp = piece.getLocation();
Point dp = dge.getDragOrigin();
int x = image.getWidth() / 2;
int y = image.getHeight() / 2;
ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), image, new Point(x, y), transferable, this);
}
@Override
public void dragEnter(DragSourceDragEvent dsde) {
}
@Override
public void dragOver(DragSourceDragEvent dsde) {
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {
}
@Override
public void dragExit(DragSourceEvent dse) {
}
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
// If the drop was not sucessful, we need to
// return the component back to it's previous
// parent
if (!dsde.getDropSuccess()) {
getParent().add(getPiece());
getParent().invalidate();
getParent().repaint();
}
}
}
}
单元格/网格就是这样,它构成了整个网格/板上的单个元素。它可以包含Piece
(实际上,您可以轻松配置它拒绝其他所有内容)
这管理DropTarget
,负责检测什么东西掉在上面......
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class Cell extends JPanel {
private DropTarget dropTarget;
private DropHandler dropHandler;
public Cell() {
setLayout(new BorderLayout());
}
@Override
public void addNotify() {
super.addNotify();
if (dropHandler == null) {
dropHandler = new DropHandler();
}
if (dropTarget == null) {
dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dropHandler, true);
}
}
@Override
public void removeNotify() {
if (dropTarget != null) {
dropTarget.removeDropTargetListener(dropHandler);
}
dropTarget = null;
dropHandler = null;
super.removeNotify();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(48, 48);
}
public class DropHandler implements DropTargetListener {
@Override
public void dragEnter(DropTargetDragEvent dtde) {
// Determine if can actual process the contents comming in.
// You could try and inspect the transferable as well, but
// There is an issue on the MacOS under some circumstances
// where it does not actually bundle the data until you accept the
// drop.
if (dtde.isDataFlavorSupported(PieceDataFlavor.SHARED_INSTANCE)) {
dtde.acceptDrag(DnDConstants.ACTION_MOVE);
} else {
dtde.rejectDrag();
}
}
@Override
public void dragOver(DropTargetDragEvent dtde) {
}
@Override
public void dropActionChanged(DropTargetDragEvent dtde) {
}
@Override
public void dragExit(DropTargetEvent dte) {
}
@Override
public void drop(DropTargetDropEvent dtde) {
boolean success = false;
// Basically, we want to unwrap the present...
if (dtde.isDataFlavorSupported(PieceDataFlavor.SHARED_INSTANCE)) {
Transferable transferable = dtde.getTransferable();
try {
Object data = transferable.getTransferData(PieceDataFlavor.SHARED_INSTANCE);
if (data instanceof PieceLabel) {
PieceLabel piece = (PieceLabel) data;
DropTargetContext dtc = dtde.getDropTargetContext();
Component component = dtc.getComponent();
if (component instanceof JComponent) {
Container parent = piece.getParent();
if (parent != null) {
parent.remove(piece);
}
((JComponent) component).add(piece);
success = true;
dtde.acceptDrop(DnDConstants.ACTION_MOVE);
invalidate();
repaint();
} else {
success = false;
dtde.rejectDrop();
}
} else {
success = false;
dtde.rejectDrop();
}
} catch (Exception exp) {
success = false;
dtde.rejectDrop();
exp.printStackTrace();
}
} else {
success = false;
dtde.rejectDrop();
}
dtde.dropComplete(success);
}
}
}
在Drag'n'Drop中,有两个特殊的类将拖拽粘合到drop ....
DataFlavor
DataFlavor
负责提供一种方法,通过这种方法,断开连接的元素不仅可以确定转移的内容,还可以确定如何重构数据......
为简单起见,我只是PieceLabel.class
import java.awt.datatransfer.DataFlavor;
public class PieceDataFlavor extends DataFlavor {
public static final PieceDataFlavor SHARED_INSTANCE = new PieceDataFlavor();
public PieceDataFlavor() {
super(PieceLabel.class, null);
}
}
Transferable
Transferable
是一个包装类,它允许数据从一个位置移动到另一个位置,例如剪贴板。
此示例相当简单,但您可以想象Transferable
可能包含多个DataFlavor
,具体取决于您想要的DataFlavor
可以更改类型(或方式)你得到了数据。
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
public class PieceTransferable implements Transferable {
private final DataFlavor[] flavors = new DataFlavor[]{PieceDataFlavor.SHARED_INSTANCE};
private final PieceLabel piece;
public PieceTransferable(PieceLabel piece) {
this.piece = piece;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
// Okay, for this example, this is over kill, but makes it easier
// to add new flavor support by subclassing
boolean supported = false;
for (DataFlavor mine : getTransferDataFlavors()) {
if (mine.equals(flavor)) {
supported = true;
break;
}
}
return supported;
}
public PieceLabel getPanel() {
return piece;
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
Object data = null;
if (isDataFlavorSupported(flavor)) {
data = getPanel();
} else {
throw new UnsupportedFlavorException(flavor);
}
return data;
}
}
因为组件是自包含的,所以把它放在一起实际上很容易......它们基本上都是照顾自己......
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class BoardPane extends JPanel {
public BoardPane() {
setLayout(new GridLayout(8, 8));
int index = 0;
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
Cell cell = new Cell();
if (index % 2 == 0) {
cell.setBackground(Color.WHITE);
} else {
cell.setBackground(Color.BLACK);
}
add(cell);
index++;
}
index++;
}
try {
PieceLabel label = new PieceLabel();
BufferedImage image = ImageIO.read(getClass().getResource("/Piece01.png"));
label.setIcon(new ImageIcon(image));
setCellPiece(label, 0, 0);
} catch (IOException ex) {
Logger.getLogger(BoardDrag.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void setCellPiece(PieceLabel label, int row, int col) {
int index = (row * 8) + col;
Cell cell = (Cell) getComponent(index);
cell.removeAll();
cell.add(label);
}
}
没错,你没有得到任何免费的东西。
您必须实施确定移动是否有效所需的逻辑。逻辑应该在主动拒绝阻力的情况下实施。这可能需要您向Transferable
添加更多信息,以便您可以确定起始单元格。
我个人希望实现某种“规则”引擎,您的DnD API可以使用该引擎使其变得可插拔