JComboBox is refering to old Frame while removeAllItems() - java

I tried to figure this out myself but I can't. I'm stuck at a strange problem.
I have a Java Program with multiple classes and forms (I use Intellij and the build in GUI-Creator). When I switch from one Screen to another I just call frame.setVisible(false); at the leafing window and frame.setVisible(true); at the window I want to show next.
On a Button Click I make this:
In Class 1:
if (e.getSource() == umschaltenButton) {
this.mainW.goToMainWindow();
logger.log(Level.INFO, "Switched Back to MainMenu");
frame.setVisible(false);
}
And here is the weird part.
In Class 2:
public void goToMainWindow() {
frame = tvElectronics.drawMainWindow(); // I get a new Frame with new Images and so on
frame.addMouseListener(al);
frame.add(BotomPanel); // in here is the JComboBox
frame.setSize(LENGTH, HEIGHT);
comboBox1.removeAllItems(); // Here it tryes to refere to the old frame before i made frame = tvElectronics.drawMainWindow();
Vector<String[]> content = tvElectronics.getContent();
for (int i = 0; i < tvElectronics.getAnz(); ++i) {
comboBox1.addItem((i + 1) + ". " + content.get(i)[3]);
}
comboBox1.setSelectedIndex(chanel);
frame.setVisible(true);
}
And so it tries to update the old frame from class2 which no longer exists because of the new one I just created. And so I have 2 frames open: one as I want it and one strange old frame form class2.
My problem is that I want bind my JComboBox to a new Frame and update it but it is still connected to the old one and that causes weird problems like jumping back in the function. I mean it is at the last line of goToMainWindow() and then it starts again at the first line.

First off you should avoid swapping JFrames as your program does since this is a very annoying GUI design. Please read The Use of Multiple JFrames, Good/Bad Practice? for more on this.
Next, it's impossible for us to tell what GUI view your JComboBox is associated with.
But having said that, it really shouldn't matter. Instead of doing what you're doing, I would give the display class that holds a JCombBox a public method that you call on the containing display class that clears the contained JComboBox's model or that places items in the model. This way, there will be no ambiguity as to which JComboBox you're referring to, and this way you avoid directly exposing a view's inner components.
As an aside, I try to gear my display or view classes towards creating JPanels, not JFrames as this will give my code much greater flexibility.
For example
// my display class
class Display1 {
private DefaultComboBoxModel<String> myModel = new DefaultComboBoxModel<>();
private JComboBox<String> myCombo = new JComboBox<>(myModel);
public void removeAllComboElements() {
myModel.removeAllElements();
}
public void addElement(String ele) {
myModel.addElement(ele);
}
}
Same for your Display2 class. Then you can call the correct method on the JComboBox that is held by the correct view/display.
This way, when you swap displays, perhaps by using a CardLayout, you can clear the JComboBox in the display that is being shown by calling its own method to clear its own combobox's model.

Related

Removing unspecific JLabel from JPanel

I am currently working on a Black Jack game in Java.
I am trying to work with Swing and I am stuck right now:
I made this method
private void addImage(String path) {
JLabel imgLabel = new JLabel();
imgLabel.setIcon(new ImageIcon(getClass().getResource(path)));
add(imgLabel);
display.pack();
}
I want to have a reset button to restart after a round ends, how can I remove these unspecific JLabels? Should I give them an identifier? If yes how?
Thanks in advance.
You can do this by creating a list that contains all the labels.
List<JLabel> labelsList = new ArrayList<JLabel>();
Make sure to declare this outside of the addImages method so that it can be accessed in other methods. Then, every time an image label is created you can add it to the list by using:
labelsList.add(imgLabel)
To remove the cards, you could implement a reset method that loops through the labels list and removes each component, making sure to also remove it from the labelsList:
public void resetLabels()
{
for (JLabel label : labelsList) {
remove(label);
labelsList.remove(label);
}
}
I think learning about collections would be very helpful in this case, keep up the good work!
EDIT: After a closer look, you shouldn't modify the list as you go through the loop, instead:
public void resetLabels()
{
for (JLabel label : labelsList) {
remove(label);
}
labelsList.clear();
}

