Adding Objects to JFrame from another class? - java

I'm trying to create a blackjack program for my final project in Java. I'm still very new to Java and OOD so I apologize if my problem seems very trivial to you :(
How my program works: I have three classes so far.
main.java
This class builds my frame and runs all the other methods.
cards.java
This class creates an array that holds the card values and location to picture. I have a for loop in there that auto-populates it.
hits.java
This class is meant to "randomly" generate a number that will represent the chosen card. The way this works is by taking the randomly created int and pointing it to a matching index location on the array.
I assign the value to string objects that I then try to add to a jlabel and then add that jlabel to my main frame. The code is as follows:
hits.java
// Import necessary classes.
import java.util.Random;
public class hits {
// Create random object.
Random rand = new Random();
// Declare variables.
int card;
String cardVal, cardPic;
// Instantiate the needed classes.
main s = new main();
cards t = new cards();
// Constructor for the class.
public hits() {
// Randomly generate a number (0 - 9).
card = rand.nextInt(10);
// Populate the array.
t.runCards();
// Assign the cards according to the num. generated.
cardVal = t.deck[card][0];
cardPic = t.deck[card][1];
}
// Run Method
public void runHits() {
// Add the card chosen to the GUI.
s.a.setText("hello");
s.dealerCards.add(s.a);
}
}
I have "hello" as the text for the label because I wanted to see if perhaps my array was not populating, but even that doesn't work. If it helps here is my main.java as well (constructor and main method):
// Constructor for the main class.
public main() {
// Setup the MAIN container.
f1.getContentPane().setLayout(new GridLayout(0, 1));
f1.setSize(200, 200);
f1.add(dealerName);
f1.add(dealerCards);
f1.add(userCards);
f1.add(userName);
// Setup the inner panels.
dealerCards.setLayout(new GridLayout(1, 2));
dealerCards.add(b);
userCards.setLayout(new GridLayout(1, 6));
userCards.add(c);
userCards.add(d);
}
// Build the frame.
public void GUILaunch() {
// Display Frame
f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f1.setVisible(true);
}
// Main method.
public static void main(String args[]) {
// Distribute the dealer's/player's starting hands.
hits deal = new hits();
deal.runHits();
// Launch the GUI
main gui = new main();
gui.GUILaunch();
}
Hopefully I have provided enough information to help you understand what's going on here. So to sum it all up: how can i add my jlabel(from another class) holding the randomly selected card to my main frame
Thanks in advance.

The deal.runHits() adds a label to the Main object that deal owns rather than the gui object.
I would suggest the following :
Make your main class have an instance of hits and hits have an instance of the cards object...
so you get something like this
public class main {
private hits hits_instance
//constructor
main(){ hits_instance = new hits(); }
//this method will add your cards
public void addCards(){
// frame = whatever frame you are using
frame.add(hits_instance.getCards());
}
}
public class hits {
private cards cards_instance;
hits(){ cards_instance= new cards();}
public JLabel getCards() {return cards_instance.getCard(randomNumber);}
}

Related

Java - How do you avoid a StackOverflow after creating multiple objects?

I am currently working on my exam-project for uni. The task is to create the boardgame ludo. After writing some code and doing some testing, I ran into a StackOverflowError.
So here is the structure (5 classes are essential):
Main.class GUI.class Game.class Player.class Piece.class
Main creates a new object of type GUI called mainGUI. This creates the visuals for the game, including a small settings area with a start button.
On the press of the Start button, a new object of the type Game is created which then creates 4 new Objects of the type Player (4 players obviously).
When creating an object of type Player, this type gets the argument 'nmbr' which just states the Number of the Player (Player1, Player2 and so on).
Each Player has 4 pieces to move around on the board, so each of those 4 Players create another 4 objects of type Piece.
Now when pressing the Start button, what should happen is, that the pieces are displayed on the board. But that doesn't happen. Instead I get an errormessage stating, that there is a StackOverflowError on the call of the first Player object.
So I tried to read into the behaviour of Object creation in java and StackOverflow and stuff like that. But the only conclusion I can get right here, is that I've created too many objects inside one another.
public class Main {
public static void main(String[] args){
Main start = new Main();
start.activate();
}
static GUI mainGui;
public Main() {
mainGui = new GUI();
}
Inside the GUI there is the JButton 'submit'
This button is supposed to start the game by creating the an object of the type Game.
submit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object btn = e.getSource();
boolean ACTIVE = ((JButton) btn).isEnabled();
if (ACTIVE) {
submit.setVisible(false);
cancel.setVisible(true);
Game spiel = new Game();
spiel.begin();
}
}
});
public class Game {
public Game() {
}
Player player1 = new Player(1); //this is where the Error occurs
Player player2 = new Player(2);
Player player3 = new Player(3);
Player player4 = new Player(4);
}
etc.
public class Player extends Game {
private String name;
private Color color;
private boolean artiPlayer;
private int playerNmbr, start, end;
private int[] startArea, endArea;
Piece piece1, piece2, piece3, piece4;
public Player(int nmbr){
if (nmbr == 1) {
System.out.println("1.1");
piece1 = new Piece(500,175, Color.BLUE, Main.mainGui);
piece2 = new Piece(550, 175, Color.BLUE, Main.mainGui);
piece3 = new Piece(500,125, Color.BLUE, Main.mainGui);
piece4 = new Piece(550, 125, Color.BLUE, Main.mainGui);
start = 0;
end = 64;
}
}
Type Piece => Piece(xPos, yPos, Color, GUI)
//and so on
Here is the exact error message:
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at spielPackage.Player.<init>(Player.java:18)
at spielPackage.Game.<init>(Game.java:9)
Sorry if the code is a bit unclean. I am fairly new to java and this is still a work in progress.
At this point I am lost as to why Java throws a StackOverflowError
Your Player should not extend Game since a Player is not a Game.
Right now you create a game which creates four players with every one of them being a different "Game" and creating four more players which are games and create four new players each......

Passing information from an ArrayList from one class to a WindowsBuilder GUI class

I am to create an GUI that takes data I've read into an ArrayList, and perform actions on that data such as displaying, sorting and calculations.
My information is weather data.
In a class called "FileReading", I read my data from a csv into an ArrayList. I need to then pass this information to my JFrame GUI class named "WeatherGUI" and perform said actions on the data.
I am having trouble passing the information from my ArrayList over to my GUI class. As I've tested that it's reading data into the ArrayList fine, I won't include that code.
This is the relevant code I have in my WeatherGUI class and I'll describe the error below
public class WeatherGUI extends JFrame implements ActionListener {
private ArrayList<Weather> weather;
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
WeatherGUI frame = new WeatherGUI();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public WeatherGUI(ArrayList<Weather> weather) {
super("Weather");
this.weather = weather;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 692, 561);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
}
The error I'm having is in the try statement where WeatherGUI wants a parameter relating to my ArrayList and I'm not sure what to put here. If I put in "weather" it tells me to make weather static which I know isn't correct. The code I've added is what the lecturer provided in a slide but I still get the error.
Here:
WeatherGUI frame = new WeatherGUI();
You designed your class in a way that the WeatherGUI needs a List (prefer to use List over ArrayList within your method signatures!) at creation time. Which makes sense.
But that means: you have to read that List object before you create the GUI object, like:
WeatherGUI frame = new WeatherGUI(FileReader.readWeatherInfos());
( where readWeatherInfos() would have a signature like public static List<Weather> readWeatherInfos() for example). Or slightly different, something like:
List<Weather> tempWeather = new FileReader().readWeatherInfos();
WeatherGUI frame = new WeatherGUI(tempWeather);
( here assuming that your read method isn't static )
You are correct about not making anything static in your class. The weather field of your class is fully correct. But you simply can't get to that field before the WeatherGUI object has been instantiated!

How to transport information from one class to another in order to show it without using static?

I am programming an application that deals with orders from a database. It has several pages, a navigation, a header that always should show information about the actual order you are working with and a content area, in which the details of said order get shown:
My MainProgram extends a JFrame and contains a CardLayout, in which the other pages are hosted, so when the user clicks on the page in the navigation, only the view of the content-area changes. Logo, header and the navigation stay the same. The header keeps displaying the order number.
As there are several different pages that contain details about the same order, I need to "send / transfer" information about the order from one page to the other so I can show some information in the header and in the content area from the order object.
But I am not getting this to work as intended, mostly to my misunderstand of static and when to use it, where objects get created exactly and also the complexity of my program: I am using a class that is intended for the navigation and therefore should also handle
the information transfer from one page to the other.
Since I am using a database, creating a MVCE will be hard, so instead I will show the important parts of my program.
MainProgram.java
Here the navigation and the content panel (centerPanel) get created, also the CardLayout. centerPanel and the CardLayout are static, so I can call this from other classes and switch the page that is shown (probably not a good idea?):
NavigationPanel navigationPanel = new NavigationPanel();
public static JPanel centerPanel = new JPanel();
public static CardLayout contentCardsLayout = new CardLayout();
I create the pages and put them into my CardLayout:
OverviewPage overviewPage = new OverviewPage();
BasicDataPage basicDataPage = new BasicDataPage();
centerPanel.setLayout(contentCardsLayout);
overviewPage.setName("overviewPage");
basicDataPage.setName("basicDataPage");
centerPanel.add(overviewPage, "overviewPage");
centerPanel.add(basicDataPage, "basicDataPage");
The main method, where I create a MainProgram object:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
MainProgram window = new MainProgram();
window.setVisible(true);
window.initialize();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
OverviewPage.java
The overview page contains a JTable which gets populated from a database. If the user double-clicks an entry, he gets transfered to the BasicDataPage where he can see the details of the order.
But in order to show the details, I need to somehow transfer the information of the order object into the target class and thats the point I am struggling with!
// I tried several things like object into constructor, static object, creating a method etc...
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
BasicDataPage basicDataPage = new BasicDataPage();
basicDataPage.recieveOrderObject(orderObject);
workNumberPanel.recieveOrderObject(orderObject);
workNumberPanel.setTxtWorkNumber(workNumberOfOrderObject);
MainProgram.contentCardsLayout.show(MainProgram.centerPanel, "basicDataPage");
}
I tried "sending" the order object to the BasicDataPage via the constructor and set the text in the JTextFields in the BasicDataPage accordingly. This did not work, the textfields simply stayed empty altough I can System.out.println(orderObject.toString()) the recieved object.
BasicDataPage.java
I also tried creating a method receiveOrderObject that I use in the OverviewPage, which should set the textfields of the basicDataPage AND the workNumberPanel, but the fields stay empty:
WorkNumberPanel workNumberPanel = new WorkNumberPanel();
JTextField txtCarWidth = new JTextField(TEXTFIELD_LENGTH);
JTextField txtCarDepth = new JTextField(TEXTFIELD_LENGTH);
JTextField txtCarHeight = new JTextField(TEXTFIELD_LENGTH);
public void recieveOrderObject(OrderObject orderObject){
txtCarDepth.setText(orderObject.getCar_depth());
}
Before posting my question I've read several Q/As here on SO like this:
Accessing UUID from another class in Java ... suggesting to use static for global variables.
I know that static variables are class variables, that all instances can use and only one version exists of. So I tried to send a static object from one class to the other.
But since I am using JTextFields, I had to mix static and non-static content, which either did not work at all or the textfields disappeared.
I have the feeling that I am getting a very basic concept in java wrong, so any help, no matter in which direction, is appreciated!
EDIT:
Based on Reşit Dönüks answer, I was able to fill the textfields by making BasicDataPage and loadBasicData(orderObject) in MainProgram static. Now I can do MainProgram.loadBasicData(orderObject); ... and the textfields in the BasicDataPage get filled as intended.
Is this a valid approach or do I get problems for using static for GUI-Elements? ..... Don't!
I realized that, your are creating BasicDataPage in each double click.
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
BasicDataPage basicDataPage = new BasicDataPage();
This is the main problem. Do not create BasicDataPage there, just reach the created instance and set the order object to that. My solution is below.
public class MainProgram implements OrderView{
//remove statics here
private JPanel centerPanel = new JPanel();
private CardLayout contentCardsLayout = new CardLayout();
private BasicDataPage basicPage;
public MainProgram() {
//other codes
OverviewPage overviewPage = new OverviewPage();
basicPage = new BasicDataPage();
centerPanel.setLayout(contentCardsLayout);
overviewPage.setName("overviewPage");
basicDataPage.setName("basicDataPage");
centerPanel.add(overviewPage, "overviewPage");
centerPanel.add(basicPage, "basicDataPage");
//oher codes
}
#Override
public void loadOrder(OrderObject order) {
basicPage.recieveOrderObject(orderObject);
contentCardsLayout.show(centerPanel, "basicDataPage");
}
}
public interface OrderView {
public void loadOrder(OrderObject order);
}
public class OverviewPage {
OrderView orderView;
public OverviewPage(OrderView orderView) {
this.orderView = orderView;
}
//in ActionPerformed
if (mouseEvent.getClickCount() == 2 && row != -1) {
String workNumberOfOrderObject = (String) table.getValueAt(row, 0);
OrderObject orderObject = GetOrderObject.getOrderObjectFromDatabase(workNumberOfOrderObject);
orderView.loadOrder(orderObject);
workNumberPanel.recieveOrderObject(orderObject);
workNumberPanel.setTxtWorkNumber(workNumberOfOrderObject);
}
}
As pointed already, Singleton is the way to go. I would just like to point out a mistake in the code provided in the answer before.
private static MainFrameinstance = null;
Rename MainFrameinstance to instance or vice-versa; because the same variable is checked by the getInstance() method.

Methods from external class accessible but GUI Components not

I have a weird Problem with my Java GUI.
I can access the Methods in the Main Class from another Class but i cannot access the Swing Components.
Let me show you how i built the whole thing
Main Class:
public class GUI extends JFrame {
static Code c = new Code();
static Draw panel = new Draw();
JTextArea codelog;
JLabel lblFile;
...
...
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI frame = new GUI();
frame.create();
}
});
}
public void create() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1280,720);
...
...
contentPane = new JPanel();
setContentPane(contentPane);
contentPane.setBackground(Color.DARK_GRAY);
GridBagLayout gbl_contentPane = new GridBagLayout();
setResizable(false);
...
...
panel.setBackground(Color.BLACK);
gbc_panel.fill = GridBagConstraints.BOTH;
gbc_panel.gridx = 1;
gbc_panel.gridy = 1;
contentPane.add(panel, gbc_panel);
codelog = new JTextArea();
codelog.setEditable(true);
JScrollPane scrollPane_1 = new JScrollPane(codelog);
codelog.setLineWrap(true);
scrollPane_1.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
codelog.setVisible(true);
scrollPane_1.setVisible(true);
GridBagConstraints gbc_scrollPane_1 = new GridBagConstraints();
gbc_scrollPane_1.gridheight = 2;
gbc_scrollPane_1.gridwidth = 4;
gbc_scrollPane_1.fill = GridBagConstraints.BOTH;
gbc_scrollPane_1.gridx = 8;
gbc_scrollPane_1.gridy = 1;
contentPane.add(scrollPane_1, gbc_scrollPane_1);
...
...
}
public void refresh(){
panel.repaint();
}
}
I am using static Code c and static Draw panel to avoid multiple instances as i also have to create Objects of the Main class in other classes.
The other Class named Code
public class Code {
...
...
static GUI g = new GUI();
String test;
...
...
public void hpgl(){
g.codelog.append(test); // gives me nullPointerException !!
g.refresh // works
...
...
}
}
The Problem is that i can access the Methods of the Main Class (GUI) from other classes (such as Code) but i cannot access the Components (such as JTextArea).
The refresh() Method in the Main Class proves it. I can access the Method and in the Main Class the repaint() works. But if i try to repaint from another class using GUI.panel.repaint() it won't work because i would in that case access the panel directly from Code Class.
The Same goes for the JTextArea. I am trying to append codelog from Code but it won't let me do it. If i create a Method in Main Class which appends the Textarea and then call the Method from Code Class it works. But using g.codelog.append(test) gives me a Java null pointer exception
So i can access the Methods but i cannot access the Swing Components.
Can you guys please help me. I don't want to have to write an extra Method in the Main Class for every single Swing Component i want to modify.
Thank You
The UI which is visible on the screen is not the same UI you have created in your Code class. If you want Code to be able to access the UI properties, you will need to pass a reference of the GUI to it.
Having said that, I would be apposed to exposing the UI components directly to any class an instead provide getters and setters (where applicable) to provide access to the information been managed. This prevents rouge classes from making changes to the UI which it should be allowed to do (like remove components)
Depending on what you are doing, an Observer Pattern might be a better choice, where by Code is notified by GUI when something it might be interested in changes. If done through the use of interfaces, this will reduce the coupling between your classes and make it more flexible
Beware static is not a mechanism for providing cross object communication and should be avoid if at all possible, especially in something as dynamic as a GUI.
I was able to solve the Problem following MadProgrammer's Suggestion.
This is what i changed.
I have 3 Classes:
Main Class
Draw
Code
Main Class
public class GUI extends JFrame {
Draw panel = new Draw(this);
Code c = new Code(this);
...
...
}
Code Class
public class Code {
private GUI g;
private Draw b;
public Code(GUI g){
this.g = g;
}
...
...
}
Draw Class
public class Draw extends JPanel{
private GUI x;
private Code c;
public Draw(GUI x){
this.x = x;
}
...
...
}
I removed all the Static declarations. It is now working. I can access the Swing Components in the Main Class now.
Is this the Professional way to do it? or is there still room for improvement. This is the first time i used the passing reference way to do it. Until now i always used static Objects.
Thank You

