This is my first attempt at using a GUI layout in Java. I am trying to create a simple memory card game, where the user flips over two cards, and tries to get a match, and if there's no match they flip back over, if there's a match they stay flipped until you get all the matches. I might have made it hard on myself though in that I made the whole game dynamic to the constructor variables that define the number of columns and rows of cards. I thought this was better than hard-coding a certain value in, but now I'm having trouble putting the images in my img folder onto my cards.
I understand that variables of variables are not permitted in Java & this is really hard for me to adapt to as a ColdFusion developer. Can you help me think of ways to accomplish this this the proper way in Java?
Here is a simplified version of my code.
import javax.swing.JFrame;
public class MemoryApp
{
public static void main(String args[])
{
//Creates a new game with 3 columns and 4 rows
final CardGame myGame = new CardGame(3, 4);
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI(myGame.getCols(), myGame.getRows());
}
});
}
private static void createAndShowGUI(int c, int r) {
//Create and set up the window.
Window frame = new Window("GridLayoutDemo", c, r);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
frame.addComponentsToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
Card game class:
public class CardGame
{
private int cols, rows;
public CardGame(int c, int r)
{
cols = c;
rows = r;
}
public int getCols(){
return cols;
}
public int getRows(){
return rows;
}
}
The window with the grid layout:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
public class Window extends JFrame
{
private int cols, rows;
GridLayout windowGrid = new GridLayout(1,1);
public Window(String t, int c, int r)
{
super(t);
cols = c;
rows = r;
windowGrid.setColumns(c);
windowGrid.setRows(r);
}
public void addComponentsToPane(final Container pane)
{
final JPanel compsToExperiment = new JPanel();
compsToExperiment.setLayout(windowGrid);
JPanel controls = new JPanel();
controls.setLayout(new GridLayout(cols,rows));
int countCard = (cols * rows) / 2;
/**
* Add buttons to experiment with Grid Layout.
* This is where I'd like to be able to loop through
* countCard and create the required number of buttons
* as well as put images on the buttons like so:
*
* ImageIcon image1 = new ImageIcon(getClass().getResource("card1.png"));
* through
* ImageIcon image1 = new ImageIcon(getClass().getResource("card" & cardCount & ".png"));
*
* Below is how I would attempt this in ColdFusion- I know
* the variable of variables syntax is invalid, it is just
* to show you what I mean.
*/
// for(int i = 0; i < countCard; i++;)
// {
// compsToExperiment.add(new JButton("../img/card" & i & ".png"));
// ImageIcon variables["image" & i] = new ImageIcon(getClass().getResource("card" & i & ".png"));
// imageButton.setIcon(variables["image" & i]);
// etc. with ButtonClickEventHandler, haven't gotten that far yet
// }
pane.add(compsToExperiment, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.CENTER);
}
}
Based on the code commented-out code that you posted so far, one approach could be like this:
public class Window extends JFrame
{
...
// A java.util.List that stores all the buttons, so
// that their icons may be changed later
private List<JButton> buttons = new ArrayList<JButton>();
// A java.util.List that stores all the ImageIcons that
// may be placed on the buttons
private List<ImageIcon> imageIcons = new ArrayList<ImageIcon>();
public void addComponentsToPane(final Container pane)
{
...
for(int i = 0; i < countCard; i++;)
{
// String concatenation is done with "+" in Java, not with "&"
String fileName = "card" + i + ".png";
// Create the icon and the button containing the icon
ImageIcon imageIcon = new ImageIcon(getClass().getResource(fileName));
JButton button = new JButton(imageIcon);
// Add the button to the main panel
compsToExperiment.add(button);
// Store the button and the icon in the lists
// for later retrieval
imageIcons.add(imageIcon);
buttons.add(button);
// Attach an ActionListener to the button that will
// be informed when the button was clicked.
button.addActionListener(createActionListener(i));
}
}
private ActionListener createActionListener(final int cardIndex)
{
return new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
clickedCardButton(cardIndex);
}
};
}
private void clickedCardButton(int cardIndex)
{
System.out.println("Pressed button for card "+cardIndex);
// Here you can change the icons of the buttons or so...
JButton button = buttons.get(cardIndex);
ImageIcon imageIcon = imageIcons.get(cardIndex);
....
}
You mentioned that this is your first attempt to build a GUI with Java. So I assume that this is only intended for "practicing". If your intention was to build a "real application", you should rather consider some Model-View-Controller (MVC) approach for this.
Related
I've been trying to get the GUI going for the application I'm working on, and, it's been ages since doing anything in Swing. Most of the stuff has come back, but not this. I cannot understand why, across none of the different text components, if I type something into them, getText() will always return "" regardless. It'll return correctly if I use setText() as a test, but that's not really helpful.
This remains consistent across JTextArea/JTextField, various ways of referencing the text field (direct variable vs pulling from the HashMap), and regardless of what kind of thread does the accessing. At times I've used the debugger to try and look at things as they're happening through other test snippets, and still nothing. Since the edit, have trimmed a lot of these cases out, but still persists in the problem.
In all cases, user input is never acquired.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class LifePathUI2 {
final static boolean shouldWeightX = true;
private GridBagConstraints cons;
private BorderLayout windowLayout;
private GridBagLayout layout;
private JFrame mainWindow;
private JPanel mainPanel;
private JComponent currentComponent;
private JTextField characterName;
/**
* #throws HeadlessException
*/
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
init();
}
public void init()
{
mainWindow.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
mainWindow.setLayout(windowLayout);
cons.ipadx = 5;
cons.ipady = 5;
cons.anchor = GridBagConstraints.NORTHWEST;
cons.fill = GridBagConstraints.NONE;
cons.weighty = 1.0;
cons.weightx = 1.0;
// to make everything work right we add a mainPanel under the mainWindow
cons.gridheight = 1;
cons.gridwidth = 1;
mainWindow.add(mainPanel);
// Readers keep in mind if you don't set this below, strange behavior happens
mainPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
mainPanel.setLayout(layout);
currentComponent = mainPanel;
addLabel(0,0,"Character Name");
characterName = addTextF(1,0,"",30,true);
endRow(2,0);
addButton(0,1,"Foo").addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println(characterName.getText());
}
});
endVertical(0,2);
mainWindow.setSize(1400, 900);
mainWindow.setVisible(true);
}
/**
* Inserts a spacer to signify the end of components for this row (effects layout so it doesn't center)
*/
private void endRow(int x, int y)
{
cons.weightx = 100.0;
addLabel(x,y,"");
cons.weightx = 1.0;
}
/**
* Inserts a spacer to signify the end of components vertically (effects layout so it doesn't center)
*/
private void endVertical(int x, int y)
{
cons.weighty = 100.0;
addLabel(x,y,"");
cons.weighty = 1.0;
}
/**
* Shorthand command to add label at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param text Display Text for label
*/
private JLabel addLabel(int x, int y, String text)
{
JLabel temp = new JLabel(text);
addC(temp,x,y);
return temp;
}
/**
* Shorthand command to add Button at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param text Display Text for Button
* #return The component created
*/
private JButton addButton(int x, int y, String text)
{
JButton temp = new JButton(text);
addC(temp,x,y);
return temp;
}
/**
* Shorthand command to add Text Field at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param value the default value for the text field
* #param cols Number of Columns
* #param updateListen If true will add listener that triggers UI updates when values change
* #return The component created
*/
private JTextField addTextF(int x, int y, String value, int cols, boolean updateListen)
{
JTextField temp = new JTextField(value, cols);
// this prevents the common issue of the text fields turning into slits
temp.setMinimumSize(temp.getPreferredSize());
addC(temp,x,y);
return temp;
}
/**
* Shorthand to add new components to the UI tab at given coords
* #param comp Component to add to UI
* #param x non-negative integer
* #param y non-negative integer
*
*/
private void addC(JComponent comp, int x, int y) {
cons.gridx = x;
cons.gridy = y;
currentComponent.add(comp,cons);
}
/**
* #param args
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
LifePathUI2 ui = new LifePathUI2();
ui.init();
}
});
}
}
I've searched every question here for swing stuff and none of them problems seem to correspond to this problem specifically. Was not entirely expecting that.
I apologize for the bigger snippet but since all of the parts immediately around creating the text fields, reading them, creating the GUI itself, all seem to match the examples online I've seen... I'm just smart enough to know there must be something odd I did here and just not seeing it.
EDIT: Simplifying example. Despite breaking this down to a much more simple test case, still no dice
EDIT2: Finished compression of things. I was actually expecting this to work because this is on the level of working code I've written before. But still nothing
A wild guess, but your update() method looks like it might be trying to extract the text from the Document that its listening to (you don't say), and if so, then use the DocumentEvent to get the Document and then extract the text. Something like:
private class TextChangeListener implements DocumentListener {
#Override
public void changedUpdate(DocumentEvent e) {
update(e);
}
#Override
public void insertUpdate(DocumentEvent e) {
update(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
update(e);
}
}
and
private void update(DocumentEvent e) {
String text;
try {
Document doc = e.getDocument();
text = e.getDocument().getText(0, doc.getLength());
// do something with text here! ************
System.out.println(text);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
Other than that, I'm lost in your code. Simplify. Refactor. Simplify some more. Refactor some more. Think smaller classes that can be fully tested in isolation.
Edit
You call init() TWICE, and that's the problem since it means that you create two of everything, one displayed and one not.
You first call it here:
public LifePathUI2() throws HeadlessException {
// ....
init();
}
and then call it again in your main:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
LifePathUI2 ui = new LifePathUI2();
ui.init();
}
});
Call it only once.
Change this:
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
init();
}
to this:
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
// init();
}
package javaapplication3;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class BattleShip2 extends JFrame implements ActionListener {
JButton[][] array2dtop = new JButton[10][10];
BattleShip2(String title) { //Board Display
super(title);//show Title
setLayout(new FlowLayout()); // button posistion
for(int x = 0; x < array2dtop.length; x++) {
for(int y = 0; y < array2dtop.length; y++) {
array2dtop[x][y] = new JButton(String.valueOf((x * 10) + y)); //display
array2dtop[x][y].setBounds(20,40,140,20);
array2dtop[x][y].setActionCommand("x"); //itself value
array2dtop[x][y].addActionListener(this);
add(array2dtop[x][y]);
}
}
}
public void actionPerformed(ActionEvent evt) { //operation
String cmd = evt.getActionCommand();
if(cmd == "x") {
System.out.println("x");
}
}
}
public class pratice3 {
public static void main(String[] args) {
BattleShip2 board = new BattleShip2("BattleShip");
board.setSize(500, 400);
board.setLocation(250, 250);
board.setDefaultCloseOperation(board.EXIT_ON_CLOSE); //Press x to EXIT
board.setVisible(true);
}
}
Hi, i am creating a battleship game, and my idea is when the user or AI hit the button, it could show ship size
For example, there have 3 ships: ship1(size = 3), ship2(size = 2), ship3(size = 1), and when the user/AI hit the ship1, and they see 3, they will know they hit ship1
but I am having troubles on the setActioncommand
on the code,
array2dtop[x][y].setActionCommand("x")
I want to assign or declare a integer(the size of the ship) into array2dtop[x][y]
but I have no idea but assign a string and how can I use it in the actionPerformed
I want to assign or declare a integer(the size of the ship) into array2dtop[x][y]
You can just parse the string to an integer using Integer.parseInt:
class BattleShip2 extends JFrame implements ActionListener {
JButton[][] array2dtop = new JButton[10][10];
BattleShip2(String title) {
super(title);
setLayout(new GridLayout(10, 10, 5, 5));
for (int x = 0; x < array2dtop.length; x++) {
for (int y = 0; y < array2dtop.length; y++) {
array2dtop[x][y] = new JButton(String.valueOf((x * 10) + y));
array2dtop[x][y].setActionCommand(String.valueOf(3));
array2dtop[x][y].addActionListener(this);
// Force buttons to be square.
Dimension dim = array2dtop[x][y].getPreferredSize();
int buttonSize = Math.max(dim.height, dim.width);
array2dtop[x][y].setPreferredSize(new Dimension(buttonSize, buttonSize));
add(array2dtop[x][y]);
}
}
}
public void actionPerformed(ActionEvent evt) {
int cmd = Integer.parseInt(evt.getActionCommand());
if (cmd == 3) {
System.out.println(3);
}
}
}
public class pratice3 {
public static void main(String[] args) {
BattleShip2 board = new BattleShip2("BattleShip");
board.pack();
board.setLocationRelativeTo(null);
board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
board.setVisible(true);
}
}
Notes:
I changed the layout to GridLayout with the same vertical and horizontal gaps as FlowLayout's defaults. It will assure that all button have the same size.
Don't use setBounds, let the layout manager take care of that.
I added 3 lines that make the buttons square, you can remove that.
Instead of using setSize for a JFrame, use pack.
Setting the location using board.setLocationRelativeTo(null) (After the call to pack) is usually a better idea since you don't know the size of the screen.
board.EXIT_ON_CLOSE should be accessed in a static way: JFrame.EXIT_ON_CLOSE (for example).
When calling Integer.parseInt, you might want to catch NumberFormatException.
With that being said, there are other ways to achieve this, such as using setClientProperty on the buttons, or just passing the number to the ActionListener's constructor.
I am trying to make a set of 2 GUIs: one, when a button is clicked, calls another, which, based on which button is clicked in the second GUI, returns a value to the first GUI. Unfortunately, when called from the first GUI's actionPerformed method, the second GUI appears blank. This does not, however, happen when JOptionPane is used.
What does JOptionPane do that allows it to work inside the actionPerformed method, and why does my example code not work inside the actionPerformed method?
The code for the first GUI, which calls the second GUI, is as follows:
public class OpeningGUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private Container container;
private JButton btn, btn2;
/**
* Constructor for class OpeningGUI - establish the JFrame
* Loads the window and moves it to the center of the screen.
*/
public OpeningGUI() {
// when mama ain't happy, ain't nobody happy
super("Dominion Launcher");
//UI components get established here
container = getContentPane(); // Container is the abstract concept of the area inside a window
container.setLayout(new BorderLayout());
container.add(getCenterPanel(), BorderLayout.CENTER);
setSize(700, 300);
pack();
setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2,
(int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Sets the game mode based on which button is clicked.
* Click stops return method from waiting.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btn) {
SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
System.out.println(sd.getSelectedIndex());
}
if(e.getSource() == btn2) {
JOptionPane.showConfirmDialog(null, "See it works, right");
}
}
/**
* Sets up the center panel with buttons to select game mode.
* #return the center panel.
*/
public JPanel getCenterPanel() {
JPanel temp = new JPanel();
btn = new JButton("SelectionDialog tester");
temp.add(btn);
btn.addActionListener(this);
btn2 = new JButton("JOptionPane tester");
temp.add(btn2);
btn2.addActionListener(this);
return temp;
}
/**
* Main method of OpeningGUI. Used to run the program.
* #param args command-line arguments. Unused.
*/
public static void main(String[] args) {
new OpeningGUI();
}
}
The code for the second GUI is as follows:
public class SelectionDialog extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private Container container;
private JButton confirmBtn;
private JButton[] buttons;
private ArrayList<Integer> selectionIndecies;
private CountDownLatch wait;
private String message;
private int numNeeded;
private boolean isMaximum;
/**
* Constructor for the SelectionDialog class.
* Selects from an ArrayList of buttons.
* #param message Message to display.
* #param num number to select.
*/
public SelectionDialog(String message, int num) {
super("Please Select Buttons");
this.message = message;
numNeeded = num;
isMaximum = false;
setupUI();
}
/**
* Establishes the JFrame and sets values for some fields.
*/
private void setupUI() {
selectionIndecies = new ArrayList<Integer>();
wait = new CountDownLatch(1);
//UI components get established here
container = getContentPane(); // Container is the abstract concept of the area inside a window
container.setLayout(new BorderLayout());
container.add(getTopPanel(), BorderLayout.NORTH);
container.add(getCenterPanel());
pack();
setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2,
(int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Changes color of buttons and adds or removes them from the selected arrays.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource() == confirmBtn) {
if((!isMaximum && selectionIndecies.size() <= numNeeded)
|| selectionIndecies.size() == numNeeded) {
wait.countDown();
dispose();
}
}
for(int i = 0; i < buttons.length; i++) {
if(e.getSource() == buttons[i]) {
if(!buttons[i].getBackground().equals(Color.ORANGE)) {
buttons[i].setBackground(Color.ORANGE);
buttons[i].setBorderPainted(false);
selectionIndecies.add(new Integer(i));
repaint();
}
else {
buttons[i].setBackground(Color.LIGHT_GRAY);
selectionIndecies.remove(new Integer(i));
repaint();
}
}
}
}
/**
* Creates the top panel of the GUI.
* Contains the prosperity check box, the number of players selector,
* and the card counter and confirm button.
* #return the top panel.
*/
private JPanel getTopPanel() {
JPanel topPanel = new JPanel();
JLabel temp = new JLabel(message + " ");
topPanel.add(temp);
confirmBtn = new JButton("Done");
topPanel.add(confirmBtn);
confirmBtn.addActionListener(this);
return topPanel;
}
/**
* Determines which buttons were selected.
* Waits until Ok has been clicked and a proper number of buttons had been selected.
* #return an array of indecies of the buttons selected.
*/
public ArrayList<Integer> getSelectedIndex() {
try {
wait.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Collections.sort(selectionIndecies);
return selectionIndecies;
}
/**
* Sets up center panel with ArrayList of buttons,
* and panels of buttons.
*/
private JScrollPane getCenterPanel() {
JPanel centerPanel = new JPanel();
buttons = new JButton[6];
for(int i = 0; i < 6; i++) {
JButton temp = new JButton("Button " + i);
temp.addActionListener(this);
temp.setVisible(true);
centerPanel.add(temp);
buttons[i] = temp;
}
return new JScrollPane(centerPanel);
}
/**
* Main method of the SelectionDialog class. For testing only.
* #param args command line arguments. Unused.
*/
public static void main(String[] args) {
SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
System.out.println(sd.getSelectedIndex());
}
}
This code is completely runnable with the two classes I have posted and appropriate import statements. The second GUI can also be run independently to show what should appear when called from the first GUI, and the first GUI contains an example JOptionPane that works normally.
Please help me figure out why the actionPerformed method stops only some GUIs from rendering, while others work normally!
You're blocking the EDT! actionPerformed is executed on the EDT, so getSelectedIndex is also and wait.await() blocks it. Notice that once this happens, the first frame doesn't respond also (and minimizing and un-minimizing the frames will not even paint them). Even if the 2nd frame were to show, it would not respond to user interaction because the first actionPerformed did not return.
I don't understand why you need the CountDownLatch. getSelectedIndex can only execute once confrimBtn is pressed, so just return the selected buttons at that point. This isn't the only solution - your design will eventually dictate the interaction between the classes.
In SelectionDialog's actionPerformed write:
if (e.getSource() == confirmBtn) {
if ((!isMaximum && selectionIndecies.size() <= numNeeded) || selectionIndecies.size() == numNeeded) {
Collections.sort(selectionIndecies);
OpeningGUI.publishSelectedIndex(selectionIndecies);
dispose();
}
}
and remove the getSelectedIndex method.
In OpeningGUI, add the following method
public static void publishSelectedIndex(ArrayList<Integer> list) {
System.out.println(list);
}
and remove from its actionPerformed the call to getSelectedIndex.
Notes:
Instead of the screen size calculation for setLocation, you can use setLocationRelativeTo(null).
Calling setSize when you call pack right after it makes the first call redundant.
No need to specify the generic type on the right-hand-side:
selectionIndecies = new ArrayList<>();
Swing should be started on the EDT (see here).
You would probably do better with a dialog instead of another JFrame.
Use different ActionListeners for buttons that function differently instead of checking the source with each call.
I am coding a little game in which i have taken a grid of JButtons in a JFrame and i want to refresh the colors of the buttons contained in a JFrame,which is already visible.As explained below
void foo(){
mainFrame.setVisible(true);//mainFrame is defined at class level.
//color update code for the buttons.
mainFrame.setVisible(true);
}
Result i am getting is not as expected and my screen gets freeze .Isn't it the right way to achieve what i wanted?
EDIT
ok i am explaining it in detail what i want to achieve.i have a class,as:-
import javax.swing.*;
import java.awt.*;
import java.util.*;
class Brick extends JButton{
public void setRandomColors(){
int random = (int) (Math.random()*50);
if(random%13==0){
this.setBackground(Color.MAGENTA);
}
else if(random%10==0){
this.setBackground(Color.red);
}
else if(random%9==0){
this.setBackground(Color.yellow);
}
else if(random%7==0){
this.setBackground(Color.orange);
}
else if(random%2==0){
this.setBackground(Color.cyan);
}
else{
this.setBackground(Color.PINK);
}
}
public void setBlackColor(){
this.setBackground(Color.black);
}
}
class Grid {
JFrame mainGrid = new JFrame();
ArrayList<Brick> bunchOfBricks = new ArrayList<>();
int gridLength = 8;//gridlenth is equals to gridweight as i have considered a Square grid.
int totalBricks = gridLength*gridLength;
public void formBunchOfBricks(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.add(new Brick());
}
}
public void formColoredGrid(){
Brick aBrick;
mainGrid.setLayout(new GridLayout(8,8));
for(int i=0;i<totalBricks;++i){
aBrick = (bunchOfBricks.get(i));
aBrick.setRandomColors();
mainGrid.add(aBrick);
}
mainGrid.setVisible(true);//its ok upto here iam getting randomly colored Frame of Bricks or so called JButtons.
delay(15);//Sorry for this one,i warn you not to laugh after looking its defination.
}
/*
I want following function to do following things:-
1.it should firstly display the Grid whose all buttons are black Colored.
2.After some time the original colored,first Row of grid formed by formColoredGrid should be displayed and all the rest Rows should be black.
3.Then second row turns colored and all other rows should be black......and so on upto last row of Grid.
*/
public void movingRows(){
setGridBlack();
delay(1);//see in upper method,for this horrible thing.
for(int i=0;i<gridLength;++i){
setGridBlack();
for (int j=0;j<gridLength;++j){
Brick aBrick = bunchOfBricks.get((gridLength*i)+j);
aBrick.setRandomColors();//Bricks are colored Row by Row.
}
delay(5);//already commented this nonsense.
mainGrid.setVisible(true);//using setVisible again,although this frame is already visible,when i called formColoredGrid.
setGridBlack();
}
//oh! disappointing,i have almost broken my arm slamming it on table that why the function result in a screen full of black buttons.
}
public void setGridBlack(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.get(i).setBlackColor();
}
}
public void delay(int a){
for ( int i=0;i<90000000;++i){
for(int j=0;j<a;++j){
}
}
}
public static void main(String args[]){
Grid g1 = new Grid();
g1.formBunchOfBricks();
g1.formColoredGrid();
g1.movingRows();
}
}
Please Help me what is the way out?
Your problem is in code not shown here:
//color update code for the buttons.
You're likely running a loop that never ends on the Swing event thread, possibly a never-ending while loop that polls the state of something(a guess), freezing your GUI. Solution: don't do this; don't use a continuous polling loop. Instead, change the colors based on responses to events as Swing is event-driven.
For better more specific help, please show the offending code and tell us more about your program.
Edit
If you're trying to show colored rows, one by one marching down the board, then my guess is right, you'll want to use a Swing Timer, one that uses an int index to indicate which row is being displayed in color. You'd increment the index inside of the Timer's ActionPerformed class, and then when all rows have been displayed stop the Timer. For example something like so:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyGrid extends JPanel {
private static final int GRID_LENGTH = 8;
private static final Color BTN_BACKGROUND = Color.BLACK;
private static final Color[] COLORS = { Color.MAGENTA, Color.CYAN,
Color.RED, Color.YELLOW, Color.ORANGE, Color.PINK, Color.BLUE,
Color.GREEN };
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 800;
private JButton[][] buttonGrid = new JButton[GRID_LENGTH][GRID_LENGTH];
private Map<JButton, Color> btnColorMap = new HashMap<>();
private Random random = new Random();
public MyGrid() {
setLayout(new GridLayout(GRID_LENGTH, GRID_LENGTH));
for (int row = 0; row < buttonGrid.length; row++) {
for (int col = 0; col < buttonGrid[row].length; col++) {
JButton btn = new JButton();
btn.setBackground(BTN_BACKGROUND);
// !! add action listener here?
add(btn);
buttonGrid[row][col] = btn;
}
}
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void resetAllBtns() {
for (JButton[] row : buttonGrid) {
for (JButton btn : row) {
btn.setBackground(BTN_BACKGROUND);
}
}
}
private class TimerListener implements ActionListener {
private int row = 0;
#Override
public void actionPerformed(ActionEvent e) {
resetAllBtns(); // make all buttons black
if (row != buttonGrid.length) {
for (int c = 0; c < buttonGrid[row].length; c++) {
int colorIndex = random.nextInt(COLORS.length);
Color randomColor = COLORS[colorIndex];
buttonGrid[row][c].setBackground(randomColor);
// !! not sure if you need this
btnColorMap.put(buttonGrid[row][c], randomColor);
}
row++;
} else {
// else we've run out of rows -- stop the timer
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
MyGrid mainPanel = new MyGrid();
JFrame frame = new JFrame("MyGrid");
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();
}
});
}
}
Please have a look at the Swing Timer Tutorial as well.
Edit 2
You ask:
but what is the reason of failure of this program,is it the useless delay function?
Your delay method does nothing but busy calculations on the Swing event thread:
public void delay(int a) {
for (int i = 0; i < 90000000; ++i) {
for (int j = 0; j < a; ++j) {
}
}
}
It's little different from a crude attempt at calling Thread.sleep(...), and is crude because you can't explicitly control how long it will run as you can with thread sleep. Again, the problem is that you're making these calls on the Swing event dispatch thread or EDT, the single thread that is responsible for all Swing drawing and user interactions. Blocking this thread will block your program making it non-running or frozen.
I'm a student programming a frogger game, when the frog collides with an object or reaches the end zone either the score is incremented or lives decremented and the frog returned to the start position. this section works and the decrement and increment work when outputting them to the console, I try to pass the variable to the other jpanel and display it there but it doesnt update and display the variable in the textField.
Game Panel
public GamePanel() {
super();
setFocusable(true);
addKeyListener(new KeyList());
System.out.println("GAME PANE FOCUS:" + this.isFocusOwner());
scores.setVisible(true);
lives = p.STARTLIVES;
scores.setCurrentLives(lives);
txtTest.setText("Hello");
txtTest.setVisible(true);
add(scores,new AbsoluteConstraints(0,550,50,800));
Boolean displayable = scores.isDisplayable();
System.out.println("Displayable" + displayable);
scores.setEnabled(false);
scores.revalidate();
scores.repaint();
scores.setVisible(true);
System.out.println("Displayable" + displayable);
car1.start();
car2.start();
car3.start();
car4.start();
Log1.start();
Log2.start();
Log3.start();
Log4.start();
Log5.start();
Log6.start();
Log7.start();
Log8.start();
//check for collisions
}
final public void winZone(Rectangle object){
if(myFrog.frogArea().intersects(object)){
currentScore = currentScore + 100;
System.out.println("current Score " + currentScore);
p.setScore(currentScore);
scores.myScore().setText("hello");
myFrog.lostLife();
}
scores panel
public class jplScores extends JPanel {
Properties p = new Properties();
int currentLives;
int i;
/** Creates new form jplScores */
public jplScores() {
initComponents();
}
public void setCurrentLives(int Lives){
currentLives = Lives;
}
public String getCurrentLives(){
String L = Integer.toString(currentLives);
return L;
}
public JTextField myScore(){
return txtScore;
}
Currently it will display the jpanel from the frame that they are both in but i have tried to make it so its a panel within a panel but i cant get the panel to display from within the game panel.
Any help would be great thanks
public FroggerGame() {
initComponents();
setFocusable(true);
//repaint();
// p.setHeight(550);
// p.setWidth(800);
// p.setLives(3);
// p.setScore(0);
PANELHEIGHT = p.getHeight();
PANELWIDTH = p.getWidth();
welcomePanel();
/*
Toolkit tool = Toolkit.getDefaultToolkit();
imgBackground = tool.getImage(imageBackground);
background = new ImageIcon(imgBackground);
//setDefaultCloseOperation(EXIT_ON_CLOSE);
*/
jps.myScore().addPropertyChangeListener(new java.beans.PropertyChangeListener() {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
// txtScorePropertyChange(evt);
jps.myScore().setText(Integer.toString(gp.currentScore()));
System.out.println(Integer.toString(gp.currentScore()));
jps.getScore(gp.currentScore());
System.out.println(" main score " + gp.currentScore());
}
});
}
....
private void btnEnterActionPerformed(ActionEvent evt) {
welcomePanel.setVisible(false);
getContentPane().setLayout(new AbsoluteLayout());
getContentPane().add(gp,new AbsoluteConstraints(0,0,800,550));
getContentPane().add(jps,new AbsoluteConstraints(0,550,800,100));
//gp.setSize(800, 550);
// gp.setBounds(0, 0, 800, 550);
gp.setVisible(true);
gp.requestFocusInWindow();
jps.setVisible(true);
gp is the game panel and jps is the score panel.
This really has little to do with "panels" or Swing GUI coding and all to do with the basic OOPS issue of passing information from one class to another. One way to solve this is to give your Scores panel a public method, say
public void changeScore(int value) {
// in here add the new value to the currently
// displayed value and display the new value.
}
Then the main class, the one with a Scores panel reference, you can call this method, passing in 1 if score is to increase or -1 if it is to decrease.
I think of this as the "push" solution, where one class pushes information into the other class. Another way to solve this is via listeners where the Scores class listens to critical properties of the other class and then changes its own score when an appropriate event occurs, and this often involves using PropertyChangeListeners or other more Swing-specific listener classes. This is sometimes a nicer solution, but I consider it a slightly more advanced concept.