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)
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. 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 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.
Drag/Piece
A
Piece
is a movable game piece, which can be dragged to a new location.The piece itself is responsible for managing the
DragGestureRecognizer
which is used to initialise the drag process...Because I wanted to display an icon in the piece, I choose to override
JLabel
, as it provides the core functionality for this...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)This manages the
DropTarget
, which is responsible for detecting when something is dropped onto it...The Glue
In Drag'n'Drop, there are two special classes which glue the drag to the drop....
The
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...For simplicity, I just
PieceLabel.class
The
Transferable
The
Transferable
is a wrapper class which allows data to be moved from one location to another, like the clipboard, for example.This example is reasonably simple, but you could imagine that a
Transferable
might contain multipleDataFlavor
s, depending on whichDataFlavor
you want could change the type (or the manner in which you get) the data.Putting it all together
Because the components are self contained, putting it together is actually really easy...they basically take care of themselves...
What's the catch?
That's right, you don't get everything of free.
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.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