So I am trying to click and drag a JLabel around a JFrame. The following code allows a JLabel to be moved around the screen when the mouse is pressed / dragged at any point on the screen, but I am not sure how to add a second ActionListener to check if the mouse is clicking on the label, assuming that is the solution.
I would like to have multiple JLabels on the screen so that the only label being moved is the one that the mouse has clicked and is now dragging.
Thanks.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class test extends JFrame implements MouseMotionListener {
private JPanel panel = new JPanel(null);
private JLabel dragLabel = new JLabel("drag test");
private int mouseX = 200;
private int mouseY = 200;
public test() {
this.add(panel);
panel.setBackground(Color.WHITE);
panel.add(dragLabel);
dragLabel.setForeground(Color.RED);
dragLabel.setBounds(mouseX, mouseY, 100, 50);
panel.addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
dragLabel.setBounds(mouseX, mouseY, 100, 50);
}
#Override
public void mouseMoved(MouseEvent e) {}
public static void main(String[] args) {
test frame = new test();
frame.setVisible(true);
frame.setSize(600, 400);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Another way to do this is to add the JLabel to a JLayeredPane or to a JPanel held by a JLayeredPane and add a MouseAdapter as the JLayeredPane's MouseListener and MouseMotionListener. Then when clicking on the label, move it to the JLayeredPane's JLayeredPane.DRAG_LAYER so it moves on top of everything else, then place the JLabel on whichever is the most appropriate level on mouse release. I've found this to work well when moving chess pieces on a chess board, for instance, and you want to make sure that the piece you're moving is displayed above all the other pieces when dragging.
Addition: You've probably left this thread, but if you come back, or for the benefit of others, I wanted to clarify what I meant by using a JLayeredPane by posting an example.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragLabelOnLayeredPane extends JLayeredPane {
public static final int WIDTH = 680;
public static final int HEIGHT = 480;
private static final int GRID_ROWS = 8;
private static final int GRID_COLS = 6;
private static final int GAP = 3;
private static final Dimension LAYERED_PANE_SIZE = new Dimension(WIDTH, HEIGHT);
private static final Dimension LABEL_SIZE = new Dimension(60, 40);
private GridLayout gridlayout = new GridLayout(GRID_ROWS, GRID_COLS, GAP, GAP);
private JPanel backingPanel = new JPanel(gridlayout);
private JPanel[][] panelGrid = new JPanel[GRID_ROWS][GRID_COLS];
private JLabel redLabel = new JLabel("Red", SwingConstants.CENTER);
private JLabel blueLabel = new JLabel("Blue", SwingConstants.CENTER);
public DragLabelOnLayeredPane() {
backingPanel.setSize(LAYERED_PANE_SIZE);
backingPanel.setLocation(2 * GAP, 2 * GAP);
backingPanel.setBackground(Color.black);
for (int row = 0; row < GRID_ROWS; row++) {
for (int col = 0; col < GRID_COLS; col++) {
panelGrid[row][col] = new JPanel(new GridBagLayout());
backingPanel.add(panelGrid[row][col]);
}
}
redLabel.setOpaque(true);
redLabel.setBackground(Color.red.brighter().brighter());
redLabel.setPreferredSize(LABEL_SIZE);
panelGrid[4][3].add(redLabel);
blueLabel.setOpaque(true);
blueLabel.setBackground(Color.blue.brighter().brighter());
blueLabel.setPreferredSize(LABEL_SIZE);
panelGrid[1][1].add(blueLabel);
backingPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setPreferredSize(LAYERED_PANE_SIZE);
add(backingPanel, JLayeredPane.DEFAULT_LAYER);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
private class MyMouseAdapter extends MouseAdapter {
private JLabel dragLabel = null;
private int dragLabelWidthDiv2;
private int dragLabelHeightDiv2;
private JPanel clickedPanel = null;
#Override
public void mousePressed(MouseEvent me) {
clickedPanel = (JPanel) backingPanel.getComponentAt(me.getPoint());
Component[] components = clickedPanel.getComponents();
if (components.length == 0) {
return;
}
// if we click on jpanel that holds a jlabel
if (components[0] instanceof JLabel) {
// remove label from panel
dragLabel = (JLabel) components[0];
clickedPanel.remove(dragLabel);
clickedPanel.revalidate();
clickedPanel.repaint();
dragLabelWidthDiv2 = dragLabel.getWidth() / 2;
dragLabelHeightDiv2 = dragLabel.getHeight() / 2;
int x = me.getPoint().x - dragLabelWidthDiv2;
int y = me.getPoint().y - dragLabelHeightDiv2;
dragLabel.setLocation(x, y);
add(dragLabel, JLayeredPane.DRAG_LAYER);
repaint();
}
}
#Override
public void mouseDragged(MouseEvent me) {
if (dragLabel == null) {
return;
}
int x = me.getPoint().x - dragLabelWidthDiv2;
int y = me.getPoint().y - dragLabelHeightDiv2;
dragLabel.setLocation(x, y);
repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
if (dragLabel == null) {
return;
}
remove(dragLabel); // remove dragLabel for drag layer of JLayeredPane
JPanel droppedPanel = (JPanel) backingPanel.getComponentAt(me.getPoint());
if (droppedPanel == null) {
// if off the grid, return label to home
clickedPanel.add(dragLabel);
clickedPanel.revalidate();
} else {
int r = -1;
int c = -1;
searchPanelGrid: for (int row = 0; row < panelGrid.length; row++) {
for (int col = 0; col < panelGrid[row].length; col++) {
if (panelGrid[row][col] == droppedPanel) {
r = row;
c = col;
break searchPanelGrid;
}
}
}
if (r == -1 || c == -1) {
// if off the grid, return label to home
clickedPanel.add(dragLabel);
clickedPanel.revalidate();
} else {
droppedPanel.add(dragLabel);
droppedPanel.revalidate();
}
}
repaint();
dragLabel = null;
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("DragLabelOnLayeredPane");
frame.getContentPane().add(new DragLabelOnLayeredPane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Please feel free to post any questions, need for clarification, or corrections.
Inspired by your code and user compilex's answer, follows demonstration:
Full code:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
/**
* A demonstration of moving around labels in a panel.
* <p>
* Some labels show up layed out in a grid. Then the
* user can drag any label anywhere on the panel.
* </p>
*/
public class LabelDragger {
public static void main(final String[] args) {
final int labelRows = 5, //How many rows of labels.
labelColumns = 5, //How many columns of labels.
labelWidth = 55, //Width for each label.
labelHeight = 20; //Height for each label.
//Border colors for labels:
final Color[] colors = new Color[]{Color.BLUE, Color.GREEN, Color.BLACK, Color.GRAY};
final Random prng = new Random(); //For selecting border color for each label.
final JPanel dragP = new JPanel(null); //Nicely set to null! :D Did not know that trick.
//Creating the listener for the panel:
final MouseAdapter ma = new MouseAdapter() {
private JLabel selectedLabel = null; //Clicked label.
private Point selectedLabelLocation = null; //Location of label in panel when it was clicked.
private Point panelClickPoint = null; //Panel's click point.
//Selection of label occurs upon pressing on the panel:
#Override
public void mousePressed(final MouseEvent e) {
//Find which label is at the press point:
final Component pressedComp = dragP.findComponentAt(e.getX(), e.getY());
//If a label is pressed, store it as selected:
if (pressedComp != null && pressedComp instanceof JLabel) {
selectedLabel = (JLabel) pressedComp;
selectedLabelLocation = selectedLabel.getLocation();
panelClickPoint = e.getPoint();
//Added the following 2 lines in order to make selectedLabel
//paint over all others while it is pressed and dragged:
dragP.setComponentZOrder(selectedLabel, 0);
selectedLabel.repaint();
}
else {
selectedLabel = null;
selectedLabelLocation = null;
panelClickPoint = null;
}
}
//Moving of selected label occurs upon dragging in the panel:
#Override
public void mouseDragged(final MouseEvent e) {
if (selectedLabel != null
&& selectedLabelLocation != null
&& panelClickPoint != null) {
final Point newPanelClickPoint = e.getPoint();
//The new location is the press-location plus the length of the drag for each axis:
final int newX = selectedLabelLocation.x + (newPanelClickPoint.x - panelClickPoint.x),
newY = selectedLabelLocation.y + (newPanelClickPoint.y - panelClickPoint.y);
selectedLabel.setLocation(newX, newY);
}
}
};
dragP.addMouseMotionListener(ma); //For mouseDragged().
dragP.addMouseListener(ma); //For mousePressed().
//Filling the panel with labels:
for (int row = 0; row < labelRows; ++row)
for (int col = 0; col < labelColumns; ++col) {
//Create label for (row, col):
final JLabel lbl = new JLabel("Label" + (row * labelColumns + col));
lbl.setHorizontalAlignment(JLabel.CENTER);
//lbl.setVerticalAlignment(JLabel.CENTER);
lbl.setBounds(col * labelWidth, row * labelHeight, labelWidth, labelHeight); //Grid-like positioning.
lbl.setBorder(new LineBorder(colors[prng.nextInt(colors.length)], 2)); //Set a border for clarity.
//Add label to panel:
dragP.add(lbl);
}
//Creating and showing the main frame:
final JFrame frame = new JFrame(LabelDragger.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//The size of the content pane adds some extra room for moving the labels:
final Dimension paneSize = new Dimension((int)(1.5 * labelWidth * labelColumns),
(int)(1.5 * labelHeight * labelRows));
frame.getContentPane().setPreferredSize(paneSize);
frame.getContentPane().add(dragP);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Explanations are added as comments.
Tips:
Take a look at the documentation on Container.findComponentAt(int x, int y), if you are going to add Components on the dragP Container, other than "draggable" labels.
Also, you can instead use Container.getComponentAt(int x, int y), in this case. I suggest you read their (small) documentation first.
Add a mouse listener to the label instead of the panel. (You might still need a mouse listener on the panel for the dragging but at least the one on the label can tell you if it was selected).
Create two global variables:
int x_pressed = 0;
int y_pressed = 0;
then create two events (mousePressed and mouseDragged over JLabel):
lbl_banner.addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e) {
//catching the current values for x,y coordinates on screen
x_pressed = e.getX();
y_pressed = e.getY();
}
});
lbl_banner.addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
//and when the Jlabel is dragged
setLocation(e.getXOnScreen() - x_pressed, e.getYOnScreen() - y_pressed);
}
});
Related
I have a program that displays a 4x4 grid of squares through a GridBagLayout layout manager. 16 JLabels which all contain a square.gif are displayed. When one of the rectangles is clicked, it is supposed to be replaced with a JLabel that contains an image (e.g, such as a hat). So, the image takes the place of the rectangle that is clicked on.
However, what happens at the moment is that the rectangle that is clicked only gets replaced sometimes. Other times, the rectangle disappears but the image does not replace it. Other times, the image displays in a rectangle that has been clicked previously but only after clicking a different rectangle. I have placed the most relevant code below.
public void displayGrid() {
c.gridx = 0;
c.gridy = 0;
try {
squareImage = ImageIO.read(this.getClass().getResource("stimulus(0).gif")); //line 37
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JLabel squareLabel = new JLabel(new ImageIcon(squareImage));
for(int i = 0; i < 16; i++){
c.gridx = i % 4;
c.gridy = i / 4;
squareLabel = new JLabel(new ImageIcon(squareImage));
squareLabels[i] = squareLabel;
panel.add(squareLabels[i], c);
squareLabels[i].addMouseListener(this);
System.out.println(c.gridx + "" + c.gridy);
}
panel.validate();
}
public void mousePressed(MouseEvent e) {
for(int i = 0; i < squareLabels.length; i++){
if(e.getSource() == squareLabels[i]){
//JLabel removedLabel = squareLabels[i];
c.gridx = (i/4);
c.gridy = (i%4);
panel.remove(squareLabels[i]);
panel.revalidate();
panel.repaint();
panel.add(stimuliLabels[0], c);
panel.validate();
}
}
}
In the mousePressed() method, I have attempted to write code that determines the JLabel that is pressed, gets the GridBagConstraints of that JLabel, removes the JLabel that is clicked on, and then replaces that JLabel with the new JLabel with the given GridBagConstraints. However, as I have already said, the program is not working as planned, and I don't know why.
Thank you for taking the time to read this. Any help would be appreciated.
Why would you want to swap JLabels? JLabels are built to hold Icons, usually ImageIcons, and if you want to swap images, best to leave the JLabels in place, and simply swap the ImageIcon that it displays which is easily done by calling setIcon(...). This is much easier than what you're trying to do, and this works with the library, not against it as you're trying to do.
public void mousePressed(MouseEvent e) {
// assuming that only JLabels are given this MouseListener:
JLabel label = (JLabel) e.getSource();
label.setIcon(desiredNewIcon);
}
For example, from my answer to a similar question:
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class RandomChessMen extends JPanel {
// for this example I get a sprite sheet that holds several sprite images in it
// the images can be found here: https://stackoverflow.com/questions/19209650
private static final String IMAGE_PATH = "http://i.stack.imgur.com/memI0.png";
private static final int LABEL_COUNT = 2;
private static final int ICON_COLUMNS = 6;
private Random random = new Random();
public RandomChessMen() throws IOException {
URL url = new URL(IMAGE_PATH);
BufferedImage largeImg = ImageIO.read(url);
setLayout(new GridLayout(1, 0));
// break down large image into its constituent sprites and place into ArrayList<Icon>
int w = largeImg.getWidth() / ICON_COLUMNS;
int h = largeImg.getHeight() / LABEL_COUNT;
for (int i = 0; i < LABEL_COUNT; i++) {
final List<Icon> iconList = new ArrayList<>();
int y = (i * largeImg.getHeight()) / LABEL_COUNT;
// get 6 icons out of large image
for (int j = 0; j < ICON_COLUMNS; j++) {
int x = (j * largeImg.getWidth()) / ICON_COLUMNS;
// get subImage
BufferedImage subImg = largeImg.getSubimage(x, y, w, h);
// create ImageIcon and add to list
iconList.add(new ImageIcon(subImg));
}
// create JLabel
final JLabel label = new JLabel("", SwingConstants.CENTER);
int eb = 40;
label.setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
// get random index for iconList
int randomIndex = random.nextInt(iconList.size());
Icon icon = iconList.get(randomIndex); // use index to get random Icon
label.setIcon(icon); // set label's icon
label.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
Icon secondIcon = label.getIcon();
// so we don't repeat icons
while (label.getIcon() == secondIcon) {
int randomIndex = random.nextInt(iconList.size());
secondIcon = iconList.get(randomIndex);
}
label.setIcon(secondIcon);
}
});
// add to GUI
add(label);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("RandomImages");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
frame.getContentPane().add(new RandomChessMen());
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I'm trying to make a chess game, but I can't figure out how to smoothly redraw my cells. I want them to be redrawn every time a Piece moves. I tried to just keep adding Jframes, but it makes it glitch back to old frames. Is there a simple way to just update some part of the GUI?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class GUI {
public static JFrame frame; //global variable for drawn frame
public static ChessBoardPane tiles;
public GUI(final Piece[][] board) { //GUI for Board and Pieces
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
frame = new JFrame("Chess"); //initialize frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //closes on exit
drawFrame(board); //draw frame
}
});
}
public void drawFrame(final Piece[][] board){ //updates gui with a new frame
try{
tiles=new ChessBoardPane(board);
frame.add(tiles); //add chess board to frame
frame.pack(); //resizes frame to fit chess grid
frame.setLocationRelativeTo(null); //window in middle of screen
frame.setVisible(true); //visible
} catch(Exception ex){
}
}
public class ChessBoardPane extends JPanel{ //creates a grid of jButtons
public ChessBoardPane(Piece board[][]) {
int index = 0;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
for (int row = 0; row < Board.width; row++) {
for (int col = 0; col < Board.height; col++) {
Color color = index % 2 == 0 ? Color.LIGHT_GRAY : Color.DARK_GRAY; //cells are dark and light gray
gbc.gridx = col;
gbc.gridy = row;
String symbol;
if(board[col][Board.height-row-1]!=null){
symbol=board[col][Board.height-row-1].symbol; //chooses the correct picture for a piece
}
else
symbol=null;
Cell tile = new Cell(color,symbol);
tile.addActionListener(new ButtonActionListener(col, Board.height-row-1));
add(tile, gbc); //add cell to gui
index++;
}
index++;
}
}
private class ButtonActionListener implements ActionListener{ //used to get x and y coordinates for button presses and call methods
private int x;
private int y;
public ButtonActionListener(int x, int y){
this.x=x;
this.y=y;
}
public void actionPerformed(ActionEvent e){ //runs commands when a tile is pressed
Game.attemptMove(x,y);
}
}
}
public class Cell extends JButton { //sets up cell behavior
int x;
int y;
public Cell(Color background, String symbol) {
setContentAreaFilled(false);
if(symbol!=null){
ImageIcon img = new ImageIcon(symbol); //puts picture in cell
setIcon(img);
}
setBorderPainted(false);
setBackground(background); //sets cell color
setOpaque(true);
}
#Override
public Dimension getPreferredSize() { //sets size of cell
return new Dimension(75, 75);
}
}
}
I am able to SWAP the panels but it is not good enough. For instance, if panel1 collides panel2, it swaps, but if the same collides panel3, panel3 also moves to panel1 location(which I don't want to happen). If there are about 10 panels, and if I want to swap panel1 with panel10 it is not possible with current logic. Can anyone please help me out.
Below is the code with the above logic:
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GDragAndDrop extends JFrame {
public GDragAndDrop() {
add(new op());
}
public static void main(String[] args) {
GDragAndDrop frame = new GDragAndDrop();
frame.setTitle("GDragAndDrop");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);// Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class op extends JPanel implements MouseListener, MouseMotionListener {
JPanel p;
Point pPoint;
Point p1;
Point p2;
MouseEvent pressed;
JPanel[] panels = new JPanel[3];
public op() {
int b = 3;
int a = 0;
for (int i = 0; i < b; i++) {
panels[i] = new JPanel();
panels[i].setBackground(Color.cyan);
panels[i].setBorder(new LineBorder(Color.black));
a = a + 20;
panels[i].setPreferredSize(new Dimension(a, 400));
panels[i].addMouseListener(this);
panels[i].addMouseMotionListener(this);
panels[i].setBorder(new LineBorder(Color.black));
panels[i].setVisible(true);
panels[i].add("Center", new JLabel("To Move" + i));
this.add(panels[i]);
}
}
public JPanel getPanelColliding(JPanel dragPanel) {
Rectangle rDrag = dragPanel.getBounds();
for (int i = 0; i < 3; i++) {
if (panels[i] == dragPanel)
continue;
Rectangle r = panels[i].getBounds();
if (r.intersects(rDrag)) {
return panels[i];
}
}
return null;
}
public void mousePressed(MouseEvent e) {
int b = 3;
for (int i = 0; i < b; i++) {
if (e.getSource() == panels[i]) {
pressed = e;
p2 = panels[i].getLocation();
}
}
}
#Override
public void mouseDragged(MouseEvent arg0) {
int b = 3;
for (int i = 0; i < b; i++) {
if (arg0.getSource() == panels[i]) {
pPoint = panels[i].getLocation(pPoint);
int x = pPoint.x - pressed.getX() + arg0.getX();
int y = pPoint.y - pressed.getY() + arg0.getY();
if (getPanelColliding(panels[i]) != null) {
JPanel DragP = new JPanel();
DragP = getPanelColliding(panels[i]);
p1 = getPanelColliding(panels[i]).getLocation(p1);
int x1 = pPoint.x - pressed.getX() + arg0.getX();
int y1 = pPoint.y - pressed.getY() + arg0.getY();
panels[i].setLocation(x1, y1);
DragP.setLocation(p2);
} else
panels[i].setLocation(x, y);
}
}
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
If you don't want to use drag-and-drop, and if your JPanels are in columns, then consider these suggestions:
I've gotten this to work having the container JPanel (the one that holds the multiple column components) use a FlowLayout
I've added the MouseAdapter as a MouseListener and MouseMotionListener to the container JPanel, not the column components.
I obtained the selected column component by calling getComponentAt(mouseEvent.getPoint()) on the container JPanel. Of course check that it's not null.
If the selected component is not null, then I set a selectedComponent variable with this component, and put a placeholder JLabel that has the same preferredSize in its place. I do this by removing all components from the container JPanel, and then re-adding them all back except for the selected component where I instead add the placeholder JLabel. I then call revalidate and repaint on the container.
To drag the selected component, elevate it to the glassPane (i.e., add it to the glassPane).
make the glassPane visible and give it a null layout.
Drag the selected component in the glassPane simply by changing its location relative to the glassPane.
On mouseReleased, find out what column component the mouse is over, again by using getComponentAt(...).
Then remove all components from the container JPanel,
and then add them all back in the desired order.
Then again call revalidate and repaint on the container JPanel.
For example:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class SwapPanelEg extends JPanel {
private static final long serialVersionUID = 1594039652438249918L;
private static final int PREF_W = 400;
private static final int PREF_H = 400;
private static final int MAX_COLUMN_PANELS = 8;
private JPanel columnPanelsHolder = new JPanel();
public SwapPanelEg() {
columnPanelsHolder.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
for (int i = 0; i < MAX_COLUMN_PANELS; i++) {
int number = i + 1;
int width = 20 + i * 3;
int height = PREF_H - 30;
columnPanelsHolder.add(new ColumnPanel(number, width, height));
}
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
columnPanelsHolder.addMouseListener(myMouseAdapter);
columnPanelsHolder.addMouseMotionListener(myMouseAdapter);
setLayout(new GridBagLayout());
add(columnPanelsHolder);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
private JComponent selectedPanel;
private Point deltaLocation;
private JLabel placeHolder = new JLabel();
private JComponent glassPane;
#Override
public void mousePressed(MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
JPanel source = (JPanel) evt.getSource();
selectedPanel = (JComponent) source.getComponentAt(evt.getPoint());
if (selectedPanel == null) {
return;
}
if (selectedPanel == source) {
selectedPanel = null;
return;
}
glassPane = (JComponent) SwingUtilities.getRootPane(source).getGlassPane();
glassPane.setVisible(true);
Point glassPaneOnScreen = glassPane.getLocationOnScreen();
glassPane.setLayout(null);
Point ptOnScreen = evt.getLocationOnScreen();
Point panelLocOnScreen = selectedPanel.getLocationOnScreen();
int deltaX = ptOnScreen.x + glassPaneOnScreen.x - panelLocOnScreen.x;
int deltaY = ptOnScreen.y + glassPaneOnScreen.y - panelLocOnScreen.y;
deltaLocation = new Point(deltaX, deltaY);
Component[] allComps = source.getComponents();
for (Component component : allComps) {
source.remove(component);
if (component == selectedPanel) {
placeHolder.setPreferredSize(selectedPanel.getPreferredSize());
source.add(placeHolder);
selectedPanel.setSize(selectedPanel.getPreferredSize());
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
glassPane.add(selectedPanel);
} else {
source.add(component);
}
}
revalidate();
repaint();
}
#Override
public void mouseDragged(MouseEvent evt) {
if (selectedPanel != null) {
Point ptOnScreen = evt.getLocationOnScreen();
int x = ptOnScreen.x - deltaLocation.x;
int y = ptOnScreen.y - deltaLocation.y;
selectedPanel.setLocation(x, y);
repaint();
}
}
#Override
public void mouseReleased(MouseEvent evt) {
if (evt.getButton() != MouseEvent.BUTTON1) {
return;
}
if (selectedPanel == null) {
return;
}
JComponent source = (JComponent) evt.getSource();
Component[] allComps = source.getComponents();
Component overComponent = (JComponent) source.getComponentAt(evt
.getPoint());
source.removeAll();
if (overComponent != null && overComponent != placeHolder
&& overComponent != source) {
for (Component component : allComps) {
if (component == placeHolder) {
source.add(overComponent);
} else if (component == overComponent) {
source.add(selectedPanel);
} else {
source.add(component);
}
}
} else {
for (Component component : allComps) {
if (component == placeHolder) {
source.add(selectedPanel);
} else {
source.add(component);
}
}
}
revalidate();
repaint();
selectedPanel = null;
}
}
private static void createAndShowGui() {
SwapPanelEg mainPanel = new SwapPanelEg();
JFrame frame = new JFrame("SwapPanelEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ColumnPanel extends JPanel {
private static final long serialVersionUID = 5366233209639059032L;
private int number;
private int prefWidth;
private int prefHeight;
public ColumnPanel(int number, int prefWidth, int prefHeight) {
setName("ColumnPanel " + number);
this.number = number;
this.prefWidth = prefWidth;
this.prefHeight = prefHeight;
add(new JLabel(String.valueOf(number)));
setBorder(BorderFactory.createLineBorder(Color.black));
setBackground(Color.cyan);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefWidth, prefHeight);
}
public int getNumber() {
return number;
}
}
If you don't want to use Swing drag-and-drop, swap the content of the source and destination, as shown in this JLayeredPane example and variation.
My Swing application has to show a modal dialog to the user. Sorry for not posting SSCCE.
topContainer might be JFrame or JApplet.
private class NewGameDialog extends JDialog {
public NewGameDialog () {
super(SwingUtilities.windowForComponent(topContainer), "NEW GAME", ModalityType.APPLICATION_MODAL);
//add components here
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
//TODO:
setSize(new Dimension(250, 200));
setLocation(650, 300);
}
}
I start the dialog like this on network event
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
NewGameDialog dialog = new NewGameDialog();
dialog.setVisible(true);
}
});
The problem is to set optimal location for my dialog.
1) If it is set as absolute value, and I move the app frame to the second screen, then dialog is shown on the first screen which is weird.
2) If it is set a relative value to JFrame, it might appear that user moved the app frame outside of the screen and the dialog being relatively located would not be visible to the user. And because it is modal, the game would be stuck.
What is the best solution considering two above mentioned issues?
This reminded me of a very favourite post of mine, using Window.setLocationByPlatform(true), on StackOverflow.
How to best position Swing GUIs
EDIT 1 :
You can add a FocusListener to your JDialog and on focusGained(...) method, you can use setLocationRelativeTo(null) for both the JFrame and the JDialog, so that they both come to the center of the screen no matter where they are before.
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/**
* Created with IntelliJ IDEA.
* User: Gagandeep Bali
* Date: 1/14/13
* Time: 7:34 PM
* To change this template use File | Settings | File Templates.
*/
public class FrameFocus
{
private JFrame mainwindow;
private CustomDialog customDialog;
private void displayGUI()
{
mainwindow = new JFrame("Frame Focus Window Example");
customDialog = new CustomDialog(mainwindow, "Modal Dialog", true);
mainwindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
JButton mainButton = new JButton(
"Click me to open a MODAL Dialog");
mainButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!customDialog.isShowing())
customDialog.setVisible(true);
}
});
contentPane.add(mainButton);
mainwindow.setContentPane(contentPane);
mainwindow.pack();
mainwindow.setLocationByPlatform(true);
mainwindow.setVisible(true);
}
public static void main(String... args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
new FrameFocus().displayGUI();
}
});
}
}
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
mainWindow.setLocationRelativeTo(null);
setLocationRelativeTo(null);
}
#Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
EDIT 2 :
I searched a bit here and there, and it turns out, in my opinion, that actually on which Monitor Screen your application comes at the first instance, will determine it's GraphicsConfiguration. Though as I roamed through the API, there is only a getter method for the said GraphicsConfiguration thingy and no setter methods for the same (Still You can specify one through the constructor of any top level Window i.e. JFrame(...)/JDialog(...)).
Now you can occupy your head with this code, which can be used to determine the appropriate location, that you want to set, again, you might have to use focusGain() method in my opinion, to satisfy condition 2 of your question. Have a look at the code attached, though no need to create a new JFrame/JDialog, just watch how to get coordinates for the screen (that you can add in the focusGain() method to determine the location of the whole Application.)
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc =
gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
JFrame f = new
JFrame(gs[j].getDefaultConfiguration());
Canvas c = new Canvas(gc[i]);
Rectangle gcBounds = gc[i].getBounds();
int xoffs = gcBounds.x;
int yoffs = gcBounds.y;
f.getContentPane().add(c);
f.setLocation((i*50)+xoffs, (i*60)+yoffs);
f.show();
}
}
EDIT 3 :
Try to change this :
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
to just :
setLocationRelativeTo(mainWindow);
To test the above thingy, I used my FrameFocus Class as is, though I had added your changes to my CustomDialog method, as shown in this modified CustomDialog Class.
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
//mainWindow.setLocationRelativeTo(null);
//setLocationRelativeTo(null);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
//x = (int) (loc.getX() + (mainWindow.getWidth() - CustomDialog.this.getWidth()) / 2);
//y = (int) (loc.getY() + (mainWindow.getHeight() - CustomDialog.this.getHeight()) / 2);
//CustomDialog.this.setLocation(x, y);
CustomDialog.this.setLocationRelativeTo(mainWindow);
break;
}
}
}
}
#Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
I think, best would be to center the dialog in the middle of the current screen as described here.
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
int x = (screenSize.width - d.getWidth()) / 2;
int y = (screenSize.height - d.getHeight()) / 2;
d.setLocation(x, y);
This always works and how it can be invisible to the user if it is right in the center of the screen? And setLocationRelativeTo can also be used but you need to invoke it at the right time.
use JDialog.setLocation() for moving JDialog on desired Point on the screen
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
public class JDialogAtPoint {
private JFrame frame = new JFrame();
private JPanel panel = new JPanel();
private JDialog dialog;
private Point location;
public JDialogAtPoint() {
createGrid();
createDialog();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
private void createGrid() {
panel.setLayout(new GridLayout(3, 3, 4, 4));
int l = 0;
int row = 3;
int col = 3;
JButton buttons[][] = new JButton[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
buttons[i][j] = new JButton("");
buttons[i][j].putClientProperty("column", i + 1);
buttons[i][j].putClientProperty("row", j + 1);
buttons[i][j].setAction(updateCol());
panel.add(buttons[i][j]);
l++;
}
}
}
private void createDialog() {
dialog = new JDialog();
dialog.setAlwaysOnTop(true);
dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
JPanel pane = (JPanel) dialog.getContentPane();
pane.setBorder(new EmptyBorder(20, 20, 20, 20));
dialog.pack();
}
public Action updateCol() {
return new AbstractAction("Display JDialog at Point") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
System.out.println("Locations coordinates" + btn.getLocation());
System.out.println("clicked column "
+ btn.getClientProperty("column")
+ ", row " + btn.getClientProperty("row"));
if (!dialog.isVisible()) {
showingDialog(btn.getLocationOnScreen());
}
}
};
}
private void showingDialog(final Point loc) {
dialog.setVisible(false);
location = loc;
int x = location.x;
int y = location.y;
dialog.setLocation(x, y);
Runnable doRun = new Runnable() {
#Override
public void run() {//dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
};
SwingUtilities.invokeLater(doRun);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JDialogAtPoint cf = new JDialogAtPoint();
}
});
}
}
With the help of all 3 answerers I have come up with code which seems exactly what I need. First, JFrame got placed in the middle of current screen and then JDialog accordingly.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
break;
}
}
}
I have class JLabelExtended, which extends class javax.swing.JLabel.
I extend it, because I want to add property dragging using mouse.
Here is my code:
public class JLabelExtended extends JLabel {
private MouseMotionAdapter mouseMotionAdapter;
private JLabelExtended jLabelExtended;
public LabelEasy(String text) {
super(text);
jLabelExtended = this;
mouseMotionAdapter = new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
System.out.println(e.getX() + " : " + e.getY());
jLabelExtended.setLocation(e.getX(), e.getY()
);
}
};
jLabelExtended.addMouseMotionListener(mouseMotionAdapter);
}
}
This is console part after label dragged:
163 : 163
144 : -87
163 : 162
144 : -88
163 : 161
144 : -89
I have several questions:
Why e.getY() takes negative results?
When I drag my label there are appeares copy of label which drags near my label. How can I fix it?
When I drag my label, it drags very slowly.For example: when I move my cursor on 10 points my label moves only on 5 point. How can I fix it?
Thanks in advance
Here are else one way to extend JLabel:
public class LabelEasy extends JLabel {
private MouseAdapter moveMouseAdapter;
private MouseMotionAdapter mouseMotionAdapter;
private LabelEasy jLabelExtended;
private int xAdjustment, yAdjustment;
Boolean count = false;
public LabelEasy(String text) {
super(text);
jLabelExtended = this;
moveMouseAdapter = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == 1) {
xAdjustment = e.getX();
yAdjustment = e.getY();
}
}
};
mouseMotionAdapter = new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (count) {
System.out.println(e.getX() + " : " + e.getY());
jLabelExtended.setLocation(xAdjustment + e.getX(), yAdjustment + e.getY());
count = false;
} else {
count = true;
}
;
}
};
jLabelExtended.addMouseMotionListener(mouseMotionAdapter);
jLabelExtended.addMouseListener(moveMouseAdapter);
}
}
But it works like previous variant.
I think you're doing it wrong. The MouseMotionListener is added to the JLabel and its location is relative to the JLabel, not the Container which holds the JLabel, so the information is useless to help you drag it. You may wish to use a MouseAdapter and add it both as a MouseListener and a MouseMotionListener. On mousePressed, get the location of the JLabel and the mouse relative to the screen and then use that for your dragging on mouseDragged. Myself, I wouldn't extend JLabel to do this but would rather just use a regular JLabel, but that's my preference.
Edit: it worked better for me when I dealt with the mouse's position relative to the screen (by calling getLocationOnScreen) and the JLabel's position relative to its Container (by calling getLocation). For e.g.,
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class DragLabelEg {
private static final String[] LABEL_STRINGS = { "Do", "Re", "Me", "Fa",
"So", "La", "Ti" };
private static final int HEIGHT = 400;
private static final int WIDTH = 600;
private static final Dimension MAIN_PANEL_SIZE = new Dimension(WIDTH,
HEIGHT);
private static final int LBL_WIDTH = 60;
private static final int LBL_HEIGHT = 40;
private static final Dimension LABEL_SIZE = new Dimension(LBL_WIDTH,
LBL_HEIGHT);
private JPanel mainPanel = new JPanel();
private Random random = new Random();
public DragLabelEg() {
mainPanel.setPreferredSize(MAIN_PANEL_SIZE);
mainPanel.setLayout(null);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
for (int i = 0; i < LABEL_STRINGS.length; i++) {
JLabel label = new JLabel(LABEL_STRINGS[i], SwingConstants.CENTER);
label.setSize(LABEL_SIZE);
label.setOpaque(true);
label.setLocation(random.nextInt(WIDTH - LBL_WIDTH),
random.nextInt(HEIGHT - LBL_HEIGHT));
label.setBackground(new Color(150 + random.nextInt(105),
150 + random.nextInt(105), 150 + random.nextInt(105)));
label.addMouseListener(myMouseAdapter);
label.addMouseMotionListener(myMouseAdapter);
mainPanel.add(label);
}
}
public JComponent getMainPanel() {
return mainPanel;
}
private class MyMouseAdapter extends MouseAdapter {
private Point initLabelLocation = null;
private Point initMouseLocationOnScreen = null;
#Override
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel)e.getSource();
// get label's initial location relative to its container
initLabelLocation = label.getLocation();
// get Mouse's initial location relative to the screen
initMouseLocationOnScreen = e.getLocationOnScreen();
}
#Override
public void mouseReleased(MouseEvent e) {
initLabelLocation = null;
initMouseLocationOnScreen = null;
}
#Override
public void mouseDragged(MouseEvent e) {
// if not dragging a JLabel
if (initLabelLocation == null || initMouseLocationOnScreen == null) {
return;
}
JLabel label = (JLabel)e.getSource();
// get mouse's new location relative to the screen
Point mouseLocation = e.getLocationOnScreen();
// and see how this differs from the initial location.
int deltaX = mouseLocation.x - initMouseLocationOnScreen.x;
int deltaY = mouseLocation.y - initMouseLocationOnScreen.y;
// change label's position by the same difference, the "delta" vector
int labelX = initLabelLocation.x + deltaX;
int labelY = initLabelLocation.y + deltaY;
label.setLocation(labelX, labelY);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGui();
}
});
}
private static void createGui() {
JFrame frame = new JFrame("App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DragLabelEg().getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}