Reputation: 6747
I am a newb in Swing so this may be stupid.
Anyway I have created a class that extends JFrame and inside a panel are contained a JTextField a JTextArea and a JButton.
This is supposed to be a console implementation so the TextField will print the input to the TextArea when I press Enter or the Button next to it.
I have made the listener for this and everything works fine. My problem now is how do I make a method that expects me to press Enter on the Field? e.g. I have a method that needs 3 line input. First line calls method, second one expects me to input something and third one expects some more input. After all input is finished I print something to the TextArea.
So practically will the method have to wait a Listener to fire or what? Can someone explain how this thing can work if at all or give me a workaround that does the trick?
Keep in mind I want some reusable way because I will probably implement a lot of methods with multi-line input. Thanks in advance!
Update: here is my class that extends JFrame - code was netbean generated mostly, I will make sure to work on the import statements sooner or later. I have not implemented a method as of yet because I have no idea how to do so, but expect me adding a little piece of code checking if the input is right at first (inside the ConsoleInputAcionPerformed) and call the method (let's call it methodX) that will need the rest of the two input lines. This class is called from another class in my main().
public class MainWindow extends javax.swing.JFrame {
private javax.swing.JButton EnterButton;
private javax.swing.JPanel ConsolePanel;
private javax.swing.JScrollPane ConsoleScroll;
private javax.swing.JTextArea ConsoleOutput;
private javax.swing.JTextField ConsoleInput;
public MainWindow() {
initComponents();
}
private void initComponents() {
ConsolePanel = new javax.swing.JPanel();
ConsoleScroll = new javax.swing.JScrollPane();
ConsoleOutput = new javax.swing.JTextArea();
ConsoleInput = new javax.swing.JTextField();
EnterButton = new javax.swing.JButton();
setTitle("Graphical Super Console v.1.0");
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
setPreferredSize(new java.awt.Dimension(800, 600));
ConsoleOutput.setColumns(20);
ConsoleOutput.setRows(5);
ConsoleOutput.setLineWrap(true);
ConsoleOutput.setEditable(false);
ConsoleOutput.setFont(new java.awt.Font("Consolas", 1, 14));
ConsoleScroll.setViewportView(ConsoleOutput);
ConsoleScroll.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
ConsoleInput.setText("");
ConsoleInput.requestFocusInWindow();
ConsoleInput.setFont(new java.awt.Font("Consolas", 1, 14));
ConsoleInput.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ConsoleInputActionPerformed(evt);
}
});
EnterButton.setText(">>");
EnterButton.setFont(new java.awt.Font("Consolas", 1, 14));
EnterButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ConsoleInputActionPerformed(evt);
}
});
javax.swing.GroupLayout ConsolePanelLayout = new javax.swing.GroupLayout(ConsolePanel);
ConsolePanel.setLayout(ConsolePanelLayout);
ConsolePanelLayout.setHorizontalGroup(
ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(ConsolePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(ConsoleScroll)
.addGroup(ConsolePanelLayout.createSequentialGroup()
.addComponent(ConsoleInput, javax.swing.GroupLayout.DEFAULT_SIZE, 679, Short.MAX_VALUE)
.addGap(18, 18, 18)
.addComponent(EnterButton)))
.addContainerGap())
);
ConsolePanelLayout.setVerticalGroup(
ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(ConsolePanelLayout.createSequentialGroup()
.addContainerGap()
.addComponent(ConsoleScroll, javax.swing.GroupLayout.DEFAULT_SIZE, 536, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(ConsolePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(EnterButton)
.addComponent(ConsoleInput, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(ConsolePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(ConsolePanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
pack();
setVisible(true);
ConsoleInput.requestFocus();
}
private void ConsoleInputActionPerformed(java.awt.event.ActionEvent evt) {
printf(">"+ConsoleInput.getText()+"\n");
ConsoleInput.setText("");
}
public javax.swing.JTextArea getConsoleOutput(){
return ConsoleOutput;
}
public javax.swing.JTextField getConsoleInput(){
return ConsoleInput;
}
public void printf(Object... obj){
for(int i=0; i<obj.length; i++){
ConsoleOutput.append(String.valueOf(obj[i]));
}
}
}
Upvotes: 2
Views: 1631
Reputation: 2479
The observer and observable:
The idea basically is that you have some class observing another and when something happens the class that is being observed, the Observable, will notify the class that Observes, the Observer, and tell it something has changed. The Observable has methods setChanged() and notifyObservers() to accomplish that. And the Observer listens to that call with the implemented update() method.
I put everything into one class so you can copy/paste and run this. When you hit a key you will see how it works.
//the textfield is wrapped in a class so that it can extends Observable
public class MyTextField extends Observable {
private JTextField jTextField = new JTextField();
//this method notifies the observers you will add
public void notify(Object o) {
this.setChanged();
this.notifyObservers(o);
}
public JTextField getJTextField() {
return jTextField;
}
}
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
// The main class that observes the swing component you tell it to
public class Controller implements Observer {
private final JFrame jFrame = new JFrame();
private final MyTextField myTextField = new MyTextField();
public Controller() {
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
jFrame.add(myTextField.getJTextField());
//here we add the Observer (Controller) to myTextField (Observable)
myTextField.addObserver(this);
//and the keylistener
myTextField.getJTextField().addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped " + e.getKeyCode());
//now we notify our observers for real
myTextField.notify(e.getKeyCode());
}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased " + e.getKeyCode());
myTextField.notify(e.getKeyCode());
}
@Override
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed " + e.getKeyCode());
myTextField.notify(e.getKeyCode());
}
});
}
// this is where the event is received by the Observer
// from the observable.
@Override
public void update(Observable observable, Object object) {
System.out.println("Notified by " + observable
+ " with object " + object);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Controller();
}
});
}
}
I hope that is understandable and a solution to your problem :)
Upvotes: 1
Reputation: 18424
You'll probably have to do this a little differently than you had hoped. Basically you maintain a state of all the lines you have already received, and only when you have three lines already, then call the method that requires three lines. The general idea goes something like this:
List<String> buffer = new ArrayList<String>();
public void actionPerformed(ActionEvent e) {
buffer.add(getText())
if (!expectingMoreInput()) {
processInput(buffer);
buffer.clear();
}
}
So for your specific case, expectingMoreInput()
would just return buffer.size() < 3
, and processInput
would actually call the method that needs three lines.
The other way to do it would be with multiple threads and an object to pass lines between them. Be careful with this - thread interactions can get complicated. Something like:
SynchronousQueue<String> queue = new SynchronousQueue<String>();
public void actionPerfomred(ActionEvent e) {
queue.add(getLine());
}
public void threeLineMethod() {
String s1, s2, s3;
try {
s1 = queue.take();
s2 = queue.take();
s3 = queue.take();
} catch (InterruptedException ex) {
}
// process the three lines
}
Note that here, take
will block on put
, which is exactly what you want. The flip side is that put
will block on take
, so if you don't have a thread constantly calling take
, the event thread will block and it will lock up the interface.
Upvotes: 0
Reputation: 6206
The easiest way to make a method hang while awaiting user input is probably to make a JDialog with a text box in it. Until the user closes the JDialog, your code will not run past the point at which you display that dialog. This doesn't quite seem like the solution you're looking for however.
What you probably want to do here to make your code hang is use wait and notify. For more information, see How to use wait and notify in Java?.
Upvotes: 0
Reputation: 14806
Here is your solution:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Frame extends JFrame{
JTextField t = new JTextField(20);
JPanel p = new JPanel();
public Frame(){
p.add(t);
t.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
t.setText("Hello world");
}
});
add(p);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
Frame f = new Frame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
}
}
Upvotes: 0
Reputation: 347214
The only thing I can think of is you need to run the input from the field through a single method that can determine the current state...ie
public void handleFieldInput(JTextField field) {
String text = field;
switch (state) {
case 0:
// First line...maybe store the result in a List or array
state++;
break;
case 1:
// Second line...
state++;
break;
case 2:
// Third line...
// Add contents to the text area
state = 0;
break;
}
}
Upvotes: 0