I have to design a batleships game for friday but the course im doing seems to have skipped a few things because although i managed all the other assignments this final project is unbelievably above my grasp but i have to do something.
i have the following GUI code which gives me my playing grids but i have absolutely no idea how to do the following things
assign a ship to some cells - and color these cells to reflect this
how to do the actual hit,miss,sunk and update the grid
i figure if i can at least do these i can duplicate the code for the cpu but im sooooooo stuck so any help is really appreciated please guys work some magic :)
/**
* BattleGui:
*
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
public class BattleGui implements ActionListener
{
// Default filename to use for saving and loading files
// Possible improvement: replace with a FileChooser
private final static String DEFAULT_FILENAME = "battlegui.txt";
private int GRID_SIZE = 8;
private JButton [] buttonArray;
public JMenuBar createMenu()
{
JMenuBar menuBar = new JMenuBar();;
JMenu menu = new JMenu("Battle Menu");
JMenuItem menuItem;
menuBar.add(menu);
// A group of JMenuItems. You can create other menu items here if desired
menuItem = new JMenuItem("New Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Load Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Save Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Quit");
menuItem.addActionListener(this);
menu.add(menuItem);
//a submenu
menu.addSeparator();
return menuBar;
}
public Container createContentPaneCPU()
{
int numButtons = GRID_SIZE * GRID_SIZE;
JPanel grid = new JPanel(new GridLayout(GRID_SIZE,GRID_SIZE));
buttonArray = new JButton[numButtons];
for (int i=0; i<numButtons; i++)
{
buttonArray[i] = new JButton(" ");
// This label is used to identify which button was clicked in the action listener
buttonArray[i].setActionCommand("" + i); // String "0", "1" etc.
buttonArray[i].addActionListener(this);
grid.add(buttonArray[i]);
}
return grid;
}
public Container createContentPane()
{
int numButtons = GRID_SIZE * GRID_SIZE;
JPanel grid = new JPanel(new GridLayout(GRID_SIZE,GRID_SIZE));
buttonArray = new JButton[numButtons];
for (int i=0; i<numButtons; i++)
{
buttonArray[i] = new JButton(" ");
// This label is used to identify which button was clicked in the action listener
//buttonArray[i].setActionCommand("" + i); // String "0", "1" etc.
// buttonArray[i].addActionListener(this);
grid.add(buttonArray[i]);
}
return grid;
}
/**
* This method handles events from the Menu and the board.
*
*/
public void actionPerformed(ActionEvent e)
{
String classname = getClassName(e.getSource());
JComponent component = (JComponent)(e.getSource());
if (classname.equals("JMenuItem"))
{
JMenuItem menusource = (JMenuItem)(e.getSource());
String menutext = menusource.getText();
// Determine which menu option was chosen
if (menutext.equals("Load Game"))
{
/* BATTLEGUI Add your code here to handle Load Game **********/
LoadGame();
}
else if (menutext.equals("Save Game"))
{
/* BATTLEGUI Add your code here to handle Save Game **********/
SaveGame();
}
else if (menutext.equals("New Game"))
{
/* BATTLEGUI Add your code here to handle Save Game **********/
NewGame();
}
}
// Handle the event from the user clicking on a command button
else if (classname.equals("JButton"))
{
JButton button = (JButton)(e.getSource());
int bnum = Integer.parseInt(button.getActionCommand());
int row = bnum / GRID_SIZE;
int col = bnum % GRID_SIZE;
System.out.println(e.getSource());
/* BATTLEGUI Add your code here to handle user clicking on the grid ***********/
button.setBackground(Color.GREEN);
fireShot(row, col);
}
}
/**
* Returns the class name
*/
protected String getClassName(Object o)
{
String classString = o.getClass().getName();
int dotIndex = classString.lastIndexOf(".");
return classString.substring(dotIndex+1);
}
/**
* Create the GUI and show it.
* For thread safety, this method should be invoked from the event-dispatching thread.
*/
private static void createAndShowGUI()
{
// Create and set up the window.
JFrame frame = new JFrame("Battleships");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int maxGap = 20;
int ButtonWidth = 20;
int ButtonHeight = 1;
BattleGui battlegui = new BattleGui();
frame.setJMenuBar(battlegui.createMenu());
JPanel gui = new JPanel(new GridLayout(2,2,20,5));
gui.setBorder(new EmptyBorder(5,5,5,5));
//Set up components preferred size
JButton b = new JButton("Just fake button");
Dimension buttonSize = b.getPreferredSize();
gui.add(new JButton("Player"));
gui.add(new JButton("CPU"));
b.setPreferredSize(new Dimension(ButtonWidth, ButtonHeight));
gui.add(battlegui.createContentPane());
gui.add(battlegui.createContentPaneCPU());
frame.setContentPane(gui);
// Create and set up the content pane.
/*
BattleGui battlegui = new BattleGui();
frame.setJMenuBar(battlegui.createMenu());
frame.setContentPane(battlegui.createContentPane());
*/
// Display the window, setting the size
frame.setSize(800, 600);
frame.setVisible(true);
}
/**
* Sets a Gui grid square at row, col to display a character
*/
public boolean setGuiSquare(int row, int col, char c)
{
int bnum = row * GRID_SIZE + col;
if (bnum >= (GRID_SIZE*GRID_SIZE))
{
return false;
}
else
{
buttonArray[bnum].setText(Character.toString(c));
}
return true;
}
/**
* This is a standard main function for a Java GUI
*/
public static void main(String[] args)
{
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
//Deploy();
}
});
}
//************************************************************************
//*** BATTLEGUI: Modify the methods below to respond to Menu and Mouse click events
/**
* This method is called from the Menu event: New Game.
* BATTLEGUI
*/
public void NewGame()
{
System.out.println("New game selected");
}
/**
* This method is called from the Menu event: Load Game.
* BATTLEGUI
*/
public void LoadGame()
{
System.out.println("Load game selected");
}
/**
* This method is called from the Menu event: Save Game.
* BATTLEGUI
*/
public void SaveGame()
{
System.out.println("Save game selected");
}
/**
* This method is called from the Mouse Click event.
* BATTLEGUI
*/
public void fireShot(int row, int col)
{
System.out.println("Fire shot selected: at (" + row + ", " + col + ")");
}
}
I would suggest, take a step back and think about the problem domain.
You have a BattleScene, which contains BattleSquares. Each battleSquare can have atmost 1 ship, and can have a color. You also have Ship objects (which can belong to a particular player, indicates if it is damaged or not)...
BattleSquare needs to decide if it is a Hit or Miss, because it has all the information. It knows wether it has a ship or not.
/**true if had a ship, false if it was a miss
*/
public class BattleSquare{
public boolean processHit(){
if (hasShip()){
ship.setState(DESTROYED);
return true;
}
return false;
}
public void setShip(Ship ship){ .... }
public boolean hasShip() { ... } } ... methods for color too
If you isolate your code into manageable snippets, where some classes represent the model, you will be able to manage things better. You appear to be mixing everything in one class and hence are feeling lost.
Similarly, your BattleScene will contains a List of BattleSquares. Once you fire, you can individuall seek a particular BattleSquare and tell it to process itself. If it is a hit, you update the state.
Idea is that your model classes only are responsible for managing state. Your controller classes can fire events, which are intercepted by views, who update the models and refresh themselves.
hopefully it helps.
Related
I am trying to make a java simulation of a train moving from station to station. I have the main code working but having trouble with the GUI. I have the basic layout with a start and stop button but as soon as the start button is selected, the main loop runs for the simulation and the GUI doesn't respond. I've been having trouble finding how to work around this. And help wpuld be much appreciated!
Here is the main simulation class:
/**
* Here is the main simulation class that runs the main loop.
* It uses instances of two classes : train and station.
* #author Ollie Jones
*
*/
public class SimEngine
{
/**
* Station object array and train object initialised.
* The line has 16 station so an array of 16 is needed.
*/
Station[] station = new Station[16];
Train train = new Train();
int forwardTimeArray[];
int backwardTimeArray[];
/**
* Constructor for objects of class SimEngine
*/
public SimEngine()
{
/**
* Here that values are initialised.
*/
train = new Train();
forwardTimeArray = new int[]{1,4,2,4,6,3,3,5,3,2,5,5,1,4,2};
backwardTimeArray = new int[]{3,2,4,5,2,2,4,3,4,2,5,2,1,4,5};
// A for loop is used to initialse the station number
for(int i=0; i<station.length; i++){
station[i] = new Station();
station[i].setStationNumber(i+1);
}
/**
* Each station name is initialised separately as
* they all have different names.
*/
station[0].setStationName("Name of 1st station");
station[1].setStationName("Name of 2nd station");
station[2].setStationName("Name of 3rd station");
station[3].setStationName("Name of 4th station");
station[4].setStationName("Name of 5ht station");
station[5].setStationName("Name of 6th station");
station[6].setStationName("Name of 7th station");
station[7].setStationName("Name of 8th station");
station[8].setStationName("Name of 9th station");
station[9].setStationName("Name of 10th station");
station[10].setStationName("Name of 11th station");
station[11].setStationName("Name of 12th station");
station[12].setStationName("Name of 13th station");
station[13].setStationName("Name of 14th station");
station[14].setStationName("Name of 15th station");
station[15].setStationName("Name of 16th station");
}
/**
* An example of a method - replace this comment with your own
*
* #param y a sample parameter for a method
* #return the sum of x and y
*/
/**
* This method stats the train simulation.
*
*/
public void start()
{
int x = 0;
System.out.println("Station Number:1"); //Print the first staion number.
while(x == 0){
int stationNumber = 0;
int time = 0;
Boolean forwards;
stationNumber = train.getStationNumber();
forwards = train.getDirection();
if (forwards == true){
time = forwardTimeArray[stationNumber-1];
sleep(time);
stationNumber = stationNumber + 1;
System.out.println("Station Nubmer:" + stationNumber);
train.setStationNumber(stationNumber);
}
else{
time = backwardTimeArray[stationNumber-2];
sleep(time);
stationNumber = stationNumber - 1;
System.out.println("Station Number:" + stationNumber);
train.setStationNumber(stationNumber);
}
if (stationNumber == 1){
forwards = true;
}
else if (stationNumber == 16){
forwards = false;
//train.setStationNumber(stationNumber-1);
}
train.setDirection(forwards);
}
}
public static void sleep(int time)
{
try{
time = time * 100;
Thread.sleep(time);
}
catch(Exception e) {}
}
public void stop()
{
System.exit(0);
}
}
Here is the sim class where the simulation is started.
public class Sim
{
private GUI gui;
private SimEngine engine;
/**
* Constructor for objects of class sim
*
*/
public Sim()
{
engine = new SimEngine();
gui = new GUI(engine);
}
/**
* Opens window if it has been closed.
*/
public void show()
{
gui.setVisable(true);
}
}
Here is the GUI, where the main issue is (i think).
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class GUI extends JFrame
{
// instance variables - replace the example below with your own
private JFrame frame;
private JTextField display;
private final SimEngine sim;
private JLabel infoLabel;
/**
* Constructor for objects of class GUI
*/
public GUI(SimEngine engine)
{
// initialise instance variables
makeFrame();
frame.setVisible(true);
sim = engine;
}
/**
* Creates GUI frame!
*/
public void makeFrame()
{
frame = new JFrame("Train Simulation");
JPanel contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(new BorderLayout(8,8));
contentPane.setBorder(new EmptyBorder(10,10,10,10));
display = new JTextField();
contentPane.add(display, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new GridLayout(1,2));
addButton(buttonPanel, "Start", () -> sim.start());
addButton(buttonPanel, "Stop", () -> sim.stop());
contentPane.add(buttonPanel, BorderLayout.CENTER);
frame.pack();
}
private void addButton(Container panel, String buttonText, ButtonAction
action)
{
JButton button = new JButton(buttonText);
button.addActionListener(e -> {action.act(); redisplay(); });
panel.add(button);
}
private interface ButtonAction
{
/**
* act on button press.
*/
void act();
}
private void redisplay()
{
}
/**
* Makes frame visable.
*/
public void setVisable(boolean visable){
frame.setVisible(visable);
}
}
You're running your simulation on the event dispatch thread. While your calculations are happening, they are monopolizing the thread that handles UI events, so it can't process anything and the UI freezes.
Use worker threads.
It looks like you have this usecase (from the linked tutorial):
The background task can provide intermediate results by invoking SwingWorker.publish, causing SwingWorker.process to be invoked from the event dispatch thread.
As explained in the comments and in this answer, running long processes on the The Event Dispatch Thread blocks it, so it does not respond to changes.
One alternative is to use a SwingWorker which does the background processing in its doInBackground() method, publishes interim values (publish method) and is capable of updating gui (process method).
The following is a basic implementation of a SwingWorker, based on your code.
It can be copy-pasted into one file (GUI.java) and run.
Note the comments in the code:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public class GUI extends JFrame
{
// instance variables - replace the example below with your own
private JFrame frame;
private JTextField display;
private final SimEngine sim;
private JLabel infoLabel;
/**
* Constructor for objects of class GUI
*/
public GUI(SimEngine engine)
{
// initialize instance variables
makeFrame();
frame.setVisible(true);
sim = engine;
}
/**
* Creates GUI frame!
*/
public void makeFrame()
{
frame = new JFrame("Train Simulation");
JPanel contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(new BorderLayout(8,8));
contentPane.setBorder(new EmptyBorder(10,10,10,10));
display = new JTextField();
contentPane.add(display, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new GridLayout(1,2));
addButton(buttonPanel, "Start", () -> sim.start());
addButton(buttonPanel, "Stop", () -> sim.stop());
contentPane.add(buttonPanel, BorderLayout.CENTER);
frame.pack();
}
private void addButton(Container panel, String buttonText, ButtonAction action)
{
JButton button = new JButton(buttonText);
button.addActionListener(e -> {action.act(); redisplay(); });
panel.add(button);
}
void updateDisplay(String newValue){
display.setText(newValue);
}
private interface ButtonAction
{
/**
* act on button press.
*/
void act();
}
private void redisplay() { }
/**
* Makes frame visible.
*/
public void setVisable(boolean visable){
frame.setVisible(visable);
}
public static void main(String[] args) {
Sim sim = new Sim();
sim.show();
}
}
//implements Listener so it can listen to SimEngine value changes
class Sim implements Listener
{
private final GUI gui;
private final SimEngine engine;
public Sim()
{
engine = new SimEngine(this);
gui = new GUI(engine);
}
/**
* make gui visible
*/
public void show()
{
gui.setVisable(true);
}
#Override
public void valueChanged(String newValue){
gui.updateDisplay(newValue);
}
}
class SimEngine {
/**
* Station object array and train object initialized.
* The line has 16 station so an array of 16 is needed.
*/
private final Station[] station = new Station[16];
private Train train = new Train();
private final int forwardTimeArray[], backwardTimeArray[];
private final Listener listener;
//accept a listener
public SimEngine(Listener listener)
{
this.listener = listener;
train = new Train();
forwardTimeArray = new int[] {1,4,2,4,6,3,3,5,3,2,5,5,1,4,2,3}; //needs 16 values, had only 16
backwardTimeArray = new int[]{3,2,4,5,2,2,4,3,4,2,5,2,1,4,5,4};
// A for loop is used to initialize the station number and name
for(int i=0; i<station.length; i++){
station[i] = new Station();
station[i].setStationNumber(i+1);
station[0].setStationName("Station #"+(i+1));
}
}
/**
* This method starts the train simulation.
*
*/
public void start()
{
//have all background processing done by a SwingWorker so GUI does not freeze
new SimulationWorker().execute();
}
public static void sleep(int time)
{
try{
Thread.sleep(time * 300);
}
catch(Exception e) {}
}
public void stop()
{
System.exit(0);
}
class SimulationWorker extends SwingWorker<Void,Integer>{
boolean stop = false; //use if you wish to stop
//do background processing
#Override
protected Void doInBackground() throws Exception {
while(! stop){
int stationNumber = 0;
int time = 0;
boolean forwards;
stationNumber = train.getStationNumber();
forwards = train.getDirection();
if (stationNumber == 1){
forwards = true;
train.setDirection(forwards);
}
else if (stationNumber == 15){
forwards = false;
train.setDirection(forwards);
}
if (forwards == true){
time = forwardTimeArray[stationNumber+1];//time = forwardTimeArray[stationNumber-1];
sleep(time);
stationNumber = stationNumber + 1;
//System.out.println("Station Number:" + stationNumber);
train.setStationNumber(stationNumber);
}
else{
time = backwardTimeArray[stationNumber-2];
sleep(time);
stationNumber = stationNumber - 1;
//System.out.println("Station Number:" + stationNumber);
train.setStationNumber(stationNumber);
}
publish(train.getStationNumber()); //publish result (station number)
}
return null;
}
//process published information
#Override
protected void process(List<Integer> stationsList) {
for(int stationNumber : stationsList){
listener.valueChanged("Train is at "+ stationNumber); //notify listener
}
}
}
}
class Train {
private int stationNumber = 0;
private boolean forwards = true ;
public int getStationNumber() {
return stationNumber;
}
public void setDirection(boolean forwards) {
this.forwards = forwards;
}
public void setStationNumber(int stationNumber) {
this.stationNumber = stationNumber;
}
public boolean getDirection() {
return forwards;
}
}
class Station {
private int stationNumber;
private String stationName;
public void setStationNumber(int stationNumber) {
this.stationNumber = stationNumber;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
}
//an interface use by Sim to listen to SimEngine changes
interface Listener {
void valueChanged(String newValue);
}
I'm trying to make a simple connect four GUI by using a grid of JPanels each of which paints a colored disk when the button below them is pressed and the panels under it are full. Before adding the game rules I'm trying to just make sure the buttons and display work properly. But it is not working - only the top left panel displays a disk (after pressing button 1 6 times). here is my code:
public class ConnectFourFrame extends JFrame {
private final JPanel gamePanelsPanel; // panel to hold the game panels
private final GamePanel[][] gamePanels; // a 2D array to hold the grid of panels to display the game disks
private final JPanel buttonsPanel; // panel to hold the buttons panels
private final JPanel gameButtonsPanel; // panel to hold the game buttons to add disk to a column
private final JButton[] gameButtons; // an array to hold the game buttons
private final JPanel clearButtonPanel; // panel to hold the clear button
private final JButton clearButton; // button to clear the game grid from disks
private enum Turn {RED_PLAYER, BLUE_PLAYER}; // keeps track of which players turn it is
private Turn turn;
// no argument constructor
public ConnectFourFrame() {
super("Connect Four");
this.setLayout(new BorderLayout());
//add panels to hold the game panel and the buttons
gamePanelsPanel = new JPanel();
add(gamePanelsPanel, BorderLayout.CENTER);
buttonsPanel = new JPanel();
buttonsPanel.setLayout(new BorderLayout());
add(buttonsPanel, BorderLayout.SOUTH);
//set up game panels
gamePanelsPanel.setLayout(new GridLayout(6,7,3,3));
gamePanelsPanel.setBackground(Color.BLACK);
gamePanels = new GamePanel[6][7];
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
gamePanels[i][j] = new GamePanel(false, Color.WHITE);
gamePanelsPanel.add(gamePanels[i][j]);
}
}
//set up game and clear buttons
gameButtonsPanel = new JPanel();
gameButtonsPanel.setLayout(new GridLayout(1,7));
clearButtonPanel = new JPanel();
gameButtons = new JButton[7];
for (int i = 0; i < 7; i++) {
gameButtons[i] = new JButton("" + (i+1));
gameButtonsPanel.add(gameButtons[i]);
}
clearButton = new JButton("CLEAR");
clearButtonPanel.add(clearButton);
buttonsPanel.add(gameButtonsPanel, BorderLayout.NORTH);
buttonsPanel.add(clearButtonPanel, BorderLayout.SOUTH);
add(buttonsPanel, BorderLayout.SOUTH);
// register event handlers
ClearButtonHandler clearButtonHandler = new ClearButtonHandler();
clearButton.addActionListener(clearButtonHandler);
GameButtonHandler gameButtonHandler = new GameButtonHandler();
for (int i = 0; i < 7; i++) {
gameButtons[i].addActionListener(gameButtonHandler);
}
turn = Turn.RED_PLAYER; //set first turn to player1
}
// inner class for game button event handling
private class GameButtonHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// get the number of the pressed button
int pressedButtonNum = Integer.parseInt(((JButton) e.getSource()).getActionCommand());
// display disk in top empty panel of the column
for (int i = 5; i >= 0; i--) {
if (!gamePanels[i][pressedButtonNum - 1].isFull()) {
if (turn == Turn.RED_PLAYER) {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.RED);
turn = Turn.BLUE_PLAYER;
}
else {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.BLUE);
turn = Turn.RED_PLAYER;
}
gamePanels[i][pressedButtonNum - 1].setFull(true);
gamePanels[i][pressedButtonNum - 1].repaint();
return;
}
}
// if column is full display message to try again
JOptionPane.showMessageDialog(gamePanelsPanel, "Column " + pressedButtonNum + " is full. Try again.");
}
}
public class GamePanel extends JPanel{
private boolean isFull; // true if the panel has a disk in it. default is empty (false).
private Color diskColor; //color of disks. default is white (same as background)
public GamePanel(boolean isFull, Color diskColor) {
super();
this.isFull = isFull;
this.diskColor = diskColor;
}
public Color getDiskColor() {
return diskColor;
}
public void setDiskColor(Color diskColor) {
this.diskColor = diskColor;
}
public boolean isFull() {
return isFull;
}
public void setFull(boolean isFull) {
this.isFull = isFull;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(diskColor);
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}
}
The problem is right here...
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
The Graphics context passed to your paintComponent method has already been translated by the components x/y position, meaning that the top/left corner of the component is always 0x0
g.fillOval(this.getWidth()/4 , this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
will probably work better
Also, calling this.setBackground(Color.WHITE); inside paintComponent is unadvisable, as it will setup a situation where by a new paint cycle will be scheduled, over and over again. Don't change the state of the UI from within a paint method
im trying to add a double value that represents the stamina of my player into a jTextArea after i click my north button cant seem to do it heres my code:
private void northButtonActionPerformed(java.awt.event.ActionEvent evt)
{
game.playerMove(MoveDirection.NORTH);
update();
double playerStamina = player.getStaminaLevel();
//tried this
String staminaLevel = Double.toString(playerStamina);
jTextArea1.setText("Stamina: " + staminaLevel);
}
im new here sorry if this is not right
heres the main method
public class Main
{
/**
* Main method of Lemur Island.
*
* #param args the command line arguments
*/
public static void main(String[] args)
{
// create the game object
final Game game = new Game();
// create the GUI for the game
final LemurIslandUI gui = new LemurIslandUI(game);
// make the GUI visible
java.awt.EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
gui.setVisible(true);
}
});
}
and heres the class
public class LemurIslandUI extends javax.swing.JFrame
{
private Game game;
private Player player;
/**
* Creates a new JFrame for Lemur Island.
*
* #param game the game object to display in this frame
*/
public LemurIslandUI(final Game game)
{
this.game = game;
initComponents();
createGridSquarePanels();
update();
}
private void createGridSquarePanels() {
int rows = game.getIsland().getNumRows();
int columns = game.getIsland().getNumColumns();
LemurIsland.removeAll();
LemurIsland.setLayout(new GridLayout(rows, columns));
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < columns; col++)
{
GridSquarePanel panel = new GridSquarePanel(game, row, col);
LemurIsland.add(panel);
}
}
}
/**
* Updates the state of the UI based on the state of the game.
*/
private void update()
{
for(Component component : LemurIsland.getComponents())
{
GridSquarePanel gsp = (GridSquarePanel) component;
gsp.update();
}
game.drawIsland();
}
Your class doesn't seem to be implmeneting ActionListener, therefore the action on your button will not be triggered.
Your class declaration should be:
public class LemurIslandUI extends javax.swing.JFrame implements ActionListener
And put the code for your button action inside:
public void actionPerformed(ActionEvent e) {}
Alternatively, you can use an anonymous class to implement the code for your button, instead of making your class implement the ActionListener. Something like:
final JButton button = new JButton();
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionevent)
{
//code
}
});
Try this.
jTextArea1.setText("Stamina: " + player.getStaminaLevel());
Using anything + string does auto casting to string.
Want to create a JMenuBar. If window-JFrame.width to small to show all JMenu of JMenuBar, a Button appears in JMenuBar and all hidden JMenu can chosen in a drop-down list. How can I realize it, please?
I'd look at JToolBar, illustrated here. You can use any required layout and most L&Fs allow the bar to become a floating window.
Use CardLayout to have a panel that contains both the normal menu, and a menu implemented with the button. Then add a ComponentListener (ComponentAdapter) to it and select the desired menu implementation in the listener's componentResized() method.
In code it would look roughly like this:
JMenuBar createCustomMenu() {
final CardLayout layout = new CardLayout();
final JMenuBar menu = new JMenuBar();
menu.setLayout(layout);
// Here you should create the normal, wide menu
JComponent normalMenu = createNormalMenu();
// Here you create the compressed, one button version
JComponent compressedMenu = createCompressedMenu();
menu.add(normalMenu, "normal");
menu.add(compressedMenu, "compressed");
menu.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (menu.getPreferredSize().getWidth() > menu.getWidth()) {
layout.show(menu, "compressed");
} else {
layout.show(menu, "normal");
}
}
});
return menu;
}
(edit: changed to return JMenuBar, since it seems to work just fine)
Here is some old code I was playing with 5 years ago. Its been so long I don't even remember how well the code works. It was designed for a JToolBar but it may give you some ideas on how to do this with a JMenuBar:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
*
* #author subanark
*/
public class PopupMenuLayout implements java.awt.LayoutManager
{
private JPopupMenu popupMenu = new JPopupMenu();
private JButton popupButton = new JButton(new PopupAction());
/** Creates a new instance of PopupMenuLayout */
public PopupMenuLayout()
{
}
/** If the layout manager uses a per-component string,
* adds the component <code>comp</code> to the layout,
* associating it
* with the string specified by <code>name</code>.
*
* #param name the string to be associated with the component
* #param comp the component to be added
*/
public void addLayoutComponent(String name, Component comp)
{
}
/**
* Lays out the specified container.
* #param parent the container to be laid out
*/
public void layoutContainer(Container parent)
{
// Position all buttons in the container
Insets insets = parent.getInsets();
int x = insets.left;
int y = insets.top;
System.out.println("bottom: " + insets.bottom);
int spaceUsed = insets.right + insets.left;
for (int i = 0; i < parent.getComponentCount(); i++ )
{
Component aComponent = parent.getComponent(i);
aComponent.setSize(aComponent.getPreferredSize());
aComponent.setLocation(x,y);
int componentWidth = aComponent.getPreferredSize().width;
x += componentWidth;
spaceUsed += componentWidth;
}
// All the buttons won't fit, add extender button
// Note: the size of the extender button changes once it is added
// to the container. Add it here so correct width is used.
int parentWidth = parent.getSize().width;
if (spaceUsed > parentWidth)
{
popupMenu.removeAll();
parent.add(popupButton);
popupButton.setSize( popupButton.getPreferredSize() );
int popupX = parentWidth - insets.right - popupButton.getSize().width;
popupButton.setLocation(popupX, y );
spaceUsed += popupButton.getSize().width;
}
// Remove buttons that don't fit and add to the popup menu
// System.out.println(spaceUsed + " ::: " + parentWidth);
int lastVisibleButtonIndex = 1;
while (spaceUsed > parentWidth)
{
lastVisibleButtonIndex++;
int last = parent.getComponentCount() - lastVisibleButtonIndex;
Component component = parent.getComponent( last );
component.setVisible( false );
spaceUsed -= component.getSize().width;
addComponentToPopup(component);
// popupButton.setLocation( button.getLocation() );
// System.out.println(spaceUsed + " : " + parentWidth);
}
}
private void addComponentToPopup(Component component)
{
System.out.println(component.getClass());
if (component instanceof JButton)
{
JButton button = (JButton)component;
JMenuItem menuItem = new JMenuItem(button.getText());
menuItem.setIcon( button.getIcon() );
ActionListener[] listeners = button.getActionListeners();
for (int i = 0; i < listeners.length; i++)
menuItem.addActionListener( listeners[i] );
popupMenu.insert(menuItem, 0);
}
if (component instanceof JToolBar.Separator)
{
popupMenu.insert( new JSeparator(), 0);
}
}
/**
* Calculates the minimum size dimensions for the specified
* container, given the components it contains.
* #param parent the component to be laid out
* #see #preferredLayoutSize
*/
public Dimension minimumLayoutSize(Container parent)
{
return popupButton.getMinimumSize();
}
/** Calculates the preferred size dimensions for the specified
* container, given the components it contains.
* #param parent the container to be laid out
*
* #see #minimumLayoutSize
*/
public Dimension preferredLayoutSize(Container parent)
{
// Move all components to the container and remove the extender button
parent.remove(popupButton);
/*
while ( popupMenu.getComponentCount() > 0 )
{
Component aComponent = popupMenu.getComponent(0);
popupMenu.remove(aComponent);
parent.add(aComponent);
}
*/
// Calculate the width of all components in the container
Dimension d = new Dimension();
d.width += parent.getInsets().right + parent.getInsets().left;
for (int i = 0; i < parent.getComponents().length; i++)
{
Component component = parent.getComponent(i);
component.setVisible( true );
d.width += component.getPreferredSize().width;
d.height = Math.max(d.height, component.getPreferredSize().height);
}
// d.height += parent.getInsets().top + parent.getInsets().bottom + 5;
d.height += parent.getInsets().top + parent.getInsets().bottom;
return d;
}
/** Removes the specified component from the layout.
* #param comp the component to be removed
*/
public void removeLayoutComponent(Component comp)
{
}
protected class PopupAction extends AbstractAction
{
public PopupAction()
{
super(">>");
}
public void actionPerformed(ActionEvent e)
{
JComponent component = (JComponent)e.getSource();
popupMenu.show(component,0,component.getHeight());
}
}
public static void main(String[] argv)
{
ActionListener simpleAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println(e.getActionCommand());
}
};
JToolBar toolBar = new JToolBar();
toolBar.setLayout(new PopupMenuLayout());
toolBar.add( createButton("one", simpleAction) );
toolBar.add( createButton("two", simpleAction) );
toolBar.add( createButton("three", simpleAction) );
toolBar.add( createButton("four", simpleAction) );
toolBar.add( createButton("five", simpleAction) );
toolBar.add( createButton("six", simpleAction) );
toolBar.addSeparator();
toolBar.add( createButton("seven", simpleAction) );
toolBar.add( createButton("eight", simpleAction) );
toolBar.addSeparator();
toolBar.add( createButton("nine", simpleAction) );
toolBar.add( createButton("ten", simpleAction) );
JFrame f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(toolBar,BorderLayout.NORTH);
f.setBounds(300,200,400,300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static JButton createButton(String text, ActionListener listener)
{
JButton button = new JButton(text);
button.addActionListener( listener );
return button;
}
}
In this case the toolbar button was converted to a JMenu when no space was available. In you case you already have a JMenu, so you should be able to juse move the JMenu from the JMenuBar to the popup menu. However you will need to change the code to always move the menus from the popup menu back to the menu bar before determining the preferred size of the menu bar.
Right now i change the background color of a button by using
button.setBackground(Color.WHITE);
That being an example.
But when i have a massive grid out of jbuttons (1000+), just running a for loop to change every buttons background is very, very slow. You can see the grid slowly turning white, box by box. I really don't want this
Is there a better way of changing every JButton on the grid to the same color at the same time?
This is how i am making the grid, the numbers used are only for example...
grid = new JPanel(new GridLayout(64, 64, 0, 0));
That's 4096 buttons, takes about 30+ seconds to change every button to the same color.
Edit 1: I need the buttons to be clickable, like when i click a button it turns blue for example. when all of the buttons are clicked, change the color of every button to white. Right now i have that working fine, but it is just slow to change the color of every button.
Edit 2: this is how i am changing the buttons:
new javax.swing.Timer(300, new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent e) {
if (counter >= counterMax) {
((Timer) e.getSource()).stop();
}
Color bckgrndColor = (counter % 2 == 0) ? flashColor : Color.white;
for (JButton button : gridButton) {
button.setBackground(bckgrndColor);
}
counter++;
}
}).start();
The fact that you see the boxes being repainted individually indicates that either double buffering is turned off, or that the paint code in the button UI makes use of paintImmediately().
I tested your setup with 64x64 JButtons, an made sure that all UI operations were executed in the EDT (Event Dispatch Thread). I can confirm the effect you saw, changing the background of all buttons took about 1200 ms, with every box repainted immediately.
You can bypass the immediate repaints by setting the grid to non-visible before, and to visible after you changed the backgrounds:
grid.setVisible(false);
for (Component comp : grid.getComponents()) {
comp.setBackground(color);
}
grid.setVisible(true);
This caused the grid to do only one repaint, and reduced the time to ~300ms (factor 4).
This is still too slow for frequent updates, so you're better off with a custom component which draws the grid, or a flyweight container (what trashgod suggested in the comment to your question) if you want allow the grid cells to be arbitrary components.
You can get a considerable benefit if only visible buttons need to be repainted. In the MVC approach shown below, each button listens to a model that defines it's current state. Updating the model is quite fast compared to repainting. Although startup takes a few seconds, I see updates taking < 10 ms. in the steady-state. It's not as scalable as the flyweight pattern used by JTable, illustrated here, but it may serve.
import java.awt.*;
import java.awt.event.*;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import javax.swing.*;
/** #see https://stackoverflow.com/questions/6117908 */
public class UpdateTest {
private static final int ROW = 64;
private static final int COL = 64;
private static final int MAX = COL * ROW;
private final DataModel model = new DataModel(MAX);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new UpdateTest().create();
}
});
}
void create() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(ROW, COL));
for (int i = 0; i < MAX; i++) {
panel.add(new ViewPanel(model, i));
}
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long start = System.nanoTime();
model.update();
System.out.println(
(System.nanoTime() - start) / (1000 * 1000));
}
});
JFrame f = new JFrame("JTextTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(panel), BorderLayout.CENTER);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
timer.start();
}
private static class ViewPanel extends JPanel implements Observer {
private final JButton item = new JButton();
private DataModel model;
private int index;
public ViewPanel(DataModel model, int i) {
this.model = model;
this.index = i;
this.add(item);
item.setText(String.valueOf(i));
item.setOpaque(true);
item.setBackground(new Color(model.get(index)));
model.addObserver(this);
}
#Override
public void update(Observable o, Object arg) {
int value = model.get(index);
item.setBackground(new Color(value));
}
}
private static class DataModel extends Observable {
private final Random rnd = new Random();
private final int[] data;
public DataModel(int n) {
data = new int[n];
fillData();
}
public void update() {
fillData();
this.setChanged();
this.notifyObservers();
}
public int get(int i) {
return data[i];
}
private void fillData() {
for (int i = 0; i < data.length; i++) {
data[i] = rnd.nextInt();
}
}
}
}