JLabel doesn't show up until you maximize my frame - java

I was looking for this situation, but apparently there is no other question about this, so, here I go.
I'm making a school project which requires that I make a JFrame and even though I had a horrible time with ActionEvents, I finally could make it work, so after I got some info in my JFrame which I need that shows in a JLabel an answer and, well, it does, the only thing is that nothing happens after I click my button, but it happens when I maximize or resize the window itself.
button1= new JButton("Add user");
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
user= input1.getText();
if(user.isEmpty()){
JOptionPane.showMessageDialog(null, "you have an error there!");
}
else {
output2 = new JLabel("thx for registering, "+user);
output2.setBounds(10,40,150,20);
panel.add(output2);
}
}
});
button1.setBounds(310,10,140,20);
panel.add(button1);

Don't use setBounds(). Swing was designed to be used with layout managers, so let the layout manger determine the size/location of a component. Read the Swing tutorial on Layout Managers for working examples to get you started.
When you add a component to a visible GUI the basic code should be:
panel.add(...);
panel.revalidate();
panel.repaint();
The revalidate() will invoke the layout manger to give the component a size/location. The repaint() makes sure all the components get painted.

Related

Show/Hide JLabel with button?

Currently the code only hides the JLabel. I'm not sure why it's not making it visible when I click the button again. Hopefully this is an easy fix
contentPane.add(btnSwap);
btnHide.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
lblHello.setVisible(false);
}
});
contentPane.add(btnHide);
btnHide.setBounds(185, 199, 89, 23);
lblHello.setVisible(true);
}
I'm not sure why it's not making it visible when I click the button again.
Why should it, since all the ActionListener does (the code that's called on button press) is to continually set the label invisible?
A solution is to simply toggle its visibility:
lblHello.setVisible(!lblHello.isVisible());
Note that to be safe, it's best to revalidate and repaint the container after making such changes, so:
btnHide.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
lblHello.setVisible(!lblHello.isVisible());
revalidate();
repaint();
}
});
as this will rid the GUI of "dirty" pixels that can occur from adding and especially from removing visible components.
A word on this:
btnHide.setBounds(185, 199, 89, 23);
This suggests that you're using null layouts with setBounds(...). While this often seems to newbie Swing coders the best way to create complex GUI's, it will come back to haunt them later, since this will mean that the GUI will look OK on one platform and one platform only, and that if later you want to enhance or improve the GUI, it can only be done with much difficulty and risk of bugs. Much better is to learn and use the layout managers.
Another recommendation:
If your desire is to change the appearance of the GUI on button press, then also have a look at the CardLayout (please check the CardLayout Tutorial) as this can be a way to cleanly and easily swap views
And a better recommendation:
Since a JLabel only shows its text or its icon or both, the best way to make it "invisible" is to remove its text and its icon, as noted by Andrew Thompson below:
// get rid of its text
lblHello.setText("");
// and if needed
lblHello.setIcon(null);
This won't work for text components such as JTextFields and JTextAreas or other components that have more "heft" than a JLabel including pretty much all other user-interaction components.
Try :
btnChangeLabelVisibilityButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
lblHello.setVisible(!lblHello.isVisible());
}
});

Removing a button during actionperformed of that button

I was trying to figure out why my program freezes and I was able to replicate it with a small script so I can put it here. Basically, in this script When you clicked on button Test1, it is supposed to remove it and added new button Test2. The program freezes. Why? How can I over come this?
final JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 600));
final JPanel panel = new JPanel();
final JButton bTest1 = new JButton("test1");
bTest1.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
panel.remove(bTest1);
panel.add(new JButton("test2"));
}
});
panel.add(bTest1);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
Offcourse in the real program, on the button click, remove all the contents of the panel and re add a new set of components.
Looking for your help!
Offcourse in the real program, on the button click, remove all the contents of the panel and re add a new set of components.
Then you should probably be using a CardLayout. A CardLayout is designed to allow you to swap panels.
Read the section from the Swing tutorial on How to Use CardLayout for more information and working examples.
The program freezes. Why? How can I over come this?
It doesn't freeze, its just that the panel isn't smart enough to repaint itself. It you resize the frame you will see the new button.
The problem is you remove the button and add a new button but the panel never repaints itself because the panel is not aware of the changes. You need to invoke the layout manager so the new button can be given a proper size.
The basic code for add/removing components on a visible GUI is:
panel.remove(...);
panel.add(...);
panel.revalidate();
panel.repaint(); // sometimes needed
Action performed method of the JButton will be executed in AWT thread. When you remove the button from the container, that will launch events that should be executed as well in the same thread. So one is waiting for the other , and so the program freezes. For solving that situation use
SwingUtilities.invokeLater
method to execute the removing action of your button

JFrame doesn't update its content

I've been struggling to get my JFrame to repaint its content. I've tried using both the revaluate() and repaint() methods together after I add the new components into the JFrame but I'm not seeing the frame change.
Here's the simple GUI of a minesweeper game I'm trying to make.
When a user clicks on either of the top 3 buttons, they enter the following code block
private void drawGrid()
{
removeAll();
setLayout(new GridLayout(2,1));
add(new JButton("button"));
setVisible(true);
revalidate();
repaint();
setVisible(false);
setVisible(true);
}
When clicking a button, the whole application turns white, but I can't see the new button I added. When I remove the two setVisible() method lines, then clicking a button definitely removes the components since I can't click on any of them now, but the 4 initial buttons are still visible. Removing the revalidate or repaint methods has no effect on the application.
What else can I try to get the application to refresh and display its new content.
Calling removeAll on a JFrame is dangerous and can produce unexpected results, another reason why it's discouraged to extend from or manage UI's directly on top level containers.
Start by using a JPanel as you base UI component, then use a CardLayout to manage switching between the views.
Separate each view into it's own class (extending from JPanel or something simular) for easier management
Maybe it would work better if you used this
easyBtn.setVisible( false );
medBtn.setVisible( false );
hardBtn.setVisible( false );
customBtn.setVisible( false );
newBtn.setVisible( true );
If that does not work, try putting it into a SwingWorker.

Why isn't this gui updating?

I have a Gui and a Game class, and I'm unable to update the gui from the game. I'm not using threads, but I've seen it update before, so that isn't the problem. The game logic is really simple, there is no need for threads. No matter how furiously I call repaint() and revalidate(), it doesn't work now, no matter where I put it.
class Gui {
//...
public Gui(Game game) {
this.game = game;
initialize();
}
private void initialize() {
//...
okButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
okAction(textField.getText());
textField.setVisible(false);
okButton.setVisible(false);
textField.setText("");
}
});
}
private void okAction(String input) {
game.receiveInput(input);
}
public void output(String msg) {
textArea.append(msg + "\n");
}
public void getInput() {
textField.setVisible(true);
okButton.setVisible(true);
textField.setText("");
}
}
Also I want to be able to pass a String back to the game instance. I thought I'd call getInput() from the game, which will show a JTextField to type in, and a JButton to submit. In the actionPerformed() method I would just get the text entered, and call a method back in the game class. I don't know if this would work, since the gui is not updating, and I never had the input field and button appear. Is this right?
This would be the method which the gui "calls back":
class Game {
//...
public void receiveInput(String input) {
int n = Integer.parseInt(input);
if ( validInput(input, actualDecision.choices.size()) ) {
parser.setAction(actualDecision.choices.get(n-1).action);
}
}
}
From the game class, I just want to call gui.output() and gui.getInput() a few times.
Where is my problem? Why isn't it updating, nor freezing? If I use the debugger, the both output() and getInput() is executed, but nothing happens...
EDIT:
Ok I see a problem myself, with the getting input part... Since it returns quickly, it can never receive an input. But that doesn't explain why aren't the input field and the button, or any text is showing up
EDIT 2:
Oh god, sorry, I really don't know how to make it shorter, but you only ever need to look at the Game and the Gui, others are just there to compile.
The code: https://gist.github.com/anonymous/53bad714592792316b4d
An xml to test against: https://gist.github.com/anonymous/30b56facb78fe6ecd482
Honestly I have just taken a look to Gui class code and don't know why it doesn't update properly when it interacts with Game class. BUT I have several remarks on your code and I hope these can lead you in the right way by making this class simpler and then you can focus on the interaction with Game class.
Menu bar
You're adding the JMenuBar to a JPanel instead of setting it to the JFrame:
panel.add(menuBar, gbc);
//frame.setJMenuBar(menuBar); Use this instead
As JFrame is a top level container prepared to handle a menu bar you should consider make the suggested change.
Saving validate() call
As the JFrame is initialized at the start of initialize() method and the JPanel is added after making the frame visible then you have to call frame.revalidate() method to revalidate the components hierarchy. If you just initialize the panel before make the frame visible then you don't need to call revalidate() method. Take a look to this answer for further details.
Missing pack() call
There's no call to frame.pack() method to lay out the frame's subcomponents. Take a look to Window.pack().
Missing GridBagConstraints when adding okButton
There's no GridBagConstraints as argument when adding okButton to panel:
panel.add(okButton);
//panel.add(okButton, gbc); This is the correct way.
Use of setSize()
In this line:
frame.setSize(800, 600);
We should avoid using set(Preferred | Minimum | Maximum)Size() because of reasons discussed in this topic: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Use of GridBagLayout
This is just a suggestion there's nothing wrong on how you are using GridBagLayout. As probably you have noted this layout manager is a little hard to use (and I really don't like it by the way :) You can use a Nested Layout approach to make the components layout easier and your code more readable. Maybe this approach is good enough:
Set the JMenuBar to the JFrame. It's one less component to lay out
;)
Add the scroll pane with the text area directly to the frame's
content pane using
BorderLayout
constraints: frame.getContentPane().add(scrollPane,
BorderLayout.CENTER);
Create a new JPanel to hold the text field and the button used to
ask for user's input and add it to the frame's content pane.
Translated to code:
JPanel usersInput = new JPanel(new FlowLayout(FlowLayout.CENTER));
usersInput.add(textField);
usersInput.add(okButton);
frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.getContentPane().add(usersInput, BorderLayout.SOUTH);
frame.setTitle("Choose your own adventure");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // It's better practice DISPOSE_ON_CLOSE ;)
frame.setVisible(true);
Update
Well I'm curious and I really want to test your game (quite nice work by the way :) The thing is I have found at least two problems in the logic:
Use of == to compare strings
In Parser class there are several string comparisons using == but this is not the proper way to compare strings. We must use .equals() method to compare strings equality. Take a look to this topic: How do I compare strings in Java?
Game.processStory() has an endless loop
This method has an endless loop here:
while ( !end() ) { // this condition never is false so the loop is infinite
...
}
Looking closer to Game.end() method I have found an incorrect string comparison:
private boolean end() {
return ( parser.getAction() == "end" );
//It should be: return parser.getAction().equals("end");
}
But fixing that doesn't solve the problem either: it turns parser.getAction() always returns "d1" and consequently it will never be equal to "end".
Having said this as Game.play() is executed in the Event Dispatching Thread (a.k.a. EDT) triggered by newGameItem menu item and Game.processStory() has this endless loop, then the EDT is getting blocked and Gui is never updated.
In this case I would suggest you take a look to Concurrency in Swing trail to learn about how to avoid blocking the EDT.

Adding JButton dynamically to the JPanel not working with Netbeans

I created a JFrame Class with Netbeans 7.3 and added two panels from the palette.
I have added a button in the first panel on the click of which I want to add a new button in the second panel(topoPane).
Below is the button click event that I have written for the same. But, the button is not getting added to the panel even when the event is getting called.
Please tell me what's wrong in it.
private void jButton1MouseClicked(java.awt.event.MouseEvent evt)
{
// TODO add your handling code here:
System.out.println("Creating the Button");
JButton but = new JButton();
but.setBackground(Color.red);
but.setText("New Button");
but.setBounds(500, 500, 500, 500);
topoPane.add(but);
topoPane.revalidate();
}
From your use of setBounds, it is obvious that you are using a null layout. Because of this you need to call repaint() as containers with no layout do not automatically repaint added components on revalidate.
Apart from the fact that calling repaint is good practice, layout managers can remove the need to make this call along with manage the sizing and positioning of components. This makes it a good reason to use a layout manager.

Categories

Resources