Acces button from container of buttons JAVA - java

I want to make an interface for a Minesweeper game in java and i have this for now:
Container pane = getContentPane();
pane.setLayout(new GridLayout(10, 10));
for (int i = 1; i <= 100; i++)
{
JButton button = new JButton();
pane.add(button);
}
I don't want the JButtons to have any text at start, but when i click on a certain button i need to get that buttons row and column in the gridview, i will call a function that will give me a number of bombs near that point and i will put that number as the text from that button that was clicked, any help? i don't know how to get a specific button from a gridview so i can change it's propreties.

An easy way to do this is to create an ActionListener class of your own that gets a row and column passed into it when these buttons are created. When their action is fired, they can just pass that in to the function you're referring to. Something like this:
package com.sandbox;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Sandbox {
public static void main(String[] args) {
Container pane = //...
pane.setLayout(new GridLayout(10, 10));
for (int r = 0; r < 10; r++) {
for (int c = 0; c < 10; c++) {
JButton button = new JButton();
button.addActionListener(new PositionAwareActionListener(r, c));
pane.add(button);
}
}
}
private static class PositionAwareActionListener implements ActionListener {
private int r;
private int c;
public PositionAwareActionListener(int r, int c) {
this.r = r;
this.c = c;
}
#Override
public void actionPerformed(ActionEvent e) {
//callYourFunction(r, c);
}
}
}

Related

java swing paint transaction

I have a matrix of JToggleButton as components of a JPanel.
I want to call setSelected() for a group of those buttons
but I want the changes to be visually simultaneous.
How can I achieve this?
Consider the following code:
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
public class Main {
private static final JToggleButton buttons[][] = new JToggleButton[8][8];
public static void main(String args[]){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JPanel panel = new JPanel();
panel.setLayout(new java.awt.GridLayout(8, 8, 0, 0));
//Initialize buttons and add them to panel
for(int i = 0; i < 8; ++i){
for(int j = 0; j < 8; ++j){
buttons[i][j] = new JToggleButton(){
public JToggleButton setRowAndColumn(int row, int column){
addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent evt){
onClick(row, column);
}
});
return this;
}
}.setRowAndColumn(i, j);
buttons[i][j].setPreferredSize(new java.awt.Dimension(40, 40));
panel.add(buttons[i][j]);
}
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
});
}
//This method is called when the JToggleButton at [row][column] is clicked
private static void onClick(int row, int column){
//In this example all other buttons at the same row or column
//as the clicked one, are also selected
for(int i = 0; i < 8; ++i){
buttons[i][column].setSelected(true);
buttons[row][i].setSelected(true);
//I don't want to see visual changes yet
}
//All changes should be visualized now
}
}
Basically I want onClick to be executed as a transaction (visually).
Thanks in advance.

How to fill left over space regarding Layout Managers?

