this.setLocationRelativeTo(null);
jFrame will show in the center of Screen. But i don't known how to set jFrame at the right of Screen.
You can do this yourself by calculating the position based on the size of the screen and the frame.
static void setLocationToTopRight(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
int x = bounds.x + bounds.width - insets.right - frame.getWidth();
int y = bounds.y + insets.top;
frame.setLocation(x, y);
}
The screen insets include places where a window is not expected to occupy, like task bars and the Mac menu bar. It's possible there is an OS where you can place something on the right side of the screen which would interfere with the window placement if you didn't subtract the insets. (I think Ubuntu can do that, actually, but I don't remember whether a window is placed on top of or behind the menu.)
Here's a simple MCVE demonstrating all four edges. Pressing one of the four buttons anchors the JFrame to that edge.
package mcve;
import javax.swing.*;
import java.awt.*;
public class WindowPlacement {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Window Placement");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton top = new JButton("Top");
JButton left = new JButton("Left");
JButton bottom = new JButton("Bottom");
JButton right = new JButton("Right");
frame.add(top, BorderLayout.NORTH);
frame.add(left, BorderLayout.WEST);
frame.add(bottom, BorderLayout.SOUTH);
frame.add(right, BorderLayout.EAST);
top.addActionListener(e -> setLocationToTop(frame));
left.addActionListener(e -> setLocationToLeft(frame));
bottom.addActionListener(e -> setLocationToBottom(frame));
right.addActionListener(e -> setLocationToRight(frame));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
// Also see:
// https://docs.oracle.com/javase/9/docs/api/java/awt/GraphicsEnvironment.html#getMaximumWindowBounds--
static Rectangle getMaxWindowBounds(JFrame frame) {
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Rectangle bounds = config.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= insets.left + insets.right;
bounds.height -= insets.top + insets.bottom;
return bounds;
}
static void setLocationToTop(JFrame frame) {
frame.setLocation(frame.getX(), getMaxWindowBounds(frame).y);
}
static void setLocationToLeft(JFrame frame) {
frame.setLocation(getMaxWindowBounds(frame).x, frame.getY());
}
static void setLocationToBottom(JFrame frame) {
Rectangle bounds = getMaxWindowBounds(frame);
frame.setLocation(frame.getX(), bounds.y + bounds.height - frame.getHeight());
}
static void setLocationToRight(JFrame frame) {
Rectangle bounds = getMaxWindowBounds(frame);
frame.setLocation(bounds.x + bounds.width - frame.getWidth(), frame.getY());
}
}
If the component is not null and is shown on the screen, then the window is located in such a way that the center of the window coincides with the center of the component.
In other words, you should try frame.setLocation() and then some values (x,y) and look what fits properly
Edit: value 0,0 will give rightcorner
so in ur case : this.setLocation(0,0);
Related
I am working on developing a Chess game. I want to have the board Container utilize a GridLayout to display an 8x8 grid of JPanels. (This will make functions such as highlighting selected squares and valid moves much easier.) I would then like to add the pieces over this layer so that they may be dragged and dropped. I initially had the pieces showing by drawing them in the individual square JPanels, but figured that would be a problem when trying to drag-and-drop them later. I have since been trying to use a JLayeredPane as the main container, but have encountered several issues.
One is that once I've specified the GridLayout for the JLayeredPane, regardless of which Integer I use to specify the layer to add the JLabel or other kind of image to, the pieces get added to the grid, making their positions set and and distorting the whole board. I have read that using LayoutManagers can interfere with layer positioning on the JLayeredPane, so this isn't too surprising. (Although the Oracle demo program from the JLayeredPane tutorial seems to do this just fine: http://download.oracle.com/javase/tutorial/uiswing/examples/components/LayeredPaneDemo2Project/src/components/LayeredPaneDemo2.java)
However, I have also tried to put the grid of JPanels into its own JPanel, then add it to a low layer of the JLayeredPane, the idea being that I could add the drag & drop icons to separate, non-opaque JPanel on a higher layer of the JLayeredPane. When I do this however, after I simply have the grid JPanel inside the JLayeredPane (i.e. before the drag-and-drop layer is added), the grid will not display.
I also have tried overriding the paintComponent (and paint) methods of the JLayeredPane to draw the piece images, but they are hidden by the JPanels (I can see that they are indeed there by setting the JPanels to non-opaque) and as far as I can tell there is no option to set the layer of the graphics on the JLayeredPane. I have also tried using the glassPane of the frame to draw the pieces, but got undesired behavior there as well.
Any help explaining some of this behavior, or where I am going wrong, would be much appreciated!
Here is a simple example that shows how you might (randomly) drag and drop a "chess piece" from one square to another:
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);
}
}
I want to create a game with a fixed width/heigth ratio for the actual screen (so I can easily just scale the game elements and don't have to bother with complex layout). In order to do so I created a JFrame with a BorderLayout with the actual screen in the center and 4 spacing-panels on the sides. After every resize I recalculate the required spacing and set the preferred sizes accordingly so the screen gets the maximum ractangle of the given ratio that fits in the frame. The following code does that job, I replaced the actual screen with a simple blue panel and also added output to check on the calculation:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JFrameResize {
private static JFrame frame;
private static JPanel inside = new JPanel();
private static JPanel spacingTop = new JPanel();
private static JPanel spacingBottom = new JPanel();
private static JPanel spacingLeft = new JPanel();
private static JPanel spacingRight = new JPanel();
private static JPanel compound = new JPanel();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
public static void createAndShowGUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
inside.setBackground(Color.BLUE);
compound.setLayout(new BorderLayout());
compound.add(inside, BorderLayout.CENTER);
compound.add(spacingTop, BorderLayout.NORTH);
compound.add(spacingBottom, BorderLayout.SOUTH);
compound.add(spacingLeft, BorderLayout.WEST);
compound.add(spacingRight, BorderLayout.EAST);
frame.add(compound);
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
calculateSize();
}
});
calculateSize();
frame.setVisible(true);
}
public static void calculateSize() {
double width = frame.getContentPane().getWidth();
double height = frame.getContentPane().getHeight();
double vSpacing = 0, hSpacing = 0;
System.out.println("frame: " + width + ":" + height);
// ratio is hardcoded to 3:4
if ((width * 3 - height * 4) >= 0) {
hSpacing = (width - (height * 4) / 3) / 2;
width = width - 2 * hSpacing;
} else {
vSpacing = (height - (width * 3) / 4) / 2;
height = height - 2 * vSpacing;
}
System.out.println("spacing: " + hSpacing + " + " + width + " + " + hSpacing + " : "
+ vSpacing + " + " + height + " + " + vSpacing);
spacingBottom.setPreferredSize(new Dimension((int) width, (int) vSpacing));
spacingTop.setPreferredSize(new Dimension((int) width, (int) vSpacing));
spacingLeft.setPreferredSize(new Dimension((int) hSpacing, (int) height));
spacingRight.setPreferredSize(new Dimension((int) hSpacing, (int) height));
inside.setPreferredSize(new Dimension((int) width, (int) height));
frame.revalidate();
frame.repaint();
System.out.println("inside: " + inside.getWidth() + ":" + inside.getHeight()
+ ", " + "border: " + spacingLeft.getWidth() + ":"
+ spacingTop.getHeight());
}
}
This works well for the most time, I get outputs like
frame: 510.0:445.0
spacing: 0.0 + 510.0 + 0.0 : 31.25 + 382.5 + 31.25
inside: 510:385, border: 0:30
and the frame shows the correct rectangle. However if I set the frame to fullscreen I get this output:
frame: 1366.0:705.0
spacing: 213.0 + 940.0 + 213.0 : 0.0 + 705.0 + 0.0
inside: 1366:643, border: 0:31
That is incorrect since my formular calculated the inside to be 940:705 but it became 1312:705. The rectangle also doesn't show correctly anymore. Same goes for using windows + arrow keys or dragging the frame to the screen sides . The calculation input is correct but the repaint/revalidate somehow behaves differently from a normal resize. No different combination of revalidate() and repaint() or spamming them across the code seems to change anything.
How does this happen and how do I fix this? Or is the approach in general flawed and should be replaced?
Don't use a BorderLayout and try to add spacing components.
Instead I would suggest you can set the layout manager of the content panel to be a GridBagLayout. When using the default GridBagConstraints any component added to the GridBagLayout will automatically be centered.
Then for the "inside" panel that you add to the frame you need to override the getPreferredSize() method to calculate the size of the panel.
To determine its preferred size you would get the size of its parent container and then you determine its preferred size based on your rules.
There would be no need for the ComponentListener because the layout manager is automatically invoked whenever the frame is resized.
Simple example to get you started:
import java.awt.*;
import javax.swing.*;
public class JFrameResize
{
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
public static void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel inside = new JPanel()
{
#Override
public Dimension getPreferredSize()
{
Dimension parent = getParent().getSize();
Dimension child = new Dimension();
int value = (parent.width * 3) - (parent.height * 4);
if (value > 0)
{
child.height = parent.height;
child.width = (int)(child.height * 4 / 3);
}
else
{
child.width = parent.width;
child.height = (int)(child.width * 3 / 4);
}
return child;
}
};
inside.setBackground(Color.BLUE);
frame.setLayout( new GridBagLayout() );
frame.add(inside, new GridBagConstraints());
frame.pack();
frame.setVisible(true);
}
}
Regarding why my approach does not work:
This source from this question (thanks to #Andrew Thompson for linking) states:
[setPreferredSize is] used by LayoutManager as hints to balance the actual layout
So my setPreferredSize was probably overridden by the BorderLayout, in order to make sure the LayoutManager does not interfere you have to overwrite the getPreferredSize
Hey Guys I have succesfully made a GUI in java that will scale polygons and circles using a slider. Everything works but I was wondering if there is a way to change the Origin point(Where it scales from). Right now it scales from the corner and I would like it to scale from the middle so I can start it in the middle and it scales out evenly. Also, If anyone could tell me an easy way to replace the Rectangle I have with an Image of some kind so you can scale the Picture up and down would be great! Thank you! Here is my code:
import javax.swing.*;
public class Fred
{
public static void main(String[] args)
{
TheWindow w = new TheWindow();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X wont close the window with out this line
w.setSize(375,375);
w.setVisible(true);
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class TheWindow extends JFrame
{
private JSlider slider; //declare slider
private drawRect myPanel; //declare/ create panel
public TheWindow()
{
super("Slider Example"); //make title
myPanel = new drawRect();
myPanel.setBackground(Color.green); //change background color
slider = new JSlider(SwingConstants.VERTICAL, 0, 315, 10);// restrains the slider from scaling square to 0-300 pixels
slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels
slider.setPaintTicks(true); //this actually paints the ticks on the screen
slider.addChangeListener
(
new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
myPanel.setD(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen
}
}
);
add(slider, BorderLayout.WEST); //similar to init method, adds slider and panel to GUI
add(myPanel, BorderLayout.CENTER);
}
}
import java.awt.*;
import javax.swing.*;
public class drawRect extends JPanel
{
private int d = 25; //this determines the beginning length of the rect.
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
g.fillRect(15,15, d, d); //paints rectangle on screen
//x , y, width, height
}
public void setD(int newD)
{
d = (newD >= 0 ? newD : 10); //if number is less than zero it will use 10 for diameter(compressed if statement)
repaint();
}
public Dimension getPrefferedSize()
{
return new Dimension(200, 200);
}
public Dimension getMinimumSize()
{
return getPrefferedSize();
}
}
Changing the "origin point" so it becomes the center of the "zoom" is basically just the process of subtract half of d from the center point.
So, assuming the the center point is 28 ((25 / 2) + 15), you would simply then subtract d / 2 (25 / 2) from this point, 28 - (25 / 2) = 15 or near enough...
I modified the paintComponent method for testing, so the rectangle is always at the center of the panel, but you can supply arbitrary values in place of the originX and originY
#Override
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
int originX = getWidth() / 2;
int originY = getHeight() / 2;
int x = originX - (d / 2);
int y = originY - (d / 2);
System.out.println(x + "x" + y);
g.fillRect(x, y, d, d); //paints rectangle on screen
//x , y, width, height
}
As for scaling an image, you should look at Graphics#drawImage(Image img, int x, int y, int width, int height, ImageObserver observer), beware though, this will scaling the image to the absolute size, it won't keep the image ratio.
A better solution might be to use a double value of between 0 and 1 and multiple the various elements by this value to get the absolute values you want
In Java, is it possible to get the Width and Height of the JFrame without the title and other borders?
frame.getWidth() and frame.getHeight()1 seems to return the width including the border.
Thanks.
frame.getContentPane().getSize();
frame.pack();
System.out.println("frame width : "+getWidth());
System.out.println("frame height: "+getHeight());
System.out.println("content pane width : "+getContentPane().getWidth());
System.out.println("content pane height: "+getContentPane().getHeight());
System.out.println("width of left + right borders: "+(getWidth()-getContentPane ().getWidth()));
System.out.println("height of top + bottom borders: "+(getHeight()-getContentPane().getHeight()));
This works fine
frame.getContentPane().getSize();
But not if you haven't added content yet. In my case, I wanted to calculate the inner dimensions of the JFrame before adding content, so I could divide up the content accordingly. Here's what I came up with.
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
pack(); // Need this, otherwise insets() show as 0.
int scrW = (int)screenSize.getWidth();
int scrH = (int)screenSize.getHeight();
int innerW = scrW - getInsets().left - getInsets().right;
int innerH = scrH - getInsets().top - getInsets().bottom;
// Need to setSize(), otherwise pack() will collapse the empty JFrame
setSize(scrW, scrH);
Here is a code snippet which works on JFrame as well as AWT's Frame (which happens to be the super-type of JFrame):
public static Dimension getInnerSize(Frame frame) {
Dimension size = frame.getSize();
Insets insets = frame.getInsets();
if (insets != null) {
size.height -= insets.top + insets.bottom;
size.width -= insets.left + insets.right;
}
return size;
}
Beware: The insets are only valid once the frame has been shown.
Here is another code snippet to work around this problem:
private static Insets defaultInsets;
public static Insets getInsetsWithDefault(Frame frame) {
// insets only correct after pack() and setVisible(true) has been
// called, so we use some fallback strategies
Insets insets = frame.getInsets();
if (insets.top == 0) {
insets = defaultInsets;
if (insets == null) {
insets = new Insets(26, 3, 3, 3);
// usual values for windows as our last resort
// but only as long as we never saw any real insets
}
} else if (defaultInsets == null) {
defaultInsets = (Insets) insets.clone();
}
return insets;
}
This code needs to be called once with a visible Frame. After that it can correctly predict the insets even for invisible frames (due to caching of the defaultInsets), assuming they are always the same.
Of course this only works if all windows get the same window-decorations. But i am not aware of any case where they might differ from window to window.
This might be useful, too:
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
MyUtilClass.getInsetsWithDefault(frame); // init the defaultInsets
}
});
It will call the getInsetsWithDefault() method once the window is visible and initialize the correct defaultInsets.
I have a subclass of JLabel that forms a component of my GUI. I have implemented the ability to drag and drop the component from one container to another, but without any visual effects. I want to have this JLabel follow the cursor during the drag of the item from one container to another. I figured that I could just create a glass pane and draw it on there. However, even after I add the component to the glass pane, set the component visible, and set the glass pane visible, and set the glass pane as opaque, I still so not see the component. I know the component works because I can add it to the content pane and have it show up.
How do I add a component to the glass pane?
Finally figured how to get the simple example working. Thanks, #akf. I was able to adapt this solution to my original problem, allowing me to remove ~60 lines of Java2D code that manually rendered a representation of the 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);
}
}
The example code below shows how to drag a chess piece around a chess board. It uses JLayeredPane instead of a glass pane, but I'm sure the concepts would be the same. That is:
a) add the glass pane to the root pane
b) make the glass pane visible
c) add the component to the glass pane making sure the bounds are valid
d) use setLocation() to animate the dragging of the component
Edit: added code to fix 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);
When using layout managers you never use the setSize() or setBounds() methods. In your case you just set the preferred size to (0, 0) since this is the default size of all components.
It works when you add the label to the frame because the default layout manger for the content pane of the frame is a border layout, therefore the preferred size of the label is ignored and the label is made the size of the frame.
However, by default a JPanel uses a FlowLayout which does respect the preferred size of the component. Since the preferred size is 0, there is nothing to paint.
Also, the glass pane needs to made visible in order for it to be painted.
I suggest you read the Swing tutorial. There are section on how layout managers work and on how glass panes work and each section has working examples.
Edit: Example code added below:
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);
}
}
Although tangential to the question, the JLayeredPane example cited by #camickr admits the following adaptation, which highlights the effect of mouseReleased() over an existing component.
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);
}
Besides the pointers to the LayerPane examples already provided, the issue with your original code centers around the setting of the preferred size of your label. You set it before the JLabel has been sized, so your:
l.setPreferredSize(l.getSize());
is ineffectual. If, on the other hand, you make that call after you make your call to setBounds, you will see your desired results. With that in mind, reorder this:
l.setPreferredSize(l.getSize());
l.setBounds(10, 10, 50, 20);
to look like this:
l.setBounds(10, 10, 50, 20);
l.setPreferredSize(l.getSize());
Since I had been following Romain Guy's blogs on Swing for a long time. I have a link that you might be interested in. He released the source - which used a GlassPane for DnD effects.
http://jroller.com/gfx/entry/drag_and_drop_effects_the
I myself never did use a fizzy animation/effect on DnD, so can't comment any further :-|