I've created a an array of JButtons that have a random color assigned to them when created as opposed to manually creating each button and assigning random colors to them. I'm now at a point where I want to use change the color, at random, of whichever button is clicked. I'd like to do it the same way as I've created and added the buttons so far(through the use of loops).
Though doing it the way I thought would work has failed. I'm given "local variable is accessed from within inner class; needs to be declared final". I'm that if I use final it can't be changed and now I'm at a loss.
Is there a possible workaround?
package test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.EventHandler;
import java.lang.String;
import java.util.Random;
public class TEST
{
/**
* #param args the command line arguments
*/
public static Random rand = new Random();
public static int oh;
public void btnPress(ActionEvent e, JButton[] jButts, float r, float g, float b) {
for (int y = 0; y < jButts.length; y++) {
if (e.getSource() == jButts[y]) {
jButts[y].setBackground(Color.getHSBColor(r, g, b));
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Suhp, Brah?");
frame.setLayout(new BorderLayout());
frame.setVisible(true);
frame.setBackground(Color.magenta);
frame.setSize(400, 400);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(4, 4));
String[] numbs = {"0", "1", "2", "3", "4", "5", "6", "7"};
final JButton[] jButts = new JButton[numbs.length];//0-7
for (int i = 0; i < 8; i++) {
jButts[i] = new JButton(numbs[i].toString());
//String leString = rand.nextInt(255).toString;
jButts[i].setBackground(Color.getHSBColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
}
for (int x = 0; x < 8; x++) {
frame.add(jButts[x]);
}
//ActionListener
for (int i =0; i < 8; i++) {
jButts[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
i++;
jButts[i].setBackground(Color.getHSBColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
}
});
}
}
}
There's no need to use i in the ActionListener. You can get the button using ActionEvent#getSource:
jButts[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
button.setBackground(Color.getHSBColor(rand.nextFloat(),
rand.nextFloat(), rand.nextFloat()));
}
});
here's a workaround,
//ActionListener
for (int i =0; i < 8; i++)
{
final int temp = i; // assign to temporary variable
jButts[temp].addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// i++; Not sure what you're trying to do here..
jButts[temp].setBackground(Color.getHSBColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
}
});
}
but I highly recommend rethinking your approach.
// ActionListener
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
button.setBackground(Color.getHSBColor(rand.nextFloat(),
rand.nextFloat(), rand.nextFloat()));
}
};
for (int i = 0; i < 8; i++)
jButts[i].addActionListener(listener);
Related
I have a matrix of colorful buttons and I need to compare their colors and their values after clicking them. All my code does is removing every button without searching their values or colors.Also i need to add the jlabel without rearranging jbuttons. How can i fix the problem?
public class Legos2 extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
public JPanel jp = (JPanel)this.getContentPane();
public JButton[][] jb = new JButton[12][24];//my matrix
public static JLabel jl = new JLabel("score",SwingConstants.CENTER);//a jlabel to count the scores every time two buttons are removed
public Legos2() {
super();
this.setSize(2000,2000);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
jp.setLayout(new GridLayout(12, 24));//size of my jpanel
Random rnd = new Random();
Color[] c = {Color.lightGray, Color.gray, Color.white, Color.cyan};//colored jbuttons
final int MAXCOLOR = c.length;
JButton jb1;
for(int i = 0;i <jb.length;i++) {
for(int j = 0; j<jb[i].length; j++) {
int k = (int) (Math.random()*9+1);//k is a random integer from 1 to 9
jb1 = new JButton(Integer.toString(k));
add(jb1);
jb[i][j]=jb1;
jb[i][j].addActionListener(this);
}
}
for(int i=0; i<jb.length; i++){
for(int j = 0; j<jb[i].length; j++) {
jb[i][j].setBackground(c[rnd.nextInt(MAXCOLOR)]);//i add colors in here
}
}
for (int row = 0; row < jb.length; row++) {
for (int column = 0; column < jb[row].length; column++) {
jb[row][column].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JButton button = (JButton) e.getSource();
String buttonText = button.getText();
// now iterate over all the jbuttons you have
for(int i=0;i<jb.length;i++){
for(int j=0;j<jb[0].length;j++){
JButton b = jb[i][j];
String bText = b.getText();
if(e.getSource()==b)
if(buttonText.equals(bText)){
b.setEnabled(false);
jl.setText(Integer.toString(i));
}
}
}
}
}
);
jp.add(jb[row][column]);
}
}
}
}
The following is mcve of the required functionality. Please review and ask for clarifications as needed:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class Legos2 extends JFrame{
private static final long serialVersionUID = 1L;
private static final int ROWS = 12, COLS =24;
public JButton[][] jb = new JButton[ROWS][COLS];
private int score = 0;
public static JLabel jl;
public Legos2() {
super();
this.setSize(2000,2000);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel jp= new JPanel();
jp.setLayout(new GridLayout(ROWS, COLS));
Random rnd = new Random();
Color[] c = {Color.lightGray, Color.gray, Color.white, Color.cyan};
final int MAXCOLOR = c.length;
//all can be done in one loop
for(int i = 0;i <jb.length;i++) {
for(int j = 0; j<jb[i].length; j++) {
int k = (int) (Math.random()*9+1);//k is a random integer from 1 to 9
JButton button = new JButton(Integer.toString(k));
button.setBackground(c[rnd.nextInt(MAXCOLOR)]);
button.addActionListener(e -> {
disableTwoMatchingButtons(button);
});
jb[i][j]=button;
jp.add(button);
}
}
add(jp); //add panel at content pane center
jl= new JLabel("",SwingConstants.CENTER);
updateScore(0);
add(jl, BorderLayout.PAGE_END); //add label at content pane bottom
pack();
setVisible(true);
}
private void disableTwoMatchingButtons(JButton button) {
for(int i=0;i<jb.length;i++){
for(int j=0;j<jb[0].length;j++){
JButton b = jb[i][j];
if(b.isEnabled() && button.getText().equals(b.getText()) &&
button.getBackground().equals(b.getBackground())){
b.setEnabled(false);
button.setEnabled(false);
updateScore(2* Integer.valueOf(b.getText()));
return;
}
}
}
}
private void updateScore(int value) {
score += value;
jl.setText("Score: "+ score );
}
public static void main(String[] args) {
new Legos2();
}
}
If you need to disable all matching button, and not just two, use :
private void disableAllMatchingButtons(JButton button) {
for(int i=0;i<jb.length;i++){
for(int j=0;j<jb[0].length;j++){
JButton b = jb[i][j];
if(b.isEnabled() && button.getText().equals(b.getText())
&& button.getBackground().equals(b.getBackground())){
b.setEnabled(false);
updateScore(Integer.valueOf(b.getText()));
}
}
}
}
(instead of disableTwoMatchingButtons)
For future post always consider posting an mcve, and stick to one question per post.
To my two questions:
1) The last added Button 9|9 is displayed to big instead of the coded height and width (100 and 100).
2) Further the ActionListener does not always register the cklicked button at the first try, but rather changes the state/color after the second click.
Any suggestions?
static JFrame gui;
public static void main(String[] args) {
int[] colors = new int[81];
for (int n=0; n<81; n++) {
colors[n] = 0;
}
setupGUI(colors);
}
private static void setupGUI(int[] colors) {
gui = new JFrame("9x9x9 LED-Cube GUI");
gui.setSize(905, 925);
gui.setResizable(false);
gui.setLayout(new BorderLayout());
for (int i=0; i<9; i++) {
for (int b=0; b<9; b++) {
int bn = b;
Button br = new Button((i+1) + "|" + (b+1));
br.setBounds(b*100, i*100, 100, 100);
gui.add(br);
br.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
changeButtonColor(br, colors, bn);
}
});
}
}
gui.setVisible(true);
}
public static void changeButtonColor(Button br, int[] colors, int bn) {
if(colors[bn] == 0) {
br.setBackground(Color.green);
colors[bn] += 1;
}
else {
br.setBackground(new Color(240, 240, 240));
colors[bn] -= 1;
}
System.out.println(colors[bn]); //for test purposes to display the state change
}
This is related to How to get X and Y index of element inside GridLayout? post and its answers.
For whatever reason none of them suggested to extend JButton to include its position in the grid and in associated array of buttons.
I have made the following illustration that simply displays button's coordinates when it's clicked.
Extended JButton:
package buttons_array;
import javax.swing.*;
#SuppressWarnings("serial")
public class ButtonWithCoordinates extends JButton {
int coordX;
int coordY;
public ButtonWithCoordinates(String buttonText, int coordX, int coordY) {
super(buttonText);
this.coordX = coordX;
this.coordY = coordY;
}
/**
* #return the coordX
*/
public int getCoordX() {
return coordX;
}
/**
* #return the coordY
*/
public int getCoordY() {
return coordY;
}
}
sample GUI:
package buttons_array;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ButtonsArray implements ActionListener {
private ButtonWithCoordinates buttons[][];
private int nRows;
private int nCols;
private JFrame frame;
private JPanel panel;
public ButtonsArray(int x, int y) {
if (x > 0 && y > 0) {
nRows = x;
nCols = y;
buttons = new ButtonWithCoordinates[nRows][nCols];
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
buttons[i][j] = new ButtonWithCoordinates(" ", i, j);
buttons[i][j].addActionListener(this);
}
}
} else {
throw new IllegalArgumentException("Illegal array dimensions!!!");
}
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ButtonWithCoordinates button = (ButtonWithCoordinates) e.getSource();
button.setText(button.getCoordX() + ", " + button.getCoordY());
}
public void GUI() {
if (buttons == null) { throw new NullPointerException("Array is not initialized!!!"); }
frame = new JFrame();
panel = new JPanel();
frame.setContentPane(panel);
panel.setLayout(new GridLayout(nRows, nCols));
for (int i=0; i < nRows; ++i) {
for (int j=0; j < nCols; ++j) {
panel.add(buttons[i][j]);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ButtonsArray(3, 5).GUI();
}
});
}
}
Now my questions:
Have I been reinventing the wheel here? I mean, is there a more straightforward way to achieve the same?
Is it in any way inferior to searching through the array each time we need to find the coordinates?
The original version of this example used extension:
GridButton extends JButton
The updated version was predicated on the colloquy seen here. While extension may be appropriate in some contexts, a few alternatives are mentioned here; a client property is particularly convenient. Identifying a button from its grid coordinates is also easy:
private static final int N = 5;
List<JButton> list = new ArrayList<>();
…
private JButton getGridButton(int r, int c) {
int index = r * N + c;
return list.get(index);
}
This is an example where you press a button and jList1 is refilled with items from a1 to a1000.:
//variable
private List<String> list = new ArrayList<>();
...
//main method
jList1.setModel(new DefaultListModel());
for(int i = 0; i < 1000; i++) {
list.add("a"+i);
}
...
//button action - jList1 refill
DefaultListModel dtm = (DefaultListModel)jList1.getModel();
dtm.removeAllElements();
for(String s : list) {
dtm.addElement(s);
}
If I fill the jList1, then select (with mouse) 0 index (first element in jList) and then press the button the program freezes while refilling the list. If I select any other element or do not select any item in the list at all then it fills just fine.
P.S. This example is done without any swing or EWT threads, because the main reason was found using them.
SSCCE:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package lt;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
/**
*
* #author Minutis
*/
public class Window {
public static void main(String[] args) {
final List<String> list = new ArrayList<>();
JFrame frame = new JFrame("BorderLayout Frame");
JPanel panel = new JPanel();
final JList jList1 = new JList();
JButton refill = new JButton("Refill");
jList1.setModel(new DefaultListModel());
for(int i = 0; i < 1000; i++) {
list.add("a"+i);
}
refill.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
DefaultListModel dtm = (DefaultListModel)jList1.getModel();
dtm.removeAllElements();
for(String s : list) {
dtm.addElement(s);
}
}
});
frame.add(panel);
panel.setLayout(new BorderLayout());
panel.add(jList1, BorderLayout.CENTER);
panel.add(refill, BorderLayout.SOUTH);
frame.setSize(300, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The problem is cause by removing all elements in the model when the first element is selected. The way it is implemented, when an index is selected and is removed, for some reason it generates a selectionChanged event. If another index is selected while another is deleted (even if it meant element shifting), it doesn't generate a selectionChanged event.
The removal is implemented in a way that it periodically removes the first element, while the selection index remains unchanged. So for the case of index 0 being selected, it generates 1000 selectionChanged events, which must be handled by the EDT and listeners. If you select another index, only 1 event is generated. That is significantly less overhead.
Before the repopulating while index 0 is selected, try manually setting the selection to index 1:
if(jList1.getSelectedIndex() == 0){
jList1.setSelectedIndex(1);
}
I think there is no need to worry if there is only one element in the list - it shouldn't do anything to set index greater that element count. But that may be implementation-specific.
For some reason, clearSelection() will cause selectionChanged events to be generated for repopulating an empty list.
logical & better could (just to avoiding mistakes from typos) be to add / remove / modify Items directly in XxxListModel,
for example
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Testing extends JFrame {
private static final long serialVersionUID = 1L;
private DefaultListModel listModel = new DefaultListModel();
private JList list = new JList(listModel);
private int currentSelectedRow = 0;
private int xX = 0;
public Testing() {
setLocation(400, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
for (int x = 0; x < 51; x++) {
listModel.addElement("" + x);
xX++;
}
JScrollPane sp = new JScrollPane(list);
final JViewport vp = sp.getViewport();
add(sp, BorderLayout.CENTER);
/*JButton btn = new JButton("Get Row:");
add(btn, BorderLayout.SOUTH);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try {
int goToRow = Integer.parseInt(JOptionPane.showInputDialog(getContentPane(), "Go To Row:"));
Rectangle r = list.getCellBounds(goToRow, goToRow);
if (goToRow > currentSelectedRow) {
r = list.getCellBounds(goToRow - 1, goToRow - 1);
int vph = vp.getExtentSize().height;
r.y += vph;
}
list.scrollRectToVisible(r);
list.setSelectedIndex(goToRow);
currentSelectedRow = goToRow;
} catch (Exception e) {
}
}
});*/
JButton btn1 = new JButton("Reset Model CastingModel");
add(btn1, BorderLayout.NORTH);
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
DefaultListModel model = (DefaultListModel) list.getModel();
model.removeAllElements();
for (int x = 0; x < 51; x++) {
model.addElement("" + (x + xX));
xX++;
}
//list.setModel(model);
}
});
JButton btn2 = new JButton("Reset Model directly from Model");
add(btn2, BorderLayout.SOUTH);
btn2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
listModel.removeAllElements();
for (int x = 0; x < 51; x++) {
listModel.addElement("" + (x + xX));
xX++;
}
}
});
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Testing().setVisible(true);
}
});
}
}
EDIT list.clearSelection(); hasn't any issue, everything moreover than 1k rows caused with desribed issue by OPs
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Testing extends JFrame {
private static final long serialVersionUID = 1L;
private DefaultListModel listModel = new DefaultListModel();
private JList list = new JList(listModel);
private int currentSelectedRow = 0;
private int xX = 0;
public Testing() {
setLocation(400, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
for (int x = 0; x < 999; x++) {
listModel.addElement("" + x);
xX++;
}
JScrollPane sp = new JScrollPane(list);
final JViewport vp = sp.getViewport();
add(sp, BorderLayout.CENTER);
JButton btn1 = new JButton("Reset Model CastingModel");
add(btn1, BorderLayout.NORTH);
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
list.clearSelection();
DefaultListModel model = (DefaultListModel) list.getModel();
model.removeAllElements();
for (int x = 0; x < 999; x++) {
model.addElement("" + (x + xX));
xX++;
}
//list.setModel(model);
}
});
JButton btn2 = new JButton("Reset Model directly from Model");
add(btn2, BorderLayout.SOUTH);
btn2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
list.clearSelection();
listModel.removeAllElements();
for (int x = 0; x < 999; x++) {
listModel.addElement("" + (x + xX));
xX++;
}
}
});
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Testing().setVisible(true);
}
});
}
}
I'm terribly unfamiliar with swing, but here I'm trying to make a "clacker" game where each time two dice are rolled, either you click the two (or 1 if they're the same number) buttons representing the two numbers or just one button representing the sum of the two numbers. If a button is clicked, visible is set to false. I have a JButton array to hold the 12 buttons, but every time a reference is made to the array buttons, it throws a NullPointerException. I've tried putting the code making the JButton array within the constructor and actionPerformed, but it still throws the same exception.
Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Clacker implements ActionListener
{
JPanel panel;
JFrame frame;
JLabel dieFace1, dieFace2, numTurns;
JButton[] buttons;
JButton rollDie, reset;
String turn="Turns: ";
int turns, numCovered;
public Clacker()
{
frame = new JFrame("clacker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel=new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(50,30,50,30));
panel.setBackground(Color.gray);
panel.setLayout(new GridLayout(5,10,1,1));
dieFace1=new JLabel(new ImageIcon("die3.jpg"));
dieFace1.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
dieFace1.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
panel.add(dieFace1);
dieFace2=new JLabel(new ImageIcon("die3.jpg"));
dieFace2.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
dieFace2.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
panel.add(dieFace2);
rollDie=new JButton("Roll Die");
rollDie.setAlignmentX(JButton.RIGHT_ALIGNMENT);
rollDie.addActionListener(this);
panel.add(rollDie);
numTurns=new JLabel(turn+turns);
numTurns.setAlignmentX(JLabel.LEFT_ALIGNMENT);
numTurns.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
panel.add(numTurns);
setArray();
reset=new JButton("Reset");
reset.setAlignmentX(JButton.LEFT_ALIGNMENT);
reset.addActionListener(this);
panel.add(reset);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
public void setArray()
{
JButton[] buttons = new JButton[12];
for (int i=0; i<12; i++)
{
buttons[i]= new JButton(i+1+"");
buttons[i].setActionCommand(i+"");
buttons[i].addActionListener(this);
panel.add(buttons[i]);
buttons[i].setEnabled(false);
System.out.println(buttons[i]);
}
}
public void actionPerformed(ActionEvent event)
{
if (event.getActionCommand().equals("Roll Die"))
{
Random asdf = new Random();
int newRoll= asdf.nextInt(6)+1;
dieFace1.setIcon(new ImageIcon("die"+newRoll+".jpg"));
Random asdf2 = new Random();
int newRoll2= asdf2.nextInt(6)+1;
dieFace2.setIcon(new ImageIcon("die"+newRoll2+".jpg"));
int sum=newRoll+newRoll2;
turns++;
numTurns.setText(turn+turns);
for (int i=0; i<7; i++) ////here
{
if (i==newRoll)
buttons[i].setEnabled(true);
if (i==newRoll2)
buttons[i].setEnabled(true);
}
for (int i=7; i<13; i++)
{
if (sum==i)
buttons[i].setEnabled(true);
}
String eventName = event.getActionCommand();
for (int b=1; b<13; b++)
{
if (eventName.equals(buttons[b].getActionCommand())) ////here
{
if (Integer.parseInt(eventName)>6)
{
buttons[b].setText("");
numCovered++;
buttons[b].setVisible(false);
for (int i=0; i<7; i++)
buttons[i].setEnabled(false);
}
}
}
for (int i=0; i<buttons.length; i++) ////here
buttons[i].setEnabled(false);
if (numCovered==12)
{
rollDie.setEnabled(false);
numTurns.setText("Win! "+turns+" turns");
}
}
else if (event.getActionCommand().equals("Reset"))
{
turns=0;
numTurns.setText(turn+turns);
rollDie.setEnabled(true);
numCovered=0;
for (int i=0; i<buttons.length; i++) ////here
{
buttons[i].setVisible(true);
buttons[i].setEnabled(false);
}
}
}
private static void runGUI()
{ JFrame.setDefaultLookAndFeelDecorated(true); Clacker game = new Clacker();}
public static void main(String[] args)
{ javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() { runGUI();}});}
}
In setArray you're declaring a local variable instead of referencing the class member variable. Just change
JButton[] buttons = new JButton[12];
to
this.buttons = new JButton[12];