I recently have been developing a Swing application. I have run into a problem with Layout managers. I can't seem to figure out how to make components in the layout grow all the way to the edge of their parent container. Let me explain.
Say I have 8 buttons all in one row. Depending on the window size will determine if they take up all the space. GBL I have found centers so both space on left and right. BoxLayout usually space on the right side. This is probably due their anchors or alignment.
I think the problem is because the Layouts when all components have same settings it tries to give each component same space. So that little extra space can't be divided up equally to each component they leave it out.
I was wondering if there was a work around for this. Like the space is so small I was hoping there was a way to make last component eat it up or divide it best it can between the components.
Here is example code showing the problem. Note when you resize the panel you get extra space.
public class LeftoverExample {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
LeftoverExample.createGUI();
}
});
}
public static void createGUI(){
JFrame jF = new JFrame();
jF.setSize(new Dimension(1333,500));
jF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create ContentPane
JPanel contentPane = new JPanel();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
GridBagLayout gBL = new GridBagLayout();
gBL.columnWidths = new int[]{0};
gBL.rowHeights = new int[]{50, 50, 50 , 50};
contentPane.setLayout(gBL);
//Initial Constraints
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.BOTH;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 1;
gBC.weighty = 0;
gBC.insets = new Insets(10, 0, 10, 0);
//Add Examples to ContentPane
contentPane.add(LeftoverExample.createGBL(false), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createGBL(true), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createBoxLayout(false), gBC);
gBC.gridy++;
contentPane.add(LeftoverExample.createBoxLayout(true), gBC);
//Final
jF.setContentPane(contentPane);
jF.setVisible(true);
}
private static JComponent createGBL(boolean addButtons){
//GBL Example
JLabel gBLJLabel = new JLabel("GridBagLayout");
gBLJLabel.setVerticalAlignment(SwingConstants.CENTER);
gBLJLabel.setLayout(new BoxLayout(gBLJLabel, BoxLayout.X_AXIS));
gBLJLabel.setBackground(Color.CYAN);
gBLJLabel.setOpaque(true);
gBLJLabel.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
GridBagLayout gBL = new GridBagLayout();
gBL.columnWidths = new int[]{0};
gBL.rowHeights = new int[]{50};
gBLJLabel.setLayout(gBL);
//Initial Constraints
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.BOTH;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 1;
gBC.weighty = 0;
gBC.insets = new Insets(0, 0, 0, 0);
//Add to GBL Panel
if(addButtons){
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
LeftoverExample.addButtons(gBLJLabel, gBC);
}
return gBLJLabel;
}
private static JComponent createBoxLayout(boolean addButtons){
//BoxLayout Example
JLabel boxLayoutJL = new JLabel("BOX_LAYOUT");
boxLayoutJL.setVerticalAlignment(SwingConstants.CENTER);
boxLayoutJL.setLayout(new BoxLayout(boxLayoutJL, BoxLayout.X_AXIS));
boxLayoutJL.setBackground(Color.GREEN);
boxLayoutJL.setOpaque(true);
boxLayoutJL.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
//Add to BoxLayout Panel
if(addButtons){
LeftoverExample.addButtons(boxLayoutJL);
LeftoverExample.addButtons(boxLayoutJL);
LeftoverExample.addButtons(boxLayoutJL);
}
return boxLayoutJL;
}
private static JButton createButton(Color c){
JButton jB = new JButton();
jB.setBackground(c);
jB.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
return jB;
}
private static void addButtons(JComponent jC, GridBagConstraints gBC){
//Create Buttons
Color[] colorA = {Color.RED, Color.BLUE, Color.BLACK, Color.GREEN};
for(Color c : colorA){
jC.add(LeftoverExample.createButton(c), gBC);
gBC.gridx++;
}
}
private static void addButtons(JComponent jC){
//Create Buttons
Color[] colorA = {Color.BLUE, Color.BLACK, Color.GREEN, Color.RED};
for(Color c : colorA){
jC.add(LeftoverExample.createButton(c));
}
}
}
See how each West and East side there is some space left that the parent (in this case JLabel) takes up but the buttons don't. I want to be able to have the buttons take up that space as well.
Picture showing example:
The problem is caused by Swing using integer values for dimensions rather than double.
Taking this fact into consideration, the remainder r of the division of the containers width divided by the number of Component (in your case JButton objects) objects it contains can be used to increase the size of the first r Component objects by 1 to compensate. Obviously this means the first r Component objects will be +1 larger than the other Components, but this should not be noticeable.
In order to update the width of the Component objects we need to have access to there container (e.g. JPanel) and all the Component objects we wish to update. In my example, I will use a List for this purpose.
Here is a method to do the work of resizing the Component objects accordingly.
private static void fixComponentWidths(Component container,
List<? extends Component> componentList, int componentHeight) {
if (!componentList.isEmpty()) { // Avoid possible division by zero
// get the desired component width for the container using integer division
int baseComponentWidth = container.getWidth() / componentList.size();
// find the remainder
int remainder = container.getWidth() % componentList.size();
// update all the components
for (int i = 0; i < componentList.size(); i++) {
// the component width will be the base width plus 1 iff i < remainder
int componentWidth = baseComponentWidth;
if (i < remainder) {
componentWidth++;
}
// update the maximum size
componentList.get(i).setMaximumSize(new Dimension(componentWidth, componentHeight));
}
// be sure to revalidate otherwise it may not work
container.revalidate();
}
}
In order for this to work on resize, a ComponentListener must be implemented for our container. This could either be the JFrame or just a JPanel (as per my example). Note, only the componentResized(ComponentEvent) method needs implementing for this task.
buttonContainer.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent ce) { // just implementing this
fixComponentWidths(buttonContainer, buttons, BUTTON_HEIGHT);
// where buttonContainer is a JPanel,
// buttons is a List of JButtons
// BUTTON_HEIGHT, well the height of the button!
}
#Override
public void componentMoved(ComponentEvent ce) { // not needed
}
#Override
public void componentShown(ComponentEvent ce) { // not needed
}
#Override
public void componentHidden(ComponentEvent ce) { // not needed
}
});
That is all that is needed. But for completeness here's a small example, based on the author's question, followed by a subclass of JPanel which uses a BoxLayout that can be used to resolve this behavior for both BoxLayout.X_AXIS and BoxLayout.Y_AXIS.
Complete example
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FillExample extends JFrame {
private static final int FRAMEL_DEFAULT_WIDTH = 700;
private static final int FRAME_DEFAULT_HEIGHT = 400;
private static final int BUTTON_HEIGHT = Integer.MAX_VALUE;
private final List<JButton> buttons;
public FillExample() {
buttons = new ArrayList<>();
}
public void createAndShow() {
setTitle("Fill Example");
setSize(FRAMEL_DEFAULT_WIDTH, FRAME_DEFAULT_HEIGHT);
final JPanel buttonContainer = new JPanel();
buttonContainer.setLayout(new BoxLayout(buttonContainer, BoxLayout.X_AXIS));
for (int i = 0; i < 3; i++) {
addButtons(buttonContainer);
}
getContentPane().add(buttonContainer);
buttonContainer.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent ce) {
fixComponentWidths(buttonContainer, buttons, BUTTON_HEIGHT);
}
#Override
public void componentMoved(ComponentEvent ce) {
}
#Override
public void componentShown(ComponentEvent ce) {
}
#Override
public void componentHidden(ComponentEvent ce) {
}
});
setVisible(true);
}
private static void fixComponentWidths(Component container, List<? extends Component> componentList, int componentHeight) {
if (!componentList.isEmpty()) {
int baseComponentWidth = container.getWidth() / componentList.size();
int remainder = container.getWidth() % componentList.size();
for (int i = 0; i < componentList.size(); i++) {
int componentWidth = baseComponentWidth;
if (i < remainder) {
componentWidth++;
}
componentList.get(i).setMaximumSize(new Dimension(componentWidth, componentHeight));
}
container.revalidate();
}
}
private void addButtons(JComponent component) {
Color[] colorA = {Color.RED, Color.BLUE, Color.BLACK, Color.GREEN};
for (Color c : colorA) {
JButton button = createButton(c);
buttons.add(button);
component.add(button);
}
}
private static JButton createButton(Color color) {
JButton button = new JButton();
button.setBackground(color);
button.setMaximumSize(new Dimension(Integer.MAX_VALUE, BUTTON_HEIGHT));
return button;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new FillExample().createAndShow();
}
});
}
}
FillBoxLayoutPanel
This small class can be used to quickly resolve this spacing issue for both BoxLayout.X_AXIS and BoxLayout.Y_AXIS. Note, that the class create the BoxLayout and the LayoutManager cannot be changed.
Component objects can be added to the panel using add(Component comp) and add(Component comp, int index). Note, not all add methods are overridden, the class should be used carefully.
import java.awt.Component;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
public class FillBoxLayoutPanel extends JPanel {
public static final int X_AXIS = BoxLayout.X_AXIS;
public static final int Y_AXIS = BoxLayout.Y_AXIS;
private final List<Component> components;
private final int direction;
private boolean layoutSet;
public FillBoxLayoutPanel(int direction) {
components = new ArrayList<>();
this.direction = direction;
setLayout(new BoxLayout(this, direction));
layoutSet = true;
addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent ce) {
adjustComponents();
}
#Override
public void componentMoved(ComponentEvent ce) {
}
#Override
public void componentShown(ComponentEvent ce) {
}
#Override
public void componentHidden(ComponentEvent ce) {
}
});
}
#Override
public void setLayout(LayoutManager mgr) {
if (layoutSet) {
throw new UnsupportedOperationException("FillPanel's layout manager cannot be changed.");
} else {
super.setLayout(mgr);
}
}
#Override
public Component add(Component comp) {
comp = super.add(comp);
components.add(comp);
return comp;
}
#Override
public Component add(Component comp, int i) {
comp = super.add(comp, i);
components.add(i, comp);
return comp;
}
private void adjustComponents() {
if (!components.isEmpty()) {
int size = direction == X_AXIS ? getWidth() : getHeight();
int baseComponentSize = size / components.size();
int remainder = size % components.size();
for (int i = 0; i < components.size(); i++) {
int componentSize = baseComponentSize;
if (i < remainder) {
componentSize++;
}
Dimension dimension;
if (direction == X_AXIS) {
dimension = new Dimension(componentSize, components.get(i).getHeight());
} else {
dimension = new Dimension(components.get(i).getWidth(), componentSize);
}
components.get(i).setMaximumSize(dimension);
}
revalidate();
}
}
}
I think the problem is because the Layouts when all components have same settings it tries to give each component same space. So that little extra space can't be divided up equally to each component they leave it out.
Maybe you can use the Relative Layout.
It allows you to easily make each component the same size.
It then has a property that allows you to determine how extra pixels should be allocated if needed.