Updating a jLabel

I have a simple GUI that has a jTextField that waits for the user to put in something. After a button is clicked, the program:
reads the input, saves it in a String variable;
opens a new GUI (that is in a separate class file), which contains an empty jLabel, and passes the String variable to it, changing the jLabel text to it.
The problem is that no matter how hard I try to reconfigure the code, adding things like repaint(), revalidate(), etc., the jLabel in the second GUI stays empty. Using a System.out.println(jLabel.getText()) reveals that the text value is indeed changed, but not displayed. How do I "refresh" this jLabel, so it'd show what I want it to? I'm aware I could add an event, though I don't want the user to click anything to refresh the GUI, the values should be there as it's initiated. I've read trough several similar posts, but found that the solutions don't work for me.
The code of first GUI's button click event:
private void sbuttonActionPerformed(java.awt.event.ActionEvent evt) {
errortext.setText("");
Search = sfield.getText();
Transl = hashes.find(Search);
if (Transl.equals("0")) errortext.setText("Word not found in database.");
else {
ws.run(Search, Transl); // <- this opens the second GUI, with two String parameters I want to display in the second GUI;
}
}
The code of the second GUI (activeword and translation are the jLabels that are giving me trouble.):
public void run(String Search, String Transl) {
WordScreen init = new WordScreen(); //initialise the second GUI;
init.setVisible(true);
activeword.setText(Search);
translation.setText(Transl);
}
Any reply is very welcome! Please ask me for more information about the code if necessary, I will make sure to reply as soon as possible!
Best solution: change WordScreen's constructor to accept the two Strings of interest:
From this:
public void run(String Search, String Transl) {
WordScreen init = new WordScreen(); //initialise the second GUI;
init.setVisible(true);
activeword.setText(Search);
translation.setText(Transl);
}
to this:
public void run(String search, String transl) {
WordScreen init = new WordScreen(search, transl);
init.setVisible(true);
}
Then in the WordScreen constructor use those Strings where needed:
public WordScreen(String search, String transl) {
JLabel someLabel = new JLabel(search);
JLabel otherLabel = new JLabel(transl);
// put them where needed
}
Note that I cannot create a comprehensive answer without your posting a decent MRE
As an aside, you will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

Handling events for a collection of JButtons