How do I use an object reference as an argument if it is instantiated after the class taking the argument?

So I have this code:
package com.erikbalen.game.rpg;
import com.erikbalen.platform.*;
import javax.swing.JFrame;
public class World extends Engine {
public static void main(String[] args) {
Gui display = new Gui(/*takes a Player argument so i can get certain variables*/);
display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
display.setSize(300,220);
display.setVisible(true);
Player player1 = new Dps("ebalen", display);
Player player2 = new Healer("frankypanky", display);
}
}
package com.erikbalen.game.rpg;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Gui extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = -384241835772507459L;
private JLabel playerInfo;
private JTextField textField;
private final static String newline = "\n";
private JTextArea feed;
private JScrollPane scrollPane;
private Player player;
public Gui(Player currentPlayer) {
super("Erik's RPG");
this.player = currentPlayer;
setLayout(new FlowLayout());
playerInfo = new JLabel("<html>Health = " + currentPlayer.getHealth() + " | " + "Mana = " + currentPlayer.getMana() + "</html>");
playerInfo.setBorder(BorderFactory.createTitledBorder(currentPlayer.getName()));
textField = new JTextField(20);
textField.addActionListener(this);
feed = new JTextArea(5, 20);
scrollPane = new JScrollPane(feed);
feed.setEditable(false);
add(playerInfo);
add(feed);
add(textField);
add(scrollPane);
}
public void actionPerformed(ActionEvent textBox) {
String text = textField.getText();
this.player.chat(text);
}
public void printText(String text) {
feed.append(text + "\n");
feed.setCaretPosition(feed.getDocument().getLength());
}
}
My problem is that class Gui takes a Player as an argument and Player takes Gui as an argument. How do I let both objects take each other as arguments? Feel free to tell me if my code is inefficient.
Well, ideally you should try to break the circular dependency, but otherwise, you could:
Create the GUI, create the players by passing in the GUI, then add the players to the GUI
Create the players, create the GUI by passing in the players, then set the GUI for the players
Create the GUI within the player constructor:
Player(String name)
{
GUI gui = new GUi(this);
...
}
All of these are non-ideal:
You may well want your classes to be immutable, which makes the first two options nasty
Publishing the this reference before your constructor has finished executing has various issues, both in terms of thread safety and the memory model, as well as potentially allowing the GUI constructor to call back to the Player object before it's fully initialized.
This goes back to "try to break the dependency" - but if that's really impossible, I would probably favour the first option, without knowing anything else. It makes sense to be able to add players to a game - it makes less sense to set the GUI for a player after the fact, IMO.
Your stuck in what we call a circular dependency. This is almost everytime the result of a bad design.
There are still solutions, but there are not really pretty. For an elegant way, you should rethink your design.
Does the GUI really need the player ? Maybe you could create a method to set the player later on. If it's not possible, you could also create a setter in the player. You won't be able to set both at construction time.
Try giving the Gui class methods for updating what is displayed to the user/users. Make the directing code (main() for example) responsible for updating the Gui with the right information as events happen.
Neither Gui nor Player should have to take each other as constructor arguments - Gui should only be responsible for displaying information it is told to, and Player should only be a logical representation of a game piece. Event-driven functionality should be reserved for the directing code.

Categories