JButtons won't update on button click

Currently displays a GUI with an 8x8 grid of randomized colored buttons. The newButton is to reset the score display to 0 and reset the grid with a fresh like it does on startup after being clicked. I haven't been able to find many solutions other than that it's to do with the way Java displays it's buttons and the way layering works. Here are the 2 classes I'm using.
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.JPanel;
import java.awt.Color;
public class ShinyButtonsApp extends JFrame implements ActionListener {
private static byte ROWS = 8;
public int useThis;
ShinyButtons shiny = new ShinyButtons();
public static ImageIcon[] icons = {new ImageIcon("RedButton.png"),
new ImageIcon("OrangeButton.png"),
new ImageIcon("YellowButton.png"),
new ImageIcon("GreenButton.png"),
new ImageIcon("BlueButton.png"),
new ImageIcon("LightGrayButton.png"),
new ImageIcon("DarkGrayButton.png")};
public ShinyButtonsApp(String title) {
super(title); // Set title of window
setDefaultCloseOperation(EXIT_ON_CLOSE); // allow window to close
setSize(578, 634); // Set size of window
setResizable(false);
getContentPane().setLayout(null);
JLabel aLabel = new JLabel("Score: ");
aLabel.setLocation(10, 570);
aLabel.setSize(80,30);
getContentPane().add(aLabel);
JTextField scoreField = new JTextField();
scoreField.setText(Integer.toString(shiny.score));
scoreField.setEditable(false);
scoreField.setHorizontalAlignment(JTextField.RIGHT);
scoreField.setLocation(60, 570);
scoreField.setSize(120,30);
scoreField.setBackground(Color.WHITE);
getContentPane().add(scoreField);
JButton newButton = new JButton("New Game");
newButton.addActionListener(this);
newButton.setLocation(348,570);
newButton.setSize(110,30);
getContentPane().add(newButton);
JButton quitButton = new JButton("Quit");
quitButton.setLocation(468,570);
quitButton.setSize(80,30);
getContentPane().add(quitButton);
resetButtons2();
}
public void actionPerformed(ActionEvent e) {
shiny.score = 0;
shiny.resetButtons();
resetButtons2();
}
public void resetButtons2() {
for (int r=0; r<ROWS; r++) {
for (int c=0; c<ROWS; c++) {
ImageIcon image1 = icons[(int)shiny.getButton(r,c)];
JButton button = new JButton(image1);
button.setLocation(10+(69*r),10+(69*c));
button.setSize(69,69);
button.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
getContentPane().add(button);
}
}
}
public static void main(String[] args) {
ShinyButtonsApp frame;
frame = new ShinyButtonsApp("Shiny Buttons"); // Create window
frame.setVisible(true); // Show window
}
}
and
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.JPanel;
import java.awt.Color;
public class ShinyButtons extends JPanel{
public static byte RED = 0;
public static byte ORANGE = 1;
public static byte YELLOW = 2;
public static byte GREEN = 3;
public static byte BLUE = 4;
public static byte LIGHT_GRAY = 5;
public static byte DARK_GRAY = 6;
public static byte ROWS = 8;
public byte[][] buttonTable;
public int score = 0;
public ShinyButtons() {
buttonTable = new byte[ROWS][ROWS];
resetButtons();
}
public void resetButtons() {
for (int r=0; r<ROWS; r++)
for (int c=0; c<ROWS; c++)
buttonTable[r][c] = (byte)(Math.random()*7);
}
public byte getButton(int r, int c) { return buttonTable[r][c]; }
public int getScore() { return score; }
}
Building on to what this answer points out (using layout managers instead of setting size, as you should be doing) you could reset the the images just by looping through the components (JLabels) of the JPanel and changing their icons.
This particular example uses JLabels with MouseListeners but it could easily be switched to JButtons with ActionListeners
newGame.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
reset(iconPanel, icons); <--- call reset method from below
score = 0;
scoreField.setText(String.valueOf(score));
}
});
....
private void reset(JPanel panel, ImageIcon[] icons) {
Component[] comps = panel.getComponents();
Random random = new Random();
for(Component c : comps) {
if (c instanceof JLabel) {
JLabel button = (JLabel)c;
int index = random.nextInt(icons.length);
button.setIcon(icons[index]);
}
}
}
Here's the complete running code. You just need to replace the image paths.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class CircleImages {
private int score = 0;
private JTextField scoreField = new JTextField(10);
public CircleImages() {
scoreField.setEditable(false);
final ImageIcon[] icons = createImageIcons();
final JPanel iconPanel = createPanel(icons, 8);
JPanel bottomLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
bottomLeftPanel.add(new JLabel("Score: "));
bottomLeftPanel.add(scoreField);
JPanel bottomRightPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
JButton newGame = new JButton("New Game");
bottomRightPanel.add(newGame);
JButton quit = new JButton("Quit");
bottomRightPanel.add(quit);
JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
bottomPanel.add(bottomLeftPanel);
bottomPanel.add(bottomRightPanel);
newGame.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
reset(iconPanel, icons);
score = 0;
scoreField.setText(String.valueOf(score));
}
});
JFrame frame = new JFrame();
frame.add(iconPanel);
frame.add(bottomPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void reset(JPanel panel, ImageIcon[] icons) {
Component[] comps = panel.getComponents();
Random random = new Random();
for(Component c : comps) {
if (c instanceof JLabel) {
JLabel button = (JLabel)c;
int index = random.nextInt(icons.length);
button.setIcon(icons[index]);
}
}
}
private JPanel createPanel(ImageIcon[] icons, int gridSize) {
Random random = new Random();
JPanel panel = new JPanel(new GridLayout(gridSize, gridSize));
for (int i = 0; i < gridSize * gridSize; i++) {
int index = random.nextInt(icons.length);
JLabel label = new JLabel(icons[index]);
label.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
score += 1;
scoreField.setText(String.valueOf(score));
}
});
label.setBorder(new LineBorder(Color.GRAY, 2));
panel.add(label);
}
return panel;
}
private ImageIcon[] createImageIcons() {
String[] files = {"blackcircle.png",
"bluecircle.png",
"greencircle.png",
"greycircle.png",
"orangecircle.png",
"redcircle.png",
"yellowcircle.png"
};
ImageIcon[] icons = new ImageIcon[files.length];
for (int i = 0; i < files.length; i++) {
icons[i] = new ImageIcon(getClass().getResource("/circles/" + files[i]));
}
return icons;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CircleImages();
}
});
}
}
You are creating a new set of buttons in resetButtons2() and placing them on top (or rather under) of the already existing set of buttons in the same locations. You should create the set of buttons once and only update their icons upon reset.
You should do a number of things:
Use a proper layout manager, e.g., GridLayout
Create the 8x8 grid of buttons only once and replace their icons when needed
Call invalidate/repaint to refresh the content pane
And for a JFrame you don't need getContentPane().add(...), you can directly do add(...)
Two things.
First, make sure you remove the existing buttons first. Alternatively, you could simply update the state of the buttons. This would require you to create an array or List of buttons first, which your reset method would then iterate over and update their properties as required.
Second, make use of an appropriate layout manager. A null layout ias a very bad idea. You do not control the font metrics of the underlying platform and this will change how your buttons look on different systems, making your UI less dynamic then it should be