I'm attempting to create dynamic interface that essentially creates a grid of a growing number of panels that look similar to the picture below. I've got some rough code to achieve that. I have an issue in regards to handling events for the JButtons that i've added to an ArrayList. I'm aware that creating inner class event handlers is best practise, how would I handle events for a scaling number of buttons that are stored in an ArrayList?
Currently i've resulted to getting the objects source to achieve this.
Global Variables:
ArrayList<JButton> buttons = new ArrayList<JButton>();
Adding buttons to the panel:
for (int i = 0; i < 2; i ++) {
int xTotal = 150;
if (i == 0) {
xTotal = 132;
}
else {
xTotal = 308;
}
xTotal = xTotal + xTotal;
JButton currentButton = new JButton("+");
currentButton.setBounds(xTotal , 375, 45, 25);
currentButton.setFont(currentButton.getFont().deriveFont(14f));
currentButton.addActionListener(new AddHandler());
buttons.add(currentButton);
panel.add(currentButton);
}
Event handler:
class AddHandler implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == buttons.get(0)) {
System.out.println("hello");
}
else {
System.out.println("it worked");
}
}
}
It looks to me like you're creating more than just a collection of JButtons. Instead you appear to be wanting to create (and correct me if I'm wrong), a collection of images/text with JButton. This suggests that you want to create a class that encapsulates one (?) menu item, that holds a JLabel for the image, a JLabel perhaps for the text, perhaps other components, and a JButton that the user can press to select the menu item. This component would be a JPanel that (important here) uses layout managers to intelligently arrange all of its constituent components. I would recommend not using ActionListeners but rather create a separate stand along class that extends AbstractAction, that you can use to set each JButton's Action, either via the JButton's constructor or via its setAction(...) method. Then you could place a collection of these JPanels in a master JPanel that uses perhaps a GridLayout, and have it held in a JScrollPane.
The details of my suggestions and the code would of course depend on the details of your GUI and your current code.
Other "side" recommendations:
Don't use null layouts and setBounds(...). Often newbie Swing programmers feel that this is the easiest way to create complex GUI's, and in the short term it may be, but in the long term, when it comes time to maintain, upgrade and enhance your GUI, it isn't. Also the GUI's so created may look OK on one platform and screen resolution, but they look terrible on all others. Learn about and use the layout managers.
Maybe you want a collection of Actions or the JButtons, but I'm not sure you need this. If the button's Action knows what to do, then no collection is needed. The Action could have a constructor that passes in references to the name and price of the menu item.
Even though youve already accepted another answer, I want to share what I have on my mind.
The idea of my idea is to store a number (maybe an ID) on the button by creating a subclass of JButton.
(ID could be, perhaps the ID of the food item this button is currently linked to)
class FoodButton extends JButton{
long id;
public FoodButton(String text, long id){
super(text);
this.id = id;
}
//Perhaps more constructors
public long getId(){
return id;
}
}
Then writing a single actionListener that gets the source of the event (even though you seem to think thats bad practice, I think it makes sense in this example), Observe:
ActionListener al = new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
FoodButton btn = (FoodButton) e.getSource();
long id = btn.getId();
//Do something now that you know what button was clicked (id)
}
}
Let me note again that this actionListener is created before your loop, so you only need one, and you'll assign the same one to all your buttons.
Then inside your loop instead of creating a normal JButton, you create a FoodButton:
ActionListener al = new ActionListener(){...};
for (int i = 0; i < 2; i ++) {
//...
//Assigning id 'i' here, but you could pick something else
JButton currentButton = new FoodButton("+", i);
//...
currentButton.addActionListener(al);
buttons.add(currentButton);
panel.add(currentButton);
}
I hope this makes some sense to you.
Also, as someone who used to use null layouts too, once you get accustomed to using layoutmanagers, im sure you will like them.

Swing based Java game

this is a problem that I've been really struggling with
I need to create a game for some University coursework. The game is basically a 4x4 grid. When i click a button, the player should move randomly within 1 square of it's location.
There's enemy spaceships that are generated randomly over the grid, and if the player lands on the spaceship, it destroys it. if there's two enemy ships on the same sqaure and the player lands on that square, the player is destroyed.
I've created a GIU using gridlayout and the button that I need to move the player, but I'm having problems trying to figure out how to actually use the grid. I've created an element class to create objects for the grid (ie, a list of enemy ships), but I don't know how to use this class with my gridlayout. I know I also have to redraw the grid after each move to reflect the changes, but I don't know if I would need to create a custom redraw method for this.
Any push in the correct direction so I can get my program actually doing something would be really helpful.
Some time ago I created a card game that also operated based on a GridLayout. Below is some of the code that was used to drive major events:
CardPanel extends JPanel {
public CardPanel() {
setLayout(gridLayout);
placeCards(N);
chatBoxText = new JTextArea(5, 30);
}
public void placeCards(int numCards) {
for (int i = 0; i < numCards; i++) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.add(new GraphicCard(card, jLabel));
add(jLabel);
jLabel.addMouseListener(this);
}
if (GameLogic.noSetsOnBoard(cardSet)) {
gridLayout.setRows(gridLayout.getRows() + 1);
placeCards(3);
}
}
public void updateCard(Integer location) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.set(location, new GraphicCard(card, jLabel));
jLabel.addMouseListener(this);
}
}
The CardPanel here is an object that represents the GridLayout (you see the constructor) as well as some of those helper methods.
Further on, on particular actions, I have the following code:
cardPanel.removeAll();
// Some other logic here
for (GraphicCard card: cardSet) {
cardPanel.add(card.getJLabel());
}
cardPanel.revalidate();
cardPanel.repaint();
In short, yes, it is custom, but it is not difficult. There are better ways to do it than the above such that you do not need to empty the entire panel in order to refresh it, the logic I have omitted above merited this particular solution.
The approach I used kept track of a CardPanel object which was an abstraction that contained not only the information that each box represented (in my case a card with some values), but also the graphic element to be rendered.
This was some time ago, so I don't recall the exact details but hopefully this is helpful.

