Newbie here trying to make a simple GUI with JTabbedPane. I've looked through a lot of examples but have been unable to find a solution. Basically, I'm trying to print out a String to a JTextArea. While it seems very simple, I have been unable to get everything to work together. I understand the difference between local and global variables, but I think that is where my problem lies. Any guidance would be greatly appreciated. *Please note that we are unable to use a layout manager for this project.
The code below represents part of the tab that has the JButton and JTextArea.
//Text area that shows details. Scrolls.
JTextArea areaDeets = new JTextArea();
areaDeets.setBounds(65, 300, 250, 300 );
areaDeets.setText("");
JScrollPane scroll = new JScrollPane (areaDeets);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
panel2.add(areaDeets);
panel2.add(scroll);
areaDeets.addActionListener(new StopTest());
//Stop button--stops tests when pressed.
JButton stop = new JButton("Stop");
stop.setBounds(215, 650, 100, 40);
panel2.add(stop);
stop.addActionListener(new StopTest());
The code below is the method that calls the ActionListener.
//Panel 1 - Stop, shows that the test has been stopped
static class StopTest implements ActionListener{
public void actionPerformed(ActionEvent e){
String stop = "The test has been stopped";
areaDeets.setText(stop);
panel2.repaint();
}
}
Edit: Code does not compile. Eclipse says that I cannot call addActionListener on a JTextField.
You can pass the reference of the JTextArea to the constructor of the ActionListener:
public class StopTest implements ActionListener {
private JTextArea area;
public StopTest(JTextArea area) {
this.area = area;
}
public void actionPerformed(ActionEvent e) {
String stop = "The test has been stopped";
area.setText(stop);
}
}
One possible solution (guessing here due to limited information): don't make the StopTest class static. Rather make it a private inner non-static class or a stand-alone class. This will allow it access to non-static fields of your outer class.
Don't add an ActionListener to your JTextArea as this is not allowed and has no real meaning, since JTextAreas allow returns to be entered, and use this to start a new line in the JTextArea.
Other unrelated recommendations:
Also, as a general rule, you should avoid use of null layout as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain.
Much better would be to use nested JPanels, each using its own layout manager, and then calling pack() on your JFrame after adding all components but prior to displaying it.
Never call setBounds(), setSize(), setPreferredSize() or any similar call on a JTextArea that goes inside of a JScrollPane as it will prevent the JScrollPane from working correctly within your JScrollPane due to your setting bounds. This will prevent the JTextArea from expanding when more lines are added, sometimes preventing display of the scrollbars, or if their displayed, preventing them from working properly. Better to set the JTextArea's viewable columns and rows via one of its constructors that has int parameters.
There's no need to call repaint() after setting the text of a JTextComponent such as a JTextArea, since the textarea's model will notify its view (the part that is rendered on the GUI) of changes, and the view will then automatically call repaint itself.
Related
I'm trying to set a variable to be a new JPanel and then add it once a button is pressed, but it is not working and I don't know why.
code:
private void nextButtonActionPerformed(java.awt.event.ActionEvent evt) {
remove(scriptPanel);
scriptPanel = new GemPanel();
add(scriptPanel);
validate();
repaint();
pack();
}
GemPanel is just a JPanel class I made. When I press the next button, it re-sizes the frame to be as small as possible and nothing actually happens. If I re-size it to normal, the original scriptPanel is still there.
What gives?
Instead of trying to remove and add entire panels, a better, less problem prone approach would be to use a CardLayout that will allow to swap views. You can see more at How to use Cardlayout
Also, by the looks of your method signature, it seems you're using the Netbeans builder too. You may also want to take a look at How to Use CardLayout with Netbeans Gui Builder
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.
It was hard to word the title for this one, but here's the explanation:
I have a menu bar that I'm adding as an external object from my MenuBar.java that extends JMenuBar to my main program file APP.java that extends JFrame.
MenuBar and a JPanel (which is in my main program file, APP.java) are added to the JFrame. How do I make buttons from the MenuBar perform actions on the JPanel.
Here's how my JMenuItem objects look like right now in MenuBar.java:
item = new JMenuItem("New);
item.setMnemonic(KeyEvent.VK_N);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
ActionEvent.ALT_MASK));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JLabel block = new JLabel();
block.setPreferredSize(new Dimension(120, 160));
//***This is where I run into a problem... I want to add this JLabel to my JPanel in
// the main file, and I also want to revalidate/repaint the JPanel to take show
// the new JPanels as they're added.....
}
});
file.add(item);
I'm not sure if I need to extend my APP to implement ActionListener.... but then I am not sure what to do afterwards.
EDIT:
Well, I was able to perform the intended action by making my content panel public and static, thus making it available without instantiating the APP object. And then I was able to implement this code into my actionPerormed methods in ActionListeners:
APP.content.add(new Thumb());
APP.content.validate();
Thumb() method creates a new JLabel;
Hopefully this won't mess up my stuff later on down the line, being that my content panel is static now.
This is hard to answer.
I would use setAction(Action) (indirectly). One can make an Action as child of AbstractAction, and an Action can hold its text, an icon, mnemonic key and more.
One typical usage is a JTextPane that provides a Action[] getActios() and those actions might be added to the menu bar or a JToolBar.
Please look up some code samples.
I leave it at this half of an answer.
An intro.
It would depend on what actions you want to perform, but the overall solution is the same. You need to pass a reference of the object of the object you want to work with to the menu class.
If you can, its better to pass a model of interace, limiting your actions to only performing work you really want them to
It's my first Post here, so forgive me please if i'm doing something wrong.
My Problem is:
I am trying to add Components to a JPanel with defined values for Size etc.
But when i add them to the Panel, they do absolutely not have the Size and Location they should have.
For example:
public class Console extends JFrame {
private JPanel mainPanel = new JPanel();
private JTextArea textField = new JTextArea();
private JTextArea textField2 = new JTextArea();
public Console() {
this.setSize(500,300);
this.mainPanel.setSize(this.getWidth(),this.getHeight());
this.textField.setEditable(false);
this.textField.setSize(this.mainPanel.getWidth(), 100);
this.textField.setPreferredSize(new Dimension(this.mainPanel.getWidth(),this.mainPanel.getHeight()));
this.textField.setLocation(0, 0);
this.textField.setText("some text");
this.textField.setVisible(true);
this.textField2.setSize(this.mainPanel.getWidth(),200);
this.textField2.setPreferredSize(new Dimension(this.getWidth(),this.getHeight()));
this.textField2.setLocation(0,this.mainPanel.getHeight()-this.textField.getHeight());
this.textField2.setText("blabla");
this.textField2.setVisible(true);
this.mainPanel.add(textField);
this.mainPanel.add(textField2);
this.mainPanel.setVisible(true);
this.add(this.mainPanel);
// I know you should not call setVisible() in the Constructor, just for making Code more simple here.
this.setVisible(true);
}
}
When i start the Application, both JTextArea's are really small and somewhere in the middle (not as set above) while the mainPanel is correct.I tried to call setSize() and setPreferredSize() in different Places in the Code, but it didn't work. I know it is better to use a LayoutManager for doing this as far as i heard but to be honest, i do not get how to use it correctly. I checked it on Oracle Doc's but i would appreciate it if someone could post a clean Solution for this, Thanks in Advance.
You need to set a proper Layout for your Container. You setLayout for a Container like JFrame, JPanel and so on. You don't add other components to layout, but to a container. Then it would layout them accordingly. It is how it works.
With proper layout you would not need to call setLocation(). Also setVisible(true) is excessive, because true is default values for those components in your code.
Better not to extend JFrame, extend JPanel instead and add it to JFrame.
Please, learn about EDT and SwingUtilities.invoketLater() you need to use it.
Also you can save some bytes, not typing this. all the time.
it's all about the layout Swing layout.
For your JTextArea problem, use:
JTextArea j=new JTextArea();
j.setColumns(20);
j.setRows(5);
Change the values of setColumns() and setRows() to vary the size; + the suggestion given about Layout Managers.
Hope this works ;)
try using the absolute layout of the jpanel.
class Deal implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
dl.setDeck();
dl.shuffle();
dl.firstDraw(pl);
for(Card c:pl.showHand())
panelplay.add(new JLabel(c.getImageIcon()));
panelplay.validate();
}
}
This is an event handler for a Jbutton. The method pl.showHand() returns a ArrayList of a user defined class 'Card'. Inserting a println() inside the loop shows the print, so the code is being executed but the Panel panelplay isnt showing card Images.
What about the existing labels on the panel? You don't remove them. I'm guessing you are using a FlowLayout and the labels just get added to the end of the panel so you don't see them.
So one solution is to use panel.removeAll() before adding the labels back to the panel. I then use:
panel.revalidate();
panel.repaint();
Or the better option as suggested earlier is to not replace the labels but just replace the Icons using the setIcon() method.
Do as Gilbert says, look at the Swing Tutorial part that concerns Labels.
JLabel has the following methods...
void setIcon(Icon)
Icon getIcon()
Also look at the SplitPaneDemo It does exactly what you want, you can even run it with JNLP to see.
You don't want to add the JLabel in the ActionListener.
You want to use an already added JLabel setText() method in the ActionListener.
You define all the Swing components once, when you create the GUI.