问题描述
我有一个游戏板,有8个我希望能够将它们移动到jpanel上的任何位置。目前,我只能进行流量或网格布局,但这不会产生预期的结果。长期目标是能够独立地点击一块并将其拖动到所需的位置/位置。 (包括在其他部分之上)
I have a game board, with 8 pieces that I would like to be able to move them anywhere on the jpanel. Currently, I can only do flow or grid layout, however this does not yield the desired results. The long term goal, is to be able to independently click on a piece and drag it to desired location/position. (including on top of other pieces)
任何建议或输入都是最受欢迎的...(感谢Hovercraft Full Of Eels,MadProgrammer,peeskillet,Andrew Thompson为他们的早先的建议)....
Any suggestions or input would be most welcome... (Thanks to Hovercraft Full Of Eels, MadProgrammer, peeskillet, Andrew Thompson for their earlier suggestions)....
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是一个狂热的棋盘游戏玩家,我想让她成为她最喜欢的一些数字版本....
P.S. This is not a school assignment, my g/f is an avid board game player, and I wanted to make her a digital version of some of her favorites....
推荐答案
Drag'n'Drop是一项严肃的工作。如果做得好,它可能真的很棒,但要为一些严肃的繁重和设计工作做好准备...
Drag'n'Drop is some serious work. When done right it can be really awesome, but be prepared for some serious heavy lifting and design work...
一种方法是尝试生成自包含的工作单元,即该部分负责管理它自己的拖动,并且单元格/网格负责管理掉落。
One approach is to try an generate self contained units of work, that is the piece is responsible for managing it's own drag and the cell/grid is responsible for managing the drop.
A Piece
是一个可移动的游戏片,可以拖到新的位置。
A Piece
is a movable game piece, which can be dragged to a new location.
该片本身负责管理 DragGestureRecognizer
,用于初始化拖动过程......
The piece itself is responsible for managing the DragGestureRecognizer
which is used to initialise the drag process...
因为我想在片中显示一个图标,所以我选择覆盖 JLabel
,因为它为此提供了核心功能......
Because I wanted to display an icon in the piece, I choose to override JLabel
, as it provides the core functionality for this...
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();
}
}
}
}
Drop / Cell / Grid
单元/网格就是这样,它构成了整个网格/板上的单个元素。它可以包含一个 Piece
(事实上,你可以很容易地配置它拒绝其他一切)
Drop/Cell/Grid
A cell/grid is just that, it makes up a single element within the over all grid/board. It can contain a Piece
(and in fact, you could easily configure it do reject everything else)
这个管理 DropTarget
,负责检测什么东西掉在上面......
This manages the DropTarget
, which is responsible for detecting when something is dropped onto it...
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中,有两个特殊的类将拖拽粘合到拖放....
The Glue
In Drag'n'Drop, there are two special classes which glue the drag to the drop....
DataFlavor
负责提供一种方法,通过这种方法,断开连接的元素不仅可以确定转移的内容,还可以确定如何重构数据...
The DataFlavor
is responsible for providing a means by which disconnected elements can determine not only what is been transferred, but how that data should be reconstituted...
为简单起见,我只是 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);
}
}
可转让
可转让
是一个包装类,它允许从一个数据移动例如,位置到另一个,比如剪贴板。
The Transferable
The Transferable
is a wrapper class which allows data to be moved from one location to another, like the clipboard, for example.
这个例子相当简单,但你可以想象一个 Transferable
可能包含多个 DataFlavor
s,具体取决于您想要的 DataFlavor
可以更改类型(或其中的方式)你得到的数据。
This example is reasonably simple, but you could imagine that a Transferable
might contain multiple DataFlavor
s, depending on which DataFlavor
you want could change the type (or the manner in which you get) the data.
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;
}
}
全部放在一起
因为组件是自包含的,所以把它放在一起实际上非常简单......它们基本上都是自己照顾...
Putting it all together
Because the components are self contained, putting it together is actually really easy...they basically take care of themselves...
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);
}
}
有什么收获?
这是对的,你没有得到任何免费的东西。
What's the catch?
That's right, you don't get everything of free.
你必须实现确定是否所需的逻辑移动是否有效。逻辑应该在主动拒绝阻力的情况下实施。这可能需要您向 Transferable
添加更多信息,以便您可以确定起始单元格。
You will have to implement the logic required to determine if a move is valid or not. The logic should be implemented in such away as to actively reject the drag. This might require you to add more information to the Transferable
so you can determine the start cell, for example.
我个人希望实现某种规则引擎,你的DnD API可以使用它来使它变得可插拔
I'd personally be looking to implement some kind of "rules" engine, which can be used by your DnD API so that it becomes pluggable
这篇关于JLayeredPanel布局管理器自由移动对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!