Java Swing: Swing Worker, invokeAndWait and Overlapping JLabels

Hey guys I've come with 2 problems, both being Java Swing related. I am developing a card game in Java. I use arreays lists of type to hold the values of each card and I have a main Play() method that calls updates to the GUI using invokeLater and a Singleton approach to my GUI class.
The first question is fairly simple. I create the cards on the GUI using JLabels like so; attaching relevent listeners and adding them to the appropriate panel, in this case, the 'Human Hand Panel':
for (int i = 0; i < (HumanHand.size()); i++)
{
Card card = HumanHand.get(i);
BufferedImage cardImage = null;
try {
cardImage = ImageIO.read(new File("card/" + card + ".jpg"));
} catch (IOException e) {
e.printStackTrace();
}
JLabel picLabel = new JLabel(new ImageIcon( cardImage ));
picLabel.addMouseListener((MouseListener) me);
HumanHandDisplay.add(picLabel);
}
//HumanHandDisplay.validate();
HumanHandDisplay.updateUI();
The problem I'm having is that when the human hand is more than 7 cards or so, it creates a larger panel area and starts a new row of cards beneath. What I would like to do is, when the hand reaches a certain size, the cards start to overlap eachother (like you would hold cards in your hand). I've messed about with .validate() but gotten nowhere. Any help you can give here would be most welcome.
My second question is about using a Swing Worker to return the Human player's card selection. I have read a little bit about Swing workers but I'm unsure as to the best way to implement one in my own game. At present, using the console I have a scanner than takes the input of an int as the choice (the place of the specific card in the ArrayList). I would like this int to be selected by clicking on the card in the players hand. At the moment I use:
String name = "" + i;
picLabel.setName(name);
to set the names of the Card JLabels to the int in the for loop creating them (as shown^^), and I use:
public void mouseClicked(MouseEvent e) {
String name = ((JLabel)e.getSource()).getName();
System.out.println("Working " + name);
selection = Integer.parseInt(name);
}
To return that int when one of the cards is clicked. Here is what I've been using to call the GUI methods from Play() aswell:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI.getInstance().UpdateHand();
}
});
My question is, how can I get the Play method to call a method in the GUI class, which sets up the hand with the appropriate listeners (lets call it GUIPlayerSelection() for now) then wait for the player to click a card, which then returns the int to my Play() method in my main class that represents the card selected. I'm unsure as how to use invoke and wait, as in, if I use invoke and wait, will it just wait for the cards to be set up, or will it wait for the mouseClicked() method to finish aswell? Or will I have to do something else to make sure it waits for the mouse to be clicked after the hand set-up? And how then will I return that int? I've been told to use a swing worker but if someone could explain how I can implement that in this case that would be awesome.
Thank-you in advance.
For your first question, are you having an issue getting the frame to repaint or with the layout of the cards?
If the latter, then you may want to have a look at Java Layout Managers, it describes how the content pane inside your JFrame organizes the components added to it.
If none of those will work for you (and I don't think they will with that you describe), you can use the setBounds method to manually align your JLabels.

Categories

Resources