How to avoid writing a text on a button that already has text on it? java

I have a method in my project, which randomly selects a button from a list and then writes the given text on it. The problem I am getting is, since it selects the button at random, it writes the text on the button even if there is already a text written on the button. what I want is, for this code to select a button and then check if there is already a text written on it and if there is, then I want it to repeat itself and select another random button, until it finds and write the given text on a button that has no text on it previously.
public class random{
public static String text2 = "text2";
public void static main(String[] args){
JButton button = new JButton("Empty");
JButton button1 = new JButton("Empty");
JButton button2 = new JButton("Empty");
randomButton(button,button1, button2);
}
public void randomButton(JButton button1, JButton button2, JButton button3){
String text = "text";
JButton[] arr = {button1, button2, button3};
Random r = new Random();
JButton b = arr[r.nextInt(arr.length)];
b.setText(text);
b.setEnabled(false);
}
}
The buttons can have one of the three texts on them - Empty, text1 or text2.
What I want to do is, if the button selected at random when the method randomButton is called, I want it to check if the button selected already has text1 or text2 and if it does then I want it to reselect another random button until it finds a button that has Empty written on it and then replace it with text1.
I have tried do while loop, if statement and while loops but it won't work, maybe it because my logic is wrong but i am not sure. below, I have pasted one of my attempts but it does not work.
do {
String text = "text";
JButton[] arr = {button1, button2, button3};
Random r = new Random();
JButton b = arr[r.nextInt(arr.length)];
if (b.getText() != "text" || b.getText() != "text2") {
b.setText(text);
b.setEnabled(false);
} else {
String text = "text";
JButton[] arr = {button1, button2, button3};
Random r = new Random();
JButton b = arr[r.nextInt(arr.length)];
b.setText(text);
b.setEnabled(false);
}
} while (b.getText() == text);
Have a look at this. There is a 2D array of JButton. When you click the random button, it will check to see if the button at a random index has an X or not, if not it it will set the text. Let me know if there's anything you don't understand
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class RandomButton {
JButton[][] buttons = new JButton[10][10];
JButton randomButton = new JButton("Choose Random Button");
public RandomButton() {
JPanel panel = new JPanel(new GridLayout(10, 10));
initButtons(panel);
randomButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
while (true) {
int[] indices = getRandom();
int i = indices[0];
int j = indices[1];
if (!"X".equals(buttons[i][j].getText())) {
buttons[i][j].setForeground(Color.BLUE);
buttons[i][j].setText("X");
break;
}
}
}
});
randomButton.setBorder(new LineBorder(Color.black, 5));
panel.setBorder(new LineBorder(Color.BLACK, 5));
JFrame frame = new JFrame("Random Button");
frame.add(panel);
frame.pack();
frame.add(randomButton, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public int[] getRandom() {
Random rand = new Random();
int i = rand.nextInt(10);
int j = rand.nextInt(10);
int[] indices = {i, j};
return indices;
}
private void initButtons(JPanel panel) {
for (int i = 0; i < buttons.length; i++) {
for (int j = 0; j < buttons[i].length; j++) {
buttons[i][j] = new JButton("O");
panel.add(buttons[i][j]);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new RandomButton();
}
});
}
}
UPDATE
import java.awt.Color;
import java.awt.GridLayout;
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.SwingUtilities;
import javax.swing.border.LineBorder;
public class RandomButton {
JButton[][] buttons = new JButton[10][10];
JButton randomButton = new JButton("Choose Random Button");
public RandomButton() {
JPanel panel = new JPanel(new GridLayout(10, 10));
initButtons(panel);
panel.setBorder(new LineBorder(Color.BLACK, 5));
JFrame frame = new JFrame("Random Button");
frame.add(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
JButton button = (JButton)e.getSource();
System.out.println(button.getText());
}
}
private void initButtons(JPanel panel) {
ButtonListener listener = new ButtonListener();
int count = 1;
for (int i = 0; i < buttons.length; i++) {
for (int j = 0; j < buttons[i].length; j++) {
JButton button = new JButton(String.valueOf(count));
buttons[i][j] = button;
button.addActionListener(listener);
panel.add(button);
count++;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new RandomButton();
}
});
}
}
package com.prasad.workouts.swing;
import java.util.Random;
import javax.swing.JButton;
public class SelectRandomBtn {
public static String text2 = "text2";
public static void randomButton(JButton button1, JButton button2,
JButton button3) {
String text = "text";
boolean isFound = true;
JButton[] arr = { button1, button2, button3 };
Random r = new Random();
for (int i = 0, length = arr.length; i < length; i ++) {
if (arr[i].getText().isEmpty()) {
isFound = false;
}
}
do {
JButton b = arr[r.nextInt(arr.length)];
if (b.getText().isEmpty()) {
isFound = true;
System.out.println("Button Name ::: " + b.getName());
System.out.println("Button text before replace ::: "
+ b.getText());
b.setText(text);
System.out.println("Button text after replace ::: "
+ b.getText());
b.setEnabled(false);
}
} while (!isFound);
}
public static void main(String[] args) {
JButton button1 = new JButton("Empty");
button1.setName("button1");
JButton button2 = new JButton();
button2.setName("button2");
JButton button3 = new JButton();
button3.setName("button3");
randomButton(button1, button2, button3);
}
}
public class Random {
public static void main(String[] args) {
JButton button1 = new JButton("Empty");
JButton button2 = new JButton("Empty");
JButton button3 = new JButton("Empty");
List<JButton> buttonList = new ArrayList<JButton>();
buttonList.add(button1);
buttonList.add(button2);
buttonList.add(button3);
setRandomButtonText(buttonList);
}
public void setRandomButtonText(List<JButton> buttonList) {
List<JButton> availableButtons = new ArrayList<JButton>();
for (JButton button : buttonList) {
if (!"text".equals(button.getText()) || !"text2".equals(button.getText())) { //write your condition
availableButtons.add(button);
}
}
Random random = new Random();
JButton selectedButton = availableButtons.get(random.nextInt(availableButtons.size()));
selectedButton.setText("YOUR_TEXT");
selectedButton.setEnabled(false);
}
}

Beginning with event listeners

I have a simple app, showing picture made of tiled images(named u1, u2,...,u16.jpg). Now I'd like to add some Events to it, so that I can show these images only when proper button is clicked. I've tried doing it on my own, but it's not working. Where am I doing something wrong?
Original code :
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.BevelBorder;
public class Tiles_2 {
public static void main(String[] args) {
final JFrame f = new JFrame("Usmiech");
JPanel panel = new JPanel(new GridLayout(4, 4, 3, 3));
JLabel l = new JLabel();
for (int i = 1; i < 17; i++) {
String path = "u"+ i+".jpg";
l = new JLabel(new ImageIcon(path));
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
panel.add(l);
}
f.setContentPane(panel);
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
New code :
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.*;
public class Zad_8_1 implements ActionListener {
public void actionPerformed(ActionEvent e) {
JButton b = (JButton)(e.getSource());
String i = b.getText();
b = new JButton(new ImageIcon("u"+i+".jpg"));
}
public static void main(String[] args) {
final JFrame f = new JFrame("Smile");
JPanel panel = new JPanel(new GridLayout(4, 4, 3, 3));
JButton l = null;
for (int i = 1; i < 17; i++) {
String path = "u"+ i+".jpg";
l = new JButton(""+i);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.setSize(53,53);
panel.add(l);
}
f.setContentPane(panel);
f.setSize(300, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
This should work like this :
this http://img535.imageshack.us/img535/3129/lab8a.jpg
Try registering the buttons to listen for the event like this:
for (int i = 1; i < 17; i++) {
String path = "u"+ i+".jpg";
l = new JButton(""+i);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.setSize(53,53);
l.addActionListener(this);
panel.add(l);
}
You need to have constructor to add event listeners.
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.*;
import java.awt.*;
public class Demo {
public static void main(String args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
render("Window");
}
});
}
public static void render(String s){
JFrame f = new JFrame("Smile");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Pane pan = new Pane();
f.add(pan);
f.setSize(300,300);
f.setVisible(true);
}
}
class Pane extends JPanel implements ActionListener {
Pane(){
setLayout(new GridLayout(4, 4, 3, 3));
JButton l = null;
for (int i = 1; i < 17; i++) {
l = new JButton(""+i);
l.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
l.addActionListener(this);
l.setSize(53,53);
add(l);
}
}
public void actionPerformed(ActionEvent e) {
JButton b = (JButton)(e.getSource());
String i = b.getText();
b.setIcon(new ImageIcon("u"+i+".jpg"));
}
}
b = new JButton(new ImageIcon("u"+i+".jpg"));
Creating a new button doesn't do anything the button just sits there in memory as it hasn't been added to the GUI.
The simple solution is to just reset the icon:
b.setIcon( new ImageIcon(...) );
Instead of reassigning b a new instance of JButton, you should simply reset the icon:
b.setIcon(new ImageIcon("u"+i+".jpg"));
(take a look at pass by reference/pass by value concepts in Java, well explained here)
on top of that, you need to follow Vincent's advice and actually register with all of the buttons as an ActionListener
l.addActionListener(this);

Categories