Lets say you have a GridLayout of JButtons in an NxN grid, in code such as this:
JPanel bPanel = new JPanel();
bPanel.setLayout(new GridLayout(N, N, 10, 10));
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++)
{
JButton b = new JButton("(" + row + ", " + col + ")");
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
}
});
bPanel.add(b);
}
}
How would one access each button individually in the grid to change the button's name through setText()? This needs to be done outside of actually pressing the button in question.
Because each button in instantiated locally as "b", a globally accessible name for each button is not possible at current. What could be done to access each button independently? Could an array like JButton[][] hold references to all the buttons? How can this be set up in the code above?
Any input is appreciated.
Thanks.
you can,
1) putClientProperty
buttons[i][j].putClientProperty("column", i);
buttons[i][j].putClientProperty("row", j);
buttons[i][j].addActionListener(new MyActionListener());
and
public class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
System.out.println("clicked column " + btn.getClientProperty("column")
+ ", row " + btn.getClientProperty("row"));
}
2) ActionCommand
You can create an array (or list or something else) to store all the buttons. Or you can use public Component[] getComponents() method of the bPanel (Container).
Related
I want to make ToDoList App. After successfully adding task to do (which contains checkbox, JLabel and date, all putted in a box) i want to remove them dynamically. With adding it's not problem but when i try to remove (ater clicking checked in checkbox) it works only once. Then it either removes not once which are intended or not removing them at all. I am not sure why it's not working so I paste all code below.
JSpinner dateSpin;
Box eventBox, boxBox;
Box[] taskBox = new Box[1000];
JTextField eventName;
Date date;
Checkbox[] doneCheck = new Checkbox[1000];
JLabel taskLabel;
JPanel panel;
JScrollPane scrollPane;
SimpleDateFormat simpleDate;
int i = 0;
public static void main(String[] args) {
new Main();
}
private Main(){
this.setSize(400, 600);
this.setTitle("To-Do List");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setLocationRelativeTo(null);
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
boxBox = Box.createVerticalBox();
scrollPane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
eventBox = Box.createHorizontalBox();
eventBox.setBorder(BorderFactory.createEtchedBorder());
JLabel plusSign = new JLabel("+");
plusSign.setFont(new Font("Serafi", PLAIN, 20));
plusSign.setMaximumSize(new Dimension(Integer.MAX_VALUE, plusSign.getMinimumSize().height));
eventBox.add(plusSign);
eventName = new JTextField(20);
eventName.setFont(new Font("Times", Font.ITALIC, 15));
eventName.setMaximumSize(new Dimension(Integer.MAX_VALUE, eventName.getMinimumSize().height));
eventName.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == eventName){
/* to do: saving every task in some file, figure out how to remove
those tasks (checkbox + jlabel) -> whole box from screen or how to send them to "done"
also "done" to do*/
simpleDate = new SimpleDateFormat("E-dd-MM-yyyy");
taskBox[i] = Box.createHorizontalBox();
taskBox[i].setBorder(BorderFactory.createTitledBorder(simpleDate.format(date)));
doneCheck[i] = new Checkbox();
doneCheck[i].addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
int k = 0;
for (int j = 0; j < doneCheck.length; j++) {
if(doneCheck[j].getState()){
//remove(doneCheck[k]);
//System.out.println("?" + i + "?" + k + " " + e.getSource().toString());
System.out.println("xxxxx" + doneCheck[j].getState());
break;
}
System.out.println("oooooo");
k++;
}
System.out.println(doneCheck.length + taskBox[k].toString());
//System.out.println("! " + k + " " + e.getSource().toString());
boxBox.remove(taskBox[k]);
//boxBox.removeAll();
boxBox.revalidate();
boxBox.repaint();
}
});
taskBox[i].add(doneCheck[i]);
String taskName = eventName.getText();
taskLabel = new JLabel(taskName);
taskLabel.setMinimumSize(new Dimension(500,10));
taskLabel.setPreferredSize(new Dimension(300, 10));
taskBox[i].add(taskLabel);
boxBox.add(taskBox[i]);
boxBox.setMaximumSize(new Dimension(Integer.MAX_VALUE, boxBox.getMinimumSize().height + 11));
panel.add(boxBox);
panel.revalidate();
panel.repaint();
i++;
}
}
});
eventBox.add(eventName);
date = new Date();
dateSpin = new JSpinner(new SpinnerDateModel(date, null, null, Calendar.DAY_OF_MONTH));
JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(dateSpin, "dd/MM/yy");
dateSpin.setEditor(dateEditor);
dateSpin.setMaximumSize(new Dimension(Integer.MAX_VALUE, dateSpin.getMinimumSize().height));
dateSpin.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if(e.getSource() == dateSpin){
date = (Date) dateSpin.getValue();
}
}
});
eventBox.add(dateSpin);
panel.add(eventBox, new FlowLayout());
this.add(scrollPane);
this.setVisible(true);
}
You never remove elements from the taskBox and doneCheck arrays.
Now if you mark the first entry as done, your ItemListener will always find this first entry when looping over the doneCheck array.
Marking the entries as done in reverse order (always the last shown entry) will remove one entry after the other.
As to your software design: it's considered bad practice to manage your data in several parallel arrays.
Please consider creating a custom class for the todo items that manages all the elements of a single todo item.
Unless you are initializing doneCheck items somewhere, this:
Checkbox[] doneCheck = new Checkbox[1000];
And this:
int k = 0;
for (int j = 0; j < doneCheck.length; j++) {
if (doneCheck[j].getState()) {
--------^^^^^^^^^^^^
Is probably one the reason it fails: you probably got a NullPointerException somewhere, eg: when value of j > 0. The NPE will probably be catched by the EventDispatchThread which may or may not be kind enough to show it on stderr...
I fail to see why you are using this array, and you can shorten your code and avoid NPE like this:
Checkbox cb = new Checkbox();
cb.addItemListener(event -> {
if (cb.getState()) { // not null!
boxBox.remove(cb);
boxBox.revalidate();
boxBox.repaint();
}
});
doneCheck[i] = cb; // I still don't know why you need that.
My guess is that you have 2 variables global int i = 0 and local int k = 0 in here
public void itemStateChanged(ItemEvent e) {
// int k = 0;//<-------- LOCAL
for (int j = 0; j < doneCheck.length; j++) {
if(doneCheck[j].getState()){
//Either k = j;
boxBox.remove(taskBox[j]);
//remove(doneCheck[k]);
//System.out.println("?" + i + "?" + k + " " + e.getSource().toString());
System.out.println("xxxxx" + doneCheck[j].getState());
break;
}
System.out.println("oooooo");
//k++;//<-- ALWAYS == last j value before the break;
}
System.out.println(doneCheck.length + taskBox[k].toString());
//System.out.println("! " + k + " " + e.getSource().toString());
//boxBox.remove(taskBox[k]);//
//boxBox.removeAll();
boxBox.revalidate();
boxBox.repaint();
}
Every time you call for itemStateChanged int k = 0; will be initialized to 0 and you will be removing element[j] from array of taskBox. As you k++ statement will be equal to the last j value before the break; because it sits after the if(doneCheck[j].getState()){...
Try moving boxBox.remove(taskBox[j]); inside the for loop and using j instead of k.
I am currently coding a game and part of it consist of having different tiles to be put in a board. I plan on simulating this by having different buttons that will be used to represent the tiles with their corresponding coordinates. For example, one button will say "A1", "A2", etc. What I would like to accomplish, is to have the user click on the "A1" tile and then the button on the board that represents "A1" will change colors, is there any way to go through the buttons on the board and compare its text to the selection of the user? The following is what I used to create the board:
JButton[][] buttons = new JButton[9][12];
JPanel panel = new JPanel(new GridLayout(9,12,5,5));
panel.setBounds(10, 11, 800, 600);
frame.getContentPane().add(panel);
//board
for (int r = 0; r < 9; r++)
{
for (int c = 0; c < 12; c++)
{
buttons[r][c] = new JButton("" + (c + 1) + numberList[r]);
buttons[r][c].setBackground(Color.WHITE);
panel.add(buttons[r][c]);
}
}
This is what I wrote on the code of one of the tiles
JButton tile1 = new JButton ("A1");
tile1.setBounds(60,725,60,60);
frame.getContentPane().add(tile1);
tile1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String buttonText = tile1.getText();
// iterating through all buttons:
for(int i=0;i<buttons.length;i++){
for(int j=0;j<buttons[0].length;j++)
{
JButton b = buttons[i][j];
String bText = b.getText();
if(buttonText.equals(bText))
{
[i][j].setBackground(Color.BLACK);
}
}
}
}
} );
However, it is given me an error saying that there is an action expected after "{"
You may add an action listener to each of the JButton you are creating in the loop like below:
buttons[r][c].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// your code here
}
} );
Placing the listener in your code may look like
JButton[][] buttons = new JButton[9][12];
JPanel panel = new JPanel(new GridLayout(9,12,5,5));
panel.setBounds(10, 11, 800, 600);
frame.getContentPane().add(panel);
//board
for (int r = 0; r < 9; r++)
{
for (int c = 0; c < 12; c++)
{
buttons[r][c] = new JButton("" + (c + 1) + numberList[r]);
buttons[r][c].setBackground(Color.WHITE);
buttons[r][c].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<buttons.length;i++){
for(int j=0;j<buttons[0].length;j++){
JButton b = buttons[i][j];
String bText = b.getText();
if(buttonText.equals(bText)){
// you found a match here
// and you have the positions i, j
//
}
}
}
}
} );
panel.add(buttons[r][c]);
}
}
And you could store the the colors to be changed to in global static array, and use that array in your action listener.
For information on adding listener to the JButton, you may refer this thread How do you add an ActionListener onto a JButton in Java
Hope this helps!
You need listeners.
Implement ActionListener to your class. This will require you to add public void actionPerformed(ActionEvent e) {} to your class.
Every JButton you use should have an action listener.
Apply one like that:
JButton but = new JButton();
but.addActionListener(this);
Finally, in the actionPerformed method we added, you need to add something like that:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == but)
but.setBackground(Color.BLACK);
}
P.S. you can get a button's text value by the means of:
but.getText();
Ok so I am trying to make a chess game in swing. I have a program that creates a 2d array of JButton's 8x8. I then create them all in a loop doing stuff like going back and forth between white/black and adding an action event. The problem i am having is that each button has the same action event and it is the event that is created last I.E. button on Row 8 column H is the action listener for all of the buttons in the array. Here is a snippet of code that is where I am creating the buttons and adding them.
I also have an Enum Columns that just goes from int to character 1 to H for example. selectPosition and targetPosition are objects that have two members columns and rows.
public void initializeGui(boolean isWhite) {
boolean shouldBeWhite = true;
for(int i = 0; i< 8; i++){
for(int j = 0; j < 8; j++){
column = i+1;
row = j+1;
JButton square = new JButton();
square.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
final int thisRow = row;
final int thisColumn = column;
selectPosition.setColumn(Columns.getColumnsFromInt(thisColumn));
selectPosition.setRow(thisRow);
if(isSelecting){
System.out.print("Selecting square to move. Row: " + thisRow + " Column: " + Columns.getColumnsFromInt(thisColumn));
selectPosition.setColumn(Columns.getColumnsFromInt(thisColumn));
selectPosition.setRow(thisRow);
} else{
System.out.print("Targeting square to move to. Row: " + thisRow + " Column: " + Columns.getColumnsFromInt(thisColumn) + "\n");
targetPosition.setColumn(Columns.getColumnsFromInt(thisColumn));
targetPosition.setRow(thisRow);
}
System.out.println("");
isSelecting = !isSelecting;
}
});
if(shouldBeWhite){
square.setBackground(Color.WHITE);
shouldBeWhite = false;
}else{
square.setBackground(Color.BLACK);
shouldBeWhite = true;
}
if (j == 7){
shouldBeWhite = !shouldBeWhite;
}
chessBoardSquares[i][j] = square;
gui.add(chessBoardSquares[i][j]);
}
}
if(isWhite){
setInitialPiecesWhiteStart();
}else{
setInitialPiecesBlackStart();
}
}
Further up as a member of this class are the following:
int column = 0, row = 0;
When I click on any of these buttons i see printed
Selecting square to move. Row: 8 Column: H
Targeting square to move to. Row: 8 Column: H
Selecting square to move. Row: 8 Column: H
Targeting square to move to. Row: 8 Column: H
and so on. My question is why are these buttons all given the same action event? My logic walk through would be something like create the first button set column = i+1 and row = j+1 then add an action listener with an action event that sets the current row/column values to the inner final variables and then prints out the thisRow and thisColumn associated with that action event. Am i overriding the values at the end or do i have the scope wrong? Basically how am i creating these buttons actions listeners incorrectly?
You could...
Use the actionCommand API to pass information between the button and the ActionListener...
JButton btn = new JButton();
btn.setActionCommand(row + "x" + column);
btn.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
//...
}
});
The problem here is you're relying on String parsing to extract the values, which can get messy quickly
You could...
Create a custom ActionListener which takes the values you want to use...
public class SquareActionListener implements ActionListener {
private int column;
private int row;
public SquareActionListener(int row, int column) {
this.row = row;
this.column = column;
}
#Override
public void actionPerformed(ActionEvent e) {
//...
}
}
This de-couples the ActionListener from the rest of the code and provides you the information you need, although, you may need to pass additional information (such as the model) as well for it to work
You could...
Make use of the Action API which is designed to be provide self contained units of work, it's generally a more re-usable solution, but might be a little beyond what you need right now
public class SquareAction extends AbstractAction {
private int column;
private int row;
public SquareAction(int row, int column) {
this.row = row;
this.column = column;
}
#Override
public void actionPerformed(ActionEvent e) {
//...
}
}
This looks alot like the last suggestion, but instead of adding it as the button's ActionListener, you actually apply it to the button directly...
JButton btn = new JButton(new SquareAction(row, column));
The button then uses other properties (which I've not set) to set itself up
I had the same issue when making a tic-tac-toe game. I used each button's hashcode to trace back which button was actually pushed. This is what my button setup looked like:
hashcodes= new ArrayList<Integer>();
for (int i=1;i<=9;i++) {
JButton button = new JButton();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setHash(button.hashCode());
testWinner();
testDraw();
}
});
hashcodes.add(button.hashCode());
panel.add(button);
}
}
private void setHash(int hashcode) {
for (int h:hashcodes) {
if (h==hashcode) {
//do stuff
}
}
}
This is my Test class, and it works perfectly.
public class Test extends javax.swing.JFrame {
private javax.swing.JButton[][] buttons;
private final int ROW = 8;
private final int COLUMN = 8;
public Test() {
initComponents();
}
private void initComponents() {
this.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setExtendedState(javax.swing.JFrame.MAXIMIZED_BOTH);
this.buttons = new javax.swing.JButton[ROW][COLUMN];
this.setLayout(new java.awt.GridLayout(ROW, COLUMN));
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
final int row = i;
final int column = j;
buttons[i][j] = new javax.swing.JButton(
String.format("Button %d-%d", i, j));
buttons[i][j].addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
// System.out.println(
// String.format("You have just pressed the button at row %d and column %d", row, column));
javax.swing.JOptionPane.showMessageDialog(
Test.this, String.format("You have just pressed the button at row %d and column %d", row, column));
}
});
this.add(buttons[i][j]);
}
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new Test().setVisible(true);
}
}
Hi I have to do a lottery with numbers when I first choice in a panel when I press Start it should serve me a panel with figures given them passed to the constructor panel but while I normally displays the panel the numbers in the JTextField does not apper.
The code when I press the button to make a panel with numbers at the function that returns to 2nd table numbers I put here a system.out and she shows them normally underneath its constructor passing the table as a parameter.
if (source == start) {
try {
int numbers[][] = fucts.takeBulletin();
RMIClient r=new RMIClient( numbers,1);
}catch() {
}
}
heare is the constructor code
public RMIClient(int [][]numbs,int a) {
super("!!!!!!!!!!!!!!!BINGO!!!!!!!!!!!!!!!!!!");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
Container pane = getContentPane();
GridLayout layout=new GridLayout(7 , 7);
pane.setLayout(layout);
pane.add(numB);
pane.add(numI);
pane.add(numN);
pane.add(numG);
pane.add(numO);
pane.add(num13);
for (int row = 0; row < numbs.length; row++) {
for (int col = 0; col < numbs[row].length; col++) {
pane.add(new JTextField(""+numbs[row][col])); <----- is nedd " "+
}
}
setContentPane(pane);
pack();
}
I can not understand why they do not pass the values from the table in textfield , is show me empty textfields
If it is a JApplet, try to move the whole content of the constructor into the init() method.
The constructor could save the int[][] numbs to an instance variable, and the init() method will see it.
int[][] numbs;
public RMIClient(int[][] numbs, int a) {
this.numbs = numbs;
}
public void init() {
...
}
I have created 9 JLabels by array. And it has common Event Listener with method of mouseClicked(MouseEvent src){... }, here i am finding problem is, how can I identify which JLabel is clicked?
Say, if label[0] is clicked then I want to show "Label-0 is clicked",
if label[1] is clicked then I want to show "Label-1 is clicked"
Can I perform this? if yes then How?
NOTE :- I found some answer stating that add Custom 'id' Property, I would but first, I prefer if there is any default method exist.
Add Label
JPanel pnl = new JPanel(new FlowLayout());
dd.add(pnl);
addlistener();
for (int i = 0; i < 10; i++) {
pnl.add(lbl[i] = new JLabel("" + i));
lbl[i].addMouseListener(listern);
}
Listener
public void mouseEnter(MouseEvent me) {
System.err.println("Hi");
me.getComponent();
if(me.getSource() instanceof JLabel){
System.out.println("lable"+ ((JLabel)me.getSource()).getText());
}
}
You could loop the array comparing the source of the event to each element in the array...
for (int index = 0; index < myLabelArray.length; index++) {
if (myLabelArray[index].equals(src.getSource())) {
System.out.println("Label-" + index + " was clicked");
break;
}
}
Or you could "name" each label...
JLabel[] myLabelArray = new JLabel[9];
for (int index = 0; index < 9; index++) {
JLabel label = new JLabel("...");
label.setName(Integer.toString(index));
label.addMouseListener(commonMouseListener);
myLabelArray[index] = label;
}
Then in your mouse listener...
public void mouseClicked(MouseEvent evt) {
System.out.println("Label-" + ((JLabel)evt.getSource()).getName() + " was clicked");
}
Or you could use a Map instead of an array or a List...