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();
}
});
}
}
Related
I Think this is a Timer issue, first time ive used them and i feel like im doing it wrong.
I have a method that for testings sake, input 6 images and with the help of a timer paints them to a JPanel:
private void drawDice(Graphics2D g2d) throws IOException, InterruptedException {
image = ImageIO.read(getClass().getResourceAsStream("/1.png"));
m_dice.add(image);
image = ImageIO.read(getClass().getResourceAsStream("/2.png"));
m_dice.add(image);
image = ImageIO.read(getClass().getResourceAsStream("/3.png"));
m_dice.add(image);
image = ImageIO.read(getClass().getResourceAsStream("/4.png"));
m_dice.add(image);
image = ImageIO.read(getClass().getResourceAsStream("/5.png"));
m_dice.add(image);
image = ImageIO.read(getClass().getResourceAsStream("/6.png"));
m_dice.add(image);
time.start();
for(int i = 0; i < m_dice.size(); i++){
g2d.drawImage(m_dice.get(i), 700, 400, null, null);
repaint();
}
time.stop();
}
Timer time = new Timer(1000,this); < at the top of the class
The desired output is that all 6 dice images are shown at one second intervals but only "6.png" shows up.
thank you.
I think that you may be unclear on how a Timer works. Suggestions:
First and foremost -- get rid of the for loop since the Timer's code will replace this.
Next, if this is being called from paintComponent or other painting method, don't. You never want to read images in from a painting method as that will slow down the method and thus slow down the perceived performance of your GUI, not a good thing.
Next, read all the images in once in your constructor and save them to an array or ArrayList of images or Icons. My own vote is an ArrayList<Icon> of ImageIcons.
Easiest way to swap images is to display ImageIcons in a JLabel and simply call setIcon(...) on the JLabel, passing in the newest icon.
Next in your Timer's ActionListener, have a counter int variable that is initialized to 0.
In the ActionListener's actionPerformed method, increment the counter variable, and swap images.
Get the ImageIcon from the ArrayList using the counter as index.
Call setIcon(...) on your JLabel (again, this is all done inside of the actionPerformed method for the Timer).
If the counter is >= the number if icons in your ArrayList, 0 the counter. and call stop() on your Timer.
Something like:
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener(){
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count < IMAGE_COUNT) {
someLabel.setIcon(icons[count]);
count++;
} else {
// stop the timer
((Timer)e.getSource()).stop();
}
}
}).start();
For example, this program "rolls" a die by randomly swapping ImageIcons in a JLabel maxCount number of times:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class RollDice extends JPanel {
// nice public domain dice face images. All 6 images in one "sprite sheet" image.
private static final String IMG_PATH = "https://upload.wikimedia.org/"
+ "wikipedia/commons/4/4c/Dice.png";
private static final int TIMER_DELAY = 200;
private List<Icon> diceIcons = new ArrayList<>(); // list to hold dice face image icons
private JLabel diceLabel = new JLabel(); // jlabel to display images
private Timer diceTimer; // swing timer
public RollDice(BufferedImage img) {
// subdivide the sprite sheet into individual images
// use them to create ImageIcons
// and add them to my diceIcons ArrayList<Icon>.
double imgW = img.getWidth() / 3.0;
double imgH = img.getHeight() / 2.0;
for (int row = 0; row < 2; row++) {
int y = (int) (row * imgH);
for (int col = 0; col < 3; col++) {
int x = (int) (col * imgW);
BufferedImage subImg = img.getSubimage(x, y, (int)imgW, (int)imgH);
diceIcons.add(new ImageIcon(subImg));
}
}
// panel to hold roll dice button
JPanel btnPanel = new JPanel();
btnPanel.setOpaque(false);
btnPanel.add(new JButton(new RollDiceAction("Roll Dice")));
// set the JLabel's icon to the first one in the collection
diceLabel.setIcon(diceIcons.get(0));
setLayout(new BorderLayout());
setBackground(Color.WHITE);
add(diceLabel);
add(btnPanel, BorderLayout.PAGE_END);
}
public void rollDice() {
// if the timer's already running, exit this method
if (diceTimer != null && diceTimer.isRunning()) {
return;
}
// else create a new Timer and start it
diceTimer = new Timer(TIMER_DELAY, new TimerListener());
diceTimer.start();
}
// ActionListener for the Swing Timer
private class TimerListener implements ActionListener {
private int count = 0; // count how many times dice changes face
private final int maxCount = 20;
#Override
public void actionPerformed(ActionEvent e) {
// once there are max count changes, stop the timer
if (count >= maxCount) {
((Timer) e.getSource()).stop();
}
// get a random index from 0 to 5
int randomIndex = (int) (Math.random() * diceIcons.size());
// show that random number's dice face
diceLabel.setIcon(diceIcons.get(randomIndex));
count++; // increment the count
}
}
// ActionListener for our button
private class RollDiceAction extends AbstractAction {
public RollDiceAction(String name) {
super(name); // text to show in the button
}
#Override
public void actionPerformed(ActionEvent e) {
rollDice(); // simply call the roll dice method
}
}
private static void createAndShowGui(BufferedImage img) {
RollDice mainPanel = new RollDice(img);
JFrame frame = new JFrame("RollDice");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
URL imgUrl = new URL(IMG_PATH);
final BufferedImage img = ImageIO.read(imgUrl);
SwingUtilities.invokeLater(() -> {
createAndShowGui(img);
});
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
I am expanding Image editing application in Java. I want to make a class that would adjust contrast of the image. Main class calls apply method and passes image that has to be modified. I managed to create JFrame and algorithm for calculation, but I have problems with Action Listener because I don't know how to make my apply method wait for the users input and only then calculate and edit image. Here is the code for contrast class:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
lic class ContrastFilter extends Filter implements ActionListener {
private JFrame contFr;
private JButton ok;
private JTextField textF;
private String s;
private int contV;
private int factor;
public ContrastFilter(String name){
super(name);
}
public void makeFrame(){
contFr = new JFrame("contrast window");
Container contentPane = contFr.getContentPane();
contentPane.setLayout(new FlowLayout());
JLabel label = new JLabel("Enter contrast");
contentPane.add(label, BorderLayout.PAGE_START);
textF = new JTextField(5);
contentPane.add(textF, BorderLayout.PAGE_START);
ok = new JButton("OK");
ok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == ok){
s = textF.getText();
contV = Integer.parseInt(s);
factor = Math.round((259*(contV+255))/(255*(259 - contV)));
}}
});
contentPane.add(ok, BorderLayout.PAGE_START);
contFr.pack();
contFr.setVisible(true);
}
public void apply(OFImage image) {
makeFrame();
int height = image.getHeight();
int width = image.getWidth();
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
Color pix = image.getPixel(x, y);
image.setPixel(x, y, new Color(trunC(factor*(pix.getRed()-128)+128),
trunC(factor*(pix.getGreen()-128)+128),
trunC(factor*(pix.getBlue()-128)+128)));
} } }
public int trunC(int a){
if (a>255){
return 255;
}
return a;
}
}
If you want your apply() method to work after the button "OK" was clicked (user inputs something, then clicks OK), then you need to put an apply() invocation into you ActionListener's actionPerformed() method body.
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;
}
}
}
Right now I am trying to make it so that the connect 4 grid on the gui is always a 7x8 no matter what window size. I have been trying to set the button array with a setMaximumSize and it's not working.
Here is the code that sets the JButton array
void ResetGame()
{
JLabel label = new JLabel("Click a column to drop piece");
for(int r=0;r<gameBoard.length;r++)
{
java.util.Arrays.fill(gameBoard[r],0,gameBoard[r].length,'0');
//loop through board columns
for(int c=0;c<gameBoard[r].length;c++)
{
gameButtons[r][c]= new JButton(empty);
panel.add(gameButtons[r][c]);
gameButtons[r][c].setPreferredSize(new Dimension(70,70));
//Allows buttons to be arranged as grid.
GridLayout grid = new GridLayout(0,8);
//Sets into grid.
gameButtons[r][c].setLayout(grid);
gameButtons[r][c].setMaximumSize(new Dimension(0,10));
}
panel.add(label);
}
// loop through array setting char array back to ' ' and buttons array back to empty pic
// reset currentPlayer and numMoves variables
}
Just in case I'll also include the window creation method here.
public void CreateWindow()
{
//Sets window title and create window object.
JFrame aWindow = new JFrame("Connect Four");
//Set window position and size
aWindow.setBounds(200,100,600,800);
//What close button does.
aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Make window visible.
aWindow.setVisible(true);
//Sets content area to work with stuff.
aWindow.setContentPane(panel);
//Gets content pane.
Container content = aWindow.getContentPane();
}
Not sure of what you are trying to achieve with setMaximumSize. Without explicit and precise requirements, we can hardly help you.
So, I would suggest that you take a look at the following snippet (which is an SSCCE) and try to find out what you are doing wrong:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Connect4 {
public class GameButton extends JPanel {
private final int row;
private final int column;
private Color color;
public GameButton(final int row, final int column) {
super();
this.row = row;
this.column = column;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Game button " + row + " " + column + " has been pressed");
}
});
}
public void setColor(Color color) {
this.color = color;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int size = Math.min(getWidth(), getHeight());
int offset = (int) ((double) size / 10);
size = size - 2 * offset;
if (color == null) {
g.setColor(Color.BLACK);
g.drawOval(offset, offset, size, size);
} else {
g.setColor(color);
g.fillOval(offset, offset, size, size);
}
}
}
protected void initUI() {
JPanel gridPanel = new JPanel(new GridLayout(7, 8));
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 8; j++) {
GameButton gameButton = new GameButton(i, j);
gridPanel.add(gameButton);
}
}
JFrame frame = new JFrame(Connect4.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(gridPanel);
frame.setSize(600, 600);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Connect4().initUI();
}
});
}
}
setMaximumSize() puts a bound on how large something can be. Depending on what you are using for a layout manager you want either setPreferredSize() or setSize().
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);
}
});