I have a class called SuggestionField It will show the user a list of items to autocomplete his input. he let of inputs is displayed in a JDialog(suggestionFrame). I need to set the JDialogs position to be below its parent, but I can not get the parents (JTextField) X and Y relative to the screen.
Example of the issue
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Arrays;
public class Runner {
public static void main(String[] args) {
JFrame frame = new JFrame();
String[] items = new String[]{"Tiger", "Wolf", "Car", "Cat", "Space", "Sing", "Scene"};
SuggestionField suggestionField = new SuggestionField(new ArrayList<>(Arrays.asList(items)), frame);
frame.setDefaultCloseOperation(3);
frame.setSize(100, 65);
frame.setLocationRelativeTo(null);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1,1));
panel.add(suggestionField);
frame.add(panel);
frame.setVisible(true);
}
public static class SuggestionField extends JTextField implements DocumentListener, KeyListener {
String[] values = new String[0];
ArrayList<String> displayValues = new ArrayList<>(0);
JDialog suggestionFrame;
JPanel suggestionPanel = new JPanel();
Color backGround = new Color(109, 104, 104, 133);
Color selectBackGround = new Color(109, 104, 104, 133);
Color textColor = new Color(5, 19, 88, 255);
Color selectTextColor = new Color(115, 134, 238, 255);
BoxLayout bl = new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS);
int selectedEntry = 0;
public SuggestionField(ArrayList<String> values, JFrame parentDisplay) {
this.getDocument().addDocumentListener(this);
this.addKeyListener(this);
suggestionFrame = new JDialog(parentDisplay);
suggestionFrame.add(suggestionPanel);
suggestionFrame.setUndecorated(true);
suggestionFrame.setAlwaysOnTop(true);
suggestionFrame.setFocusable(false);
suggestionPanel.setFocusable(false);
bl.maximumLayoutSize(suggestionPanel);
this.values = values.toArray(this.values).clone();
}
public boolean updateSuggestions() {
boolean added = false;
boolean add;
displayValues.clear();
for (int i = 0;i < values.length;i++) {
add = true;
for (int k = 0;k < this.getText().length() && k < values[i].length();k++) {
if (values[i].toUpperCase().charAt(k) != this.getText().toUpperCase().charAt(k)) {
add = false;
break;
}
}
if (add && !values[i].equalsIgnoreCase(this.getText()) && values[i].length() > this.getText().length()) {
added = true;
displayValues.add(values[i]);
}
}
return added;
}
private void updateDisplay() {
suggestionFrame.setSize(this.getWidth(), 16 * displayValues.size());
suggestionPanel.removeAll();
suggestionPanel.setLayout(new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS));
for (int i = 0;i < displayValues.size();i++) {
JLabel a = new JLabel(displayValues.get(i));
if (i == selectedEntry) {
a.setBackground(selectBackGround);
a.setForeground(selectTextColor);
} else {
a.setBackground(backGround);
a.setForeground(textColor);
}
suggestionPanel.add(a);
}
suggestionPanel.revalidate();
suggestionFrame.revalidate();
suggestionFrame.setLocation(this.getX(), this.getY() + this.getHeight());
suggestionFrame.setVisible(true);
}
#Override
public void insertUpdate(DocumentEvent e) {
if (updateSuggestions()) {
updateDisplay();
} else suggestionFrame.setVisible(false);
selectedEntry = 0;
this.requestFocus();
}
#Override
public void removeUpdate(DocumentEvent e) {
if (updateSuggestions()) {
updateDisplay();
} else suggestionFrame.setVisible(false);
selectedEntry = 0;
this.requestFocus();
}
#Override
public void changedUpdate(DocumentEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int ID = e.getKeyCode();
switch (ID) {
case 40:
if (selectedEntry < displayValues.size() - 1) selectedEntry++;
updateDisplay();
break;
case 38:
if (selectedEntry > 0) selectedEntry--;
updateDisplay();
break;
case 10:
this.setText(displayValues.get(selectedEntry));
break;
case 27:
suggestionFrame.setVisible(false);
break;
}
this.requestFocus();
this.grabFocus();
}
#Override
public void keyReleased(KeyEvent e) {
}
}
}
Is there any way that inside the updateDisplay() (line 97) method I could get the absolute position of the JTextField for suggestionFrame
You can use SwingUtilities.convertPointToScreen to do this.
Just pass (x, y+height) and your input field to get the starting point to display your dropdown.
JButton component = new JButton();
Point pt = new Point(component.getLocation());
pt.y += component.getHeight();
SwingUtilities.convertPointToScreen(pt, component);
// pt is now in screen coords... so you can use it to position the pop-up
The full documentation can be found here: https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html
I need to set the JDialogs position to be below its parent..
Use Window.setLocationRelativeTo(Component).
This will just place the JDialog ontop of the JTextFiled
Actually, it's more subtle than that. It will take into account if the dialog would otherwise be off the screen, for example, and move it back on. That is something that would need to be explicitly handled, if basing the position on the 'location on screen'.
Related
Im creating a program in which I must from time to time reset a button Array and display it on a jPanel. The function below adds the jButtons to my panel and displays them perfectly the first time that it is called, but from then on, every time I call it (after emptying the jButton array and applying .removeAll() to the panel) it wont let me change the background color of the jButton. Some assistance to help me find out why this is would be great, thanks.
import java.awt.*;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import javafx.scene.layout.Border;
import javax.swing.*;
/**
*
* #author Luis
*/
public class MineSweeper extends JFrame implements ActionListener {
int int_dim = 11;
int int_cellsShown = 0;
JButton[][] arr_btnField = new JButton[int_dim][int_dim];
int[][] arr_solution = new int[int_dim][int_dim];
Color[] clr_palette = {Color.white, new Color(0X00, 0X94, 0XFF), new Color(0X00, 0X26, 0XFF), new Color(0X00, 0XAA, 0X0A), Color.red, Color.MAGENTA, new Color(0XFF, 0X00, 0X00), new Color(0X9B, 0X00, 0X00)};
boolean bool_change = false;
boolean bool_won = false;
boolean bool_firstround = false;
javax.swing.border.Border border = BorderFactory.createLineBorder(Color.darkGray, 1, true);
MenuBar menu_bar;
Menu menu;
MenuItem optionNew;
//boolean[][] arr_boolShowed=new boolean[int_dim][int_dim];
int int_mines = 8;
ArrayList<Integer> arl_field = new ArrayList<Integer>();
JPanel jpanel = new JPanel();
JPanel jpanel2 = new JPanel();
//ArrayList<Boolean> arl_boolShowed = new ArrayList<Boolean>();
/**
* #param args the command line arguments
*/
public MineSweeper() throws FontFormatException, IOException {
resetGame();
//JFrame frame = new JFrame("");
this.getContentPane().add(jpanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(true);
this.setTitle("Minesweeper");
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
this.setSize(500, 500);
menu_bar = new MenuBar();
menu = new Menu("File");
optionNew = new MenuItem("Win");
optionNew.addActionListener(this);
menu.add(optionNew);
menu_bar.add(menu);
this.setMenuBar(menu_bar);
}
public void resetGame() {
jpanel.removeAll();
arl_field.clear();
arr_btnField = new JButton[int_dim][int_dim];
arr_solution = new int[int_dim][int_dim];
bool_change = false;
bool_won = false;
//arl_field = new ArrayList<Integer>();
for (int i = 0; i < arr_solution.length; i++) {
for (int j = 0; j < arr_solution[i].length; j++) {
arr_solution[i][j] = 1;
}
}
jpanel.setLayout(new GridLayout(0, int_dim));//if(bool_firstround==false)jpanel.setLayout(new GridLayout(0,int_dim));
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j] = new JButton();////if(bool_firstround==false)arr_btnField[i][j] = new JButton();//arl_field.get(i*int_dim+j)+"");
arr_btnField[i][j].setText("");
arr_btnField[i][j].setBackground(new Color(0X00, 0X94, 0XFF));
arr_btnField[i][j].setBorder(border);
arr_btnField[i][j].setForeground(clr_palette[1]);
arr_btnField[i][j].addMouseListener(listener);
arr_btnField[i][j].setFocusable(false);
jpanel.add(arr_btnField[i][j]);
}
}
jpanel.revalidate();
jpanel.repaint();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
MouseListener listener = new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
outerloop:
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
if (e.getSource() == arr_btnField[i][j]) {
if (SwingUtilities.isLeftMouseButton(e)) {
labelText(i, j);
}
if (SwingUtilities.isRightMouseButton(e)) {
arr_btnField[i][j].setBackground(Color.red);
}
//bool_won=false;
break outerloop;
}
}
}
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
if (bool_won == true)
gameWon();
}
#Override
public void mouseExited(MouseEvent e) {
}
};
public void labelText(int i, int j) {
if (bool_won == false) {
arr_btnField[i][j].setText("1");
arr_btnField[i][j].setBackground(Color.white);
if (arr_btnField[i][j].getBorder() == border) {
int_cellsShown++;
System.out.println("Cells shown: " + int_cellsShown);
if (int_cellsShown >= (int_dim * int_dim - int_mines)) {
bool_won = true;
}
}
if (bool_won == false)
arr_btnField[i][j].setBorder(BorderFactory.createLineBorder(Color.darkGray, 1, true));
}
}
public void gameWon() {
int dialogResult = JOptionPane.showConfirmDialog(null, "You Won! Do you want to start a new game?", "Congratulations!", JOptionPane.YES_NO_OPTION);
if (dialogResult == JOptionPane.YES_OPTION) {
bool_won = false;
int_cellsShown = 0;
resetGame();
}
}
#Override
public void actionPerformed(ActionEvent e) {
int_cellsShown = 0;
int_dim++;
resetGame();
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j].setBackground(Color.red);
}
}
}
}
Display after the first time:
Display after the second time:
I invoke .revalidate in the fourth line of the method.
That doesn't do anything since there on no components added to the panel. The revalidate() needs to be done AFTER the components have been added.
The displayed panel is not jpanel, the displayed panel is jpanel2, thats why I assign jpanel2 to the value of jpanel in the end of the method.
You can't just change a reference and expect the components to be moved from one panel to another.
The components need to be added to the panel that is added to the GUI.
Edit:
First of all Swing components start with "J". Don't use AWT components (MenuBar, Menu, MenuItem) in a Swing application.
The problem is your LAF:
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
The components are created with the current LAF. When you first create the game, the default LAF is used to create all the buttons (and other components). This LAF allows you to change the background color of the buttons.
However, then you change the LAF. So when you reset the game board the buttons are now created with the System LAF. This LAF apparently does not allow you to change the background color of the button.
This should be easy to test. Create a GUI:
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JButton button = new JButton("testing");
button.setBackground(Color.RED);
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( button );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
First test the code as above to see if the background of the button changes.
Then uncomment the LAF change and retest.
A possible solution so you are not dependent on the LAF is to use an Icon to represent the background color of the button. Then you can center any text on top of the Icon. Something like:
import java.awt.*;
import javax.swing.*;
public class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
JPanel panel = new JPanel( new GridLayout(2, 2) );
for (int i = 0; i < 4; i++)
{
Icon icon = new ColorIcon(Color.RED, 50, 50);
JLabel label = new JLabel( icon );
label.setText("" + i);
label.setHorizontalTextPosition(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.CENTER);
panel.add(label);
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setSize(200, 200);
f.setLocationRelativeTo( null );
f.setVisible(true);
}
}
Yep, I too just noticed, that the problem is here:
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
If you comment out the UIManager line, your code works. This line is only valid after the GUI has been created, and so doesn't take effect until new components are created. Note that I was working on minimizing your code to discover this, and was cutting out code to see what caused the problem until this was all that was left.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.UIManager;
#SuppressWarnings("serial")
public class MineSweeper extends JFrame implements ActionListener {
private static final Color BTN_COLOR = new Color(0X00, 0X94, 0XFF);
int int_dim = 11;
JButton[][] arr_btnField = new JButton[int_dim][int_dim];
JMenuBar menu_bar;
JMenu menu;
JMenuItem optionNew;
JPanel jpanel = new JPanel();
public MineSweeper() {
resetGame();
this.getContentPane().add(jpanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(true);
this.setTitle("Minesweeper");
menu_bar = new JMenuBar();
menu = new JMenu("File");
menu.setMnemonic(KeyEvent.VK_F);
optionNew = new JMenuItem("Win");
optionNew.setMnemonic(KeyEvent.VK_W);
optionNew.addActionListener(this);
menu.add(optionNew);
menu_bar.add(menu);
this.setJMenuBar(menu_bar);
this.setPreferredSize(new Dimension(500, 500));
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void resetGame() {
jpanel.removeAll();
arr_btnField = new JButton[int_dim][int_dim];
jpanel.setLayout(new GridLayout(0, int_dim));
for (int i = 0; i < arr_btnField.length; i++) {
for (int j = 0; j < arr_btnField[i].length; j++) {
arr_btnField[i][j] = new JButton();
arr_btnField[i][j].setBackground(BTN_COLOR);
jpanel.add(arr_btnField[i][j]);
}
}
jpanel.revalidate();
jpanel.repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
resetGame();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MineSweeper();
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
});
}
}
This is my code for a game im making. At the moment im not worried about how the game functions I've been more so worried about the fact that each time I hit the UP button the panels disappear and sometimes when i hit the LEFT button as well. Is there an explanation to this can anyone help me understand why this happens?? I have a feeling it has something to do with my if statements but im not really sure. also, im messing around with the key listener and if you could give me some advice on key listeners like some dos and donts I really appreciate the help!!
import javax.swing.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.*;
public class Game extends Applet implements ActionListener,KeyListener {
Image image;
MediaTracker tr;
JLabel label,computerLabel;
JPanel panel,computerPanel;
Button start,up,down;
Label result;
Dimension SIZE = new Dimension(50,50);
int x = 0;
int y = 0;
int w = 100;
int q = 100;
int WIDTH = 50;
int HEIGHT = 50;
//Player Integers
int zeroPosX,zeroPosY,xLeft,xUp;
//Computer integers
int compZeroPosX,compZeroPosY,compXLeft,compXUp;
//--------------------------------------
public void init() {
setLayout(new FlowLayout());
start = new Button("Start");
up = new Button("UP");
down = new Button("LEFT");
//PlayerPiece stuff
ImageIcon icon = new ImageIcon("playerpiece.png");
label = new JLabel(icon);
panel = new JPanel();
label.setVisible(true);
panel.add(label);
panel.setPreferredSize(SIZE);
//ComputerPiece Stuff
ImageIcon computerIcon = new ImageIcon("computerPiece.png");
computerPanel = new JPanel();
computerLabel = new JLabel(computerIcon);
computerLabel.setVisible(true);
computerPanel.add(computerLabel);
computerPanel.setPreferredSize(SIZE);
//===============================================
result = new Label("=========");
addKeyListener(this);
setSize(650,650);
up.addActionListener(this);
down.addActionListener(this);
start.addActionListener(this);
label.setSize(WIDTH,HEIGHT);
label.setLocation(0,0);
add(computerPanel);
add(panel);
add(start);
add(up);
add(down);
add(result);
}
//--------------------------------------
public void paint(Graphics g) {
Graphics2D firstLayer = (Graphics2D)g;
Graphics2D secondLayer = (Graphics2D)g;
Graphics2D thirdLayer = (Graphics2D)g;
secondLayer.setColor(Color.BLACK);
for(x=100; x<=500; x+=100)
for(y=100; y <=500; y+=100)
{
firstLayer.fillRect(x,y,WIDTH,HEIGHT);
}
for(w=150; w<=500; w+=100)
for(q=150; q <=500; q+=100)
{
secondLayer.fillRect(w,q,WIDTH,HEIGHT);
}
}
//--------------------------------------
public void actionPerformed(ActionEvent ae) {
int [] range = {50,0,0,0,0};
int selection = (int)Math.random()*5 ;
//~~~~~~~~~~~~~~~~~~~~~~~~~
//PlayerPositioning
zeroPosX = panel.getX();
zeroPosY = panel.getY();
xLeft = zeroPosX - 50;
xUp = zeroPosY - 50;
//ComputerPositioning
compZeroPosX = computerPanel.getX();
compZeroPosY = computerPanel.getY();
compXLeft = compZeroPosX - range[selection];
compXUp = compZeroPosY - range[selection];
//~~~~~~~~~~~~~~~~~~~~~~~~~~
Button user = (Button)ae.getSource();
//Starting the game
if(user.getLabel() == "Start") {
result.setText("=========");
//playersetup
label.setLocation(0,0);
panel.setLocation(300,500);
//============================
//npc setup
computerLabel.setLocation(0,0);
computerPanel.setLocation(500,300);
}
if(compZeroPosX >= 150) {
if(compZeroPosY >= 150) {
if(zeroPosX >= 150) {
if(zeroPosY >=150) {
if(user.getLabel() == "UP") {
panel.setLocation(zeroPosX,xUp);
}
else
computerPanel.setLocation(compZeroPosX,compXUp);
if(user.getLabel() == "LEFT") {
panel.setLocation(xLeft,zeroPosY);
}
else
computerPanel.setLocation(compXLeft,compZeroPosY);
if(panel.getX() < 150)
result.setText("GAME-OVER");
if(panel.getY() < 150)
result.setText("GAME-OVER");
}
}
}
}
}
#Override
public void keyPressed(KeyEvent kp) {
int keycode = kp.getKeyCode();
switch (keycode) {
case KeyEvent.VK_W:
panel.setLocation(xLeft,zeroPosY);
break;
}
}
#Override
public void keyReleased(KeyEvent kr) {
}
#Override
public void keyTyped(KeyEvent kt) {
}
}
Issues and suggestions:
You're mixing AWT (e.g., Applet, Button, Label) with Swing (e.g., JPanel, JLabel) dangerously and without need. Stick with Swing and get rid of all vestiges of AWT.
You're painting directly in a top-level window, here the Applet, a dangerous thing to do. Don't. Follow the Swing graphics tutorials and do your drawing in a JPanel's paintComponent method.
You're not calling the super method within your painting method override, another dangerous thing to do, and another indication that you're trying to do this without reading the important relevant tutorials.
Don't compare Strings using == or !=. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here.
You're trying to directly set the location of a component such as a JPanel without regard for the layout managers. Don't do this. Instead move logical (non-component) entities and display the movement in your graphics.
You can find links to the Swing tutorials and to other Swing resources here: Swing Info
Later we can talk why you should avoid applets of all flavors...
Myself, I'd move ImageIcons around a grid of JLabels and not directly use a painting method at all. For example,
To see, run the following code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Game2 extends JPanel {
private static final String CPU_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/"
+ "Gorilla-thinclient.svg/50px-Gorilla-thinclient.svg.png";
private static final String PERSON_PATH = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/"
+ "Emblem-person-blue.svg/50px-Emblem-person-blue.svg.png";
private static final int SQR_WIDTH = 50;
private static final int SIDES = 10;
private static final Dimension SQR_SIZE = new Dimension(SQR_WIDTH, SQR_WIDTH);
private static final Color DARK = new Color(149, 69, 53);
private static final Color LIGHT = new Color(240, 220, 130);
private JLabel[][] labelGrid = new JLabel[SIDES][SIDES];
private Icon playerIcon;
private Icon computerIcon;
public Game2() throws IOException {
// would use images instead
playerIcon = createIcon(PERSON_PATH);
computerIcon = createIcon(CPU_PATH);
JPanel buttonPanel = new JPanel();
buttonPanel.add(new JButton(new StartAction("Start", KeyEvent.VK_S)));
buttonPanel.add(new JButton(new UpAction("Up", KeyEvent.VK_U)));
buttonPanel.add(new JButton(new LeftAction("Left", KeyEvent.VK_L)));
JPanel gameBrd = new JPanel(new GridLayout(SIDES, SIDES));
gameBrd.setBorder(BorderFactory.createLineBorder(Color.BLACK));
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
JLabel label = new JLabel();
label.setPreferredSize(SQR_SIZE);
label.setOpaque(true);
Color c = i % 2 == j % 2 ? DARK : LIGHT;
label.setBackground(c);
gameBrd.add(label);
labelGrid[i][j] = label;
}
}
setLayout(new BorderLayout());
add(buttonPanel, BorderLayout.PAGE_START);
add(gameBrd);
// random placement, just for example
labelGrid[4][4].setIcon(computerIcon);
labelGrid[5][5].setIcon(playerIcon);
}
private Icon createIcon(String path) throws IOException {
URL url = new URL(path);
BufferedImage img = ImageIO.read(url);
return new ImageIcon(img);
}
private abstract class MyAction extends AbstractAction {
public MyAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
}
private class StartAction extends MyAction {
public StartAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO start game code
}
}
// move all icons up
private class UpAction extends MyAction {
public UpAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// collection to hold label that needs to be moved
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newI = i == 0 ? labelGrid.length - 1 : i - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[newI][j], icon);
}
}
}
// move the icon after the iteration complete so as not to move it twice
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
// move all icons left
private class LeftAction extends MyAction {
public LeftAction(String name, int mnemonic) {
super(name, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Map<JLabel, Icon> labelMap = new HashMap<>();
for (int i = 0; i < labelGrid.length; i++) {
for (int j = 0; j < labelGrid[i].length; j++) {
Icon icon = labelGrid[i][j].getIcon();
if (icon != null) {
int newJ = j == 0 ? labelGrid[i].length - 1 : j - 1;
labelGrid[i][j].setIcon(null);
labelMap.put(labelGrid[i][newJ], icon);
}
}
}
for (JLabel label : labelMap.keySet()) {
label.setIcon(labelMap.get(label));
}
}
}
private static void createAndShowGui() {
Game2 mainPanel = null;
try {
mainPanel = new Game2();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("Game2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
I'm trying to create a "Tic Tac Toe" game. I've chosen to create a variation of JPanel to represent each square. The class beneath represents one of 9 panels that together make up my game board.
Now the problem I'm having is that when I click the panel a 'X' should be displayed inside of the panel, but nothing happens. I'd very much appreciate it if someone steered me in the right direction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener {
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
add(ticTacLbl, 0);
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
add(ticTacLbl, 0);
isUsed = true;
}
} else {
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
EDIT:
I simply added my label component in the constructor of my TicTacToePanel so that I no longer have to call revalidate() and I'm not adding components during runtime.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TicTacToePanel extends JPanel implements MouseListener{
private boolean isPlayer1Turn = true;
private boolean isUsed = false;
private JLabel ticTacLbl = new JLabel();
public TicTacToePanel(){
add(ticTacLbl, 0);
setBorder(BorderFactory.createLineBorder(Color.BLACK));
addMouseListener(this);
}
public void mouseClicked(MouseEvent e){
}
public void mousePressed(MouseEvent e){
if (!isUsed) {
if (isPlayer1Turn) {
ticTacLbl.setForeground(Color.red);
ticTacLbl.setText("X");
isUsed = true;
} else {
ticTacLbl.setForeground(Color.blue);
ticTacLbl.setText("O");
isUsed = true;
}
}
else{
}
}
public void mouseReleased(MouseEvent e){
}
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e){
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToePanel());
}
}
The GUI constructor:
public TicTacToeGUI(int gameMode){
if(gameMode == 0){
amountOfPanels = 9;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(3, 3));
setPreferredSize(new Dimension(100, 100));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
else if(gameMode == 1){
amountOfPanels = 225;
TicTacToePanel[] panelArr = new TicTacToePanel[amountOfPanels];
add(gamePanel, new GridLayout(15, 15));
setPreferredSize(new Dimension(500, 500));
for(int i = 0; i < amountOfPanels; i++){
panelArr[i] = new TicTacToePanel();
gamePanel.add(panelArr[i]);
}
}
}
public static void main(String[] args){
JOptionPane.showMessageDialog(null, new TicTacToeGUI(0));
}
}
When you add/remove components at runtime, always call revalidate() afterwards. revalidate() makes the component refresh/relayout.
So just call revalidate() after you add the label and it will work.
If you're goal is to create a Tic Tac Toe game, then you may wish to re-think your current strategy of adding components to the GUI on the fly. Much better would be to create a grid of components, say of JLabel, and place them on the JPanel at program start up. This way you can change the pressed JLabel's text and color, and even its Icon if you want to be fancy during program run without having to add or remove components. For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class TicTacToePanel extends JPanel {
private static final int ROWS = 3;
private static final int MY_C = 240;
private static final Color BG = new Color(MY_C, MY_C, MY_C);
private static final int PTS = 60;
private static final Font FONT = new Font(Font.SANS_SERIF, Font.BOLD, PTS);
public static final Color X_COLOR = Color.BLUE;
public static final Color O_COLOR = Color.RED;
private JLabel[][] labels = new JLabel[ROWS][ROWS];
private boolean xTurn = true;
public TicTacToePanel() {
setLayout(new GridLayout(ROWS, ROWS, 2, 2));
setBackground(Color.black);
MyMouse myMouse = new MyMouse();
for (int row = 0; row < labels.length; row++) {
for (int col = 0; col < labels[row].length; col++) {
JLabel label = new JLabel(" ", SwingConstants.CENTER);
label.setOpaque(true);
label.setBackground(BG);
label.setFont(FONT);
add(label);
label.addMouseListener(myMouse);
}
}
}
private class MyMouse extends MouseAdapter {
#Override // override mousePressed not mouseClicked
public void mousePressed(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
String text = label.getText().trim();
if (!text.isEmpty()) {
return;
}
if (xTurn) {
label.setForeground(X_COLOR);
label.setText("X");
} else {
label.setForeground(O_COLOR);
label.setText("O");
}
// information to help check for win
int chosenX = -1;
int chosenY = -1;
for (int x = 0; x < labels.length; x++) {
for (int y = 0; y < labels[x].length; y++) {
if (labels[x][y] == label) {
chosenX = x;
chosenY = y;
}
}
}
// TODO: check for win here
xTurn = !xTurn;
}
}
private static void createAndShowGui() {
TicTacToePanel mainPanel = new TicTacToePanel();
JFrame frame = new JFrame("Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.DISPOSE_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();
}
});
}
}
The following code is working in jdk 1.8 update 45 and in java 1.8 update 31 but NOT in java 1.8 update 45.
The program is a button moving back and forth until the user presses the button and makes it stop and the text is changed to "MOVE". When the button is pressed again, the button starts moving and the text is changed to "STOP".
In java 8 update 45, the button does not start moving but the text changes. Why?
package mainpackage;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class mainPanel implements ActionListener {
JFrame frame1;
JPanel panel1;
JButton button = new JButton("STOP");
boolean buttonPressed = false;
boolean move = true;
// 0 = left & 1 = right
int direction = 1;
int x = 0;
public static void main(String[] args) {
new mainPanel().loadGUI();
}
public void loadGUI() {
frame1 = new JFrame("Moving button");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setVisible(true);
frame1.setSize(300, 58);
frame1.setResizable(false);
panel1 = new JPanel(); // Xwidth=294, Yheight=272
panel1.setSize(300, 30);
panel1.setLayout(null);
frame1.add(panel1);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame1.setLocation(dim.width/2-frame1.getSize().width/2, dim.height/2-frame1.getSize().height/2);
button.setSize(80, 30); //X, Y
panel1.add(button);
button.addActionListener(this);
while(true) moveButton();
}
public void moveButton() {
while(move == true) {
switch(direction) {
// left
case 0: {
while(x > 0) {
if(move == false) break;
button.setLocation(x, 0);
x--;
panel1.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(mainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
if(buttonPressed == true) {
direction = 0;
buttonPressed = false;
} else if(buttonPressed == false) {
direction = 1;
buttonPressed = false;
}
}
// right
case 1: {
while(x < panel1.getWidth() - button.getWidth()) {
if(move == false) break;
button.setLocation(x, 0);
x++;
panel1.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(mainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
if(buttonPressed == true) {
direction = 1;
buttonPressed=false;
} else if(buttonPressed == false) {
direction=0;
buttonPressed=false;
}
}
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (move == true){
move=false;
} else if (move == false) {
move=true;
}
buttonPressed = true;
if((button.getText()).equals("STOP")) {
button.setText("MOVE");
} else button.setText("STOP");
}
}
Yours is broken code to begin with since it ignores Swing threading rules, and frankly I'm surprised that it worked in previous versions of Java. You're calling a while (true) loop that will tie up any thread that it is called in. Java 8 is correctly trying to start your Swing GUI on the Swing event thread, something that all Swing programs should do. If you get rid of the while (true) loops that risk being called on the Swing event dispatch thread, and instead use a Swing Timer your code should work. The Timer will run a loop in a background thread, but all code called repeatedly in its ActionListener will be called on the Swing event thread.
For example:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MyMainPanel extends JPanel {
private static final int PREF_W = 300;
private static final int PREF_H = 30;
private static final int TIMER_DELAY = 20;
public static final int DELTA_X = 3;
private JButton moveButton = new JButton(new MoveButtonAction("Move"));
private Timer moveTimer = new Timer(TIMER_DELAY, new MoveTimerListener());
private boolean moveRight = true;
public MyMainPanel() {
moveButton.setSize(moveButton.getPreferredSize());
int y = (getPreferredSize().height - moveButton.getPreferredSize().height) / 2;
moveButton.setLocation(0, y);
setLayout(null); // !! lord I hate this
add(moveButton);
moveTimer.start();
}
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = Math.max(superSz.width, PREF_W);
int prefH = Math.max(superSz.height, PREF_H);
return new Dimension(prefW, prefH);
}
private class MoveButtonAction extends AbstractAction {
public MoveButtonAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
moveRight = !moveRight;
}
}
private class MoveTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (moveRight) {
if (moveButton.getLocation().x + moveButton.getWidth() >= getWidth()) {
moveRight = false;
}
} else {
if (moveButton.getLocation().x <= 0) {
moveRight = true;
}
}
int x = moveButton.getLocation().x + (moveRight ? DELTA_X : -DELTA_X);
int y = moveButton.getLocation().y;
moveButton.setLocation(new Point(x, y));
repaint();
}
}
private static void createAndShowGui() {
MyMainPanel mainPanel = new MyMainPanel();
JFrame frame = new JFrame("GUI Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You will want to read up on Swing thread safety to see why your program is failing. See Lesson: Concurrency in Swing to see more on this.
Edit: code that stops and starts movement:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyMainPanel extends JPanel {
private static final String MOVE = "Move";
private static final String STOP = "Stop";
private static final int PREF_W = 300;
private static final int PREF_H = 30;
private static final int TIMER_DELAY = 20;
public static final int DELTA_X = 3;
private MoveButtonAction moveButtonAction = new MoveButtonAction(STOP);
private JButton moveButton = new JButton(moveButtonAction);
private Timer moveTimer = new Timer(TIMER_DELAY, new MoveTimerListener());
private boolean moveRight = true;
public MyMainPanel() {
moveButton.setSize(moveButton.getPreferredSize());
int y = (getPreferredSize().height - moveButton.getPreferredSize().height) / 2;
moveButton.setLocation(0, y);
setLayout(null); // !! lord I hate this
add(moveButton);
moveTimer.start();
}
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = Math.max(superSz.width, PREF_W);
int prefH = Math.max(superSz.height, PREF_H);
return new Dimension(prefW, prefH);
}
private class MoveButtonAction extends AbstractAction {
public MoveButtonAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (MOVE.equals(getValue(NAME))) {
moveTimer.start();
putValue(NAME, STOP);
int mnemonic = (int) STOP.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
} else {
moveTimer.stop();
putValue(NAME, MOVE);
int mnemonic = (int) MOVE.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
AbstractButton button = (AbstractButton) e.getSource();
button.setSize(button.getPreferredSize());
repaint();
}
}
private class MoveTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (moveRight) {
if (moveButton.getLocation().x + moveButton.getWidth() >= getWidth()) {
moveRight = false;
}
} else {
if (moveButton.getLocation().x <= 0) {
moveRight = true;
}
}
int x = moveButton.getLocation().x + (moveRight ? DELTA_X : -DELTA_X);
int y = moveButton.getLocation().y;
moveButton.setLocation(new Point(x, y));
repaint();
}
}
private static void createAndShowGui() {
MyMainPanel mainPanel = new MyMainPanel();
JFrame frame = new JFrame("GUI Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Just added some methods and fixed some lines in your code, you can try it now:
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class mainPanel implements ActionListener {
JFrame frame1;
JPanel panel1;
JButton button = new JButton("START");
boolean buttonPressed = false;
boolean move = false;
Timer timer;
int direction = 1;
int x = 0;
public static void main(String[] args) {
new mainPanel().loadGUI();
}
public void loadGUI() {
frame1 = new JFrame("Moving button");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setVisible(true);
frame1.setSize(300, 58);
frame1.setResizable(false);
panel1 = new JPanel(); // Xwidth=294, Yheight=272
panel1.setSize(300, 30);
panel1.setLayout(null);
frame1.add(panel1);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame1.setLocation(dim.width / 2 - frame1.getSize().width / 2, dim.height / 2 - frame1.getSize().height / 2);
button.setSize(80, 30); //X, Y
panel1.add(button);
button.addActionListener(this);
moveButton();
}
public void moveButton() {
if (timer == null) {
initTimer();
}
move = !move;
if (move) {
button.setText("STOP");
direction = (direction == 0) ? 1 : 0;
timer.start();
} else {
button.setText("MOVE");
timer.stop();
}
}
public void initTimer() {
timer = new Timer(10, (e) -> {
switch (direction) {
case 0: // right
x++;
if (x >= panel1.getWidth() - button.getWidth()) {
direction = 1;
}
break;
case 1: // left
x--;
if (x <= 0) {
direction = 0;
}
break;
}
button.setLocation(x, 0);
panel1.repaint();
});
}
#Override
public void actionPerformed(ActionEvent e) {
moveButton();
}
}
Is there a way to toggle a read-only mode so when you click any object in your window it simply returns what you clicked, ignoring the object's usual event handling? IE, while in this "read-only" mode, if you click on a Button, it simply returns the button, not actually pressing the button. Then I could do something like:
if ("thing pressed" == button) "do this";
else if ("thing pressed" == panel) "do that";
else "do nothing";
Here's my code, its a frame with 3 colored boxes. Clicking the 2nd box, the 3rd box, or the background will display a message. Clicking box 1 does nothing. I like using new mouse adapters so I want to do it this way.
Now what I want is when you click box 1, box 1 is treated as selected (if that helps you get the picture). Then if you click anywhere, including box 1 again, box 1 is deselected and nothing else (meaning that box 2, box 3. or the background's message will display). At that time, only if box 2 or 3 were clicked, they will still not display their normal message but a different message would be displayed.
I'm very sorry if I come off a little short.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Labels {
public static void main(String[] args) {
new Labels();
}
Square l1, l2, l3;
public Labels() {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
l1 = new Square();
l2 = new Square();
l3 = new Square();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(120, 150);
frame.setResizable(false);
panel.setVisible(true);
panel.setLayout(null);
l1.setLocation(5, 5);
l2.setLocation(5, 60);
l3.setLocation(60, 5);
l2.setColor("yellow");
l3.setColor("black");
l1.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
//do nothing
}
});
l2.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 2");
}
});
l3.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pushed label 3");
}
});
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("pushed background");
}
});
frame.add(panel);
panel.add(l1);
panel.add(l2);
panel.add(l3);
}
class Square extends JLabel{
Color color = Color.blue;
public Square() {
// TODO Auto-generated constructor stub\
setVisible(true);
setSize(50,50);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(color);
g.fillRect(0, 0, 50, 50);
}
public void setColor(String color){
if (color == "white") this.color = Color.white;
else if (color == "black") this.color = Color.black;
else if (color == "yellow") this.color = Color.yellow;
else {
System.out.println("Invalid color");
return;
}
repaint();
}
}
}
Don't disable anything. Simply change the state of your class, perhaps by using a few boolean flag variables/fields and change these flags depending on what is pressed.
So have boolean fields called label1PressedLast, label2PressedLast, and label3PressedLast or something similar, and when a label is pressed, check the states of all other flags and have your program's behavior change depending on the state of these flags and the label that was just pressed. Then set all flags to false except for the one corresponding to the label that was just pressed.
For example, this little program reacts only if the first and then the third JLabel have been pressed:
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;
public class FlagEg extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean[] flags = new boolean[LABEL_COUNT];
public FlagEg() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
flags[i] = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
// check if first label and then third pressed:
if (flags[0] && index == 2) {
System.out.println("first and then third label pressed!");
}
// reset all labels and flags to initial state
inactivateAll();
// set pressed label background color and set flag of label just pressed
labels[index].setBackground(Color.pink);
flags[index] = true;
}
private static void createAndShowGui() {
FlagEg mainPanel = new FlagEg();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Logic iteration two: only label 1 is the "primer" JLabel. This is actually easier to implement, because now you only need one boolean flag, that representing label 1 being pressed:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class FlagEg2 extends JPanel {
private static final int LABEL_COUNT = 3;
private JLabel[] labels = new JLabel[LABEL_COUNT];
private boolean label1Flag = false;
public FlagEg2() {
setLayout(new GridLayout(1, 0, 20, 0));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// panel mouse listener
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent arg0) {
inactivateAll();
}
});
MouseListener labelsMouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvt) {
myMousePressed(mouseEvt);
}
};
// create JLabels and add MouseListener
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel("Label " + (i + 1));
labels[i].addMouseListener(labelsMouseListener);
labels[i].setOpaque(true);
labels[i].setBorder(BorderFactory.createLineBorder(Color.black));
add(labels[i]);
}
}
private void inactivateAll() {
for (int i = 0; i < labels.length; i++) {
labels[i].setBackground(null);
label1Flag = false;
}
}
private void myMousePressed(MouseEvent mouseEvt) {
JLabel label = (JLabel) mouseEvt.getSource();
// which label was pressed?
int index = -1;
for (int i = 0; i < labels.length; i++) {
if (label == labels[i]) {
index = i;
}
}
if (label1Flag) {
if (index == 1) {
System.out.println("Label 1 and label 2 pressed");
} else if (index == 2) {
System.out.println("Label 1 and label 3 pressed");
}
}
// reset all labels and flags to initial state
inactivateAll();
// if label1, then activate it
if (index == 0) {
labels[0].setBackground(Color.pink);
label1Flag = true;
}
}
private static void createAndShowGui() {
FlagEg2 mainPanel = new FlagEg2();
JFrame frame = new JFrame("Flag Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
package javaapplication6;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
*
* #author Jan Vorcak <vorcak#mail.muni.cz>
*/
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
MouseListener listener = new MouseAdapter() {
private int count = 0;
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JLabel) {
count++;
if (count >= 2) {
System.out.println("clicked 2 times on labels");
count = 0;
}
} else {
count = 0;
}
}
};
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
l1.addMouseListener(listener);
l2.addMouseListener(listener);
l3.addMouseListener(listener);
frame.addMouseListener(listener); // or panel.addMouseListener(listener);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
You could want to create a listener that do the job for using the putClientProperty method of JComponent.
public class JComponentClickCountListener extends MouseAdapter {
private final Integer ONE = 1;
#Override
public void mouseClicked(MouseEvent e) {
if (e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object property = jComponent.getClientProperty(JComponentClickCountListener.class);
if (property instanceof Number) {
property = ONE + ((Number) property).intValue();
}
else {
property = ONE;
}
jComponent.putClientProperty(JComponentClickCountListener.class, property);
}
}
}
Then in your code you can decide to have a single instace of that class for all of your components or create a new one each time.
This could give you the advantage of using the propertyChangeListener for future actions.
PS.
The code example do not represent all logic for OP question but i could by used as solid base. Later on i will try to update it. To cover that.
EDIT2:
I think that you should separate the logic, of selection and action over selected items. Then the task is divided into two tasks. First is the possibility to store the information about it state, clicked active, clicked again inactive. The second tasks it to operate on that status when a jComponent status was changed.
This is an simple example that i wrote, the functionality is to highlight the background of labels when the are selected and remove it when it was clicked again or the panel was clicked remove all selections.
This example is divided to three elements Enum, Iterface and class that manage the logic of selection
Enum - we store the possible statuses and a property key.
public enum JComponentActivationStatus {
NONE,
ACTIVE,
INACTIVE;
public static final String PROPERTY_KEY = JComponentActivationStatus.class.getCanonicalName();
}
Interface - provide a delegate for action to be taken when jcomponenet status change.
public abstract interface JComponenetActivationStatusChangeAction<T extends JComponent> {
public abstract void onActivation(T object);
public abstract void onDeactivation(T object);
}
Class - This class mange the status logic of jcomponents.
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
public class JComponenetActivationManager {
public static <T extends JComponent> T addMouseStatusControl(T jComponent) {
jComponent.addMouseListener(new JComponentMouseStatusModyfier());
return jComponent;
}
public static <T extends JComponent> T addActivationStatusChangeAction(T jComponenet, JComponenetActivationStatusChangeAction<T> statusChangeAction) {
jComponenet.addPropertyChangeListener(craeteJCompositeActivationStatusChangeListener(statusChangeAction));
return jComponenet;
}
public static <T extends JComponent> PropertyChangeListener craeteJCompositeActivationStatusChangeListener(JComponenetActivationStatusChangeAction<T> action) {
return new JComponentStatusPropertyChangeListener<T>(action);
}
/**
* Class that set the status for the JComponet after doubClicl
*/
private final static class JComponentMouseStatusModyfier extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
if(e.getComponent() instanceof JComponent) {
JComponent jComponent = (JComponent) e.getComponent();
Object propertyValue = jComponent.getClientProperty(JComponentActivationStatus.PROPERTY_KEY);
if(JComponentActivationStatus.ACTIVE.equals(propertyValue)) { //We check that the ACTIVE status is already selected, if so we inactive.
propertyValue = JComponentActivationStatus.INACTIVE; //If so we inactive it.
} else {
propertyValue = JComponentActivationStatus.ACTIVE; // Otherwise we set it as active
}
jComponent.putClientProperty(JComponentActivationStatus.PROPERTY_KEY, propertyValue); // We use the property key form status
}
}
}
/**
* Help class that fire the actions after status is changed
*/
private static final class JComponentStatusPropertyChangeListener<T extends JComponent> implements PropertyChangeListener {
private final JComponenetActivationStatusChangeAction<T> statusChangeAction;
/**
*
*/
public JComponentStatusPropertyChangeListener(JComponenetActivationStatusChangeAction<T> statusChangeAction) {
if(statusChangeAction == null) {
throw new IllegalArgumentException("action can not be null at this point");
}
this.statusChangeAction = statusChangeAction;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if(JComponentActivationStatus.PROPERTY_KEY.equals(evt.getPropertyName())) {
if(JComponentActivationStatus.ACTIVE.equals(evt.getNewValue())) {
statusChangeAction.onActivation((T) evt.getSource());
}
if(JComponentActivationStatus.INACTIVE.equals(evt.getNewValue())){
statusChangeAction.onDeactivation((T) evt.getSource());
}
}
}
}
}
That class contain two public static method, that allow the developer to add the functionality to mange status to any jComponent object, add subscribe the action handler if any change occur.
At the end we have the main method that test our solution
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel l1 = new JLabel("Label 1");
JLabel l2 = new JLabel("Label 2");
JLabel l3 = new JLabel("Label 3");
panel.setBackground(Color.CYAN);
addMouseStatusControl(panel);
addMouseStatusControl(l1);
addMouseStatusControl(l2);
addMouseStatusControl(l3);
JComponenetActivationStatusChangeAction<JLabel> activeBackground = new JComponenetActivationStatusChangeAction<JLabel>() {
#Override
public void onActivation(JLabel object) {
object.setOpaque(true);
object.setBackground(Color.YELLOW);
}
#Override
public void onDeactivation(JLabel object) {
object.setOpaque(false);
object.setBackground(object.getParent().getBackground());
}
};
JComponenetActivationStatusChangeAction<JPanel> deactivateChildrens = new JComponenetActivationStatusChangeAction<JPanel>() {
#Override
public void onDeactivation(JPanel object) {
}
#Override
public void onActivation(JPanel object) {
for(Component component : object.getComponents()) {
if(component instanceof JComponent) {
((JComponent) component).putClientProperty(JComponentActivationStatus.PROPERTY_KEY,JComponentActivationStatus.INACTIVE);
}
}
}
};
addActivationStatusChangeAction(l1, activeBackground);
addActivationStatusChangeAction(l2, activeBackground);
addActivationStatusChangeAction(l3, activeBackground);
addActivationStatusChangeAction(panel, deactivateChildrens);
panel.add(l1);
panel.add(l2);
panel.add(l3);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
The solution is very flexible and extendable in case you will need to add more labels.
The example is for those that want to learn. Any comment would be appreciate.