I've been looking at the java trail on input verification and I was hoping someone could just confirm that the InputVerifier class is the best way to approach my problem.
I've a number of JTextFields in a JPanel, with a submit button. Each Field is initially blank. The problem is that the Verifier class works on condition of losing focus, but with multiple fields nothing ensures that each gets focus in the first place. For example you could enter a valid entry into the first field, and click submit, despite verification being needed on the following fields.
I'm sure I could rig something together, whereby when the submit button is pressed it checks the relevant JTextAreas for any input at all and assigns focus to the first one that still requires data, but I was hoping that there was a more elegant way to check input? Especially as the InputVerifier can be set for components rather than just textfields. Tracking or customising focus doesn't really help either as several of the fields are optional but still in a logical order (so I wouldn't want to force focus in an unnatural order just to ensure data entry)
I thought about adding the InputVerifier to a containing component, or the submit button, but again, I can't get around the problem of focus.
The problem is that the Verifier class works on condition of losing focus, but with multiple fields nothing ensures that each gets focus in the first place. For example you could enter a valid entry into the first field, and click submit, despite verification being needed on the following fields.
That is correct. The InputVerifier only works when the component loses focus.
Typically, there are two types of editing done on a forum:
field level edits. These are edits to make sure a component contains valid data. That is numbers are number, postal codes are validated for proper format etc. These type of edits can by done using the InputVerifier
form level edits. These ensure that all mandatory fields for the form are entered. This is done when you click the "Submit" button.
If all the fields are mandatory you could try disabling the "Submit" button until data has been entered in all text fields by using logic something like:
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.event.*;
public class DataEntered implements DocumentListener
{
private JButton button;
private List<JTextField> textFields = new ArrayList<JTextField>();
public DataEntered(JButton button)
{
this.button = button;
}
public void addTextField(JTextField textField)
{
textFields.add( textField );
textField.getDocument().addDocumentListener( this );
}
public boolean isDataEntered()
{
for (JTextField textField : textFields)
{
if (textField.getText().trim().length() == 0)
return false;
}
return true;
}
#Override
public void insertUpdate(DocumentEvent e)
{
checkData();
}
#Override
public void removeUpdate(DocumentEvent e)
{
checkData();
}
#Override
public void changedUpdate(DocumentEvent e) {}
private void checkData()
{
button.setEnabled( isDataEntered() );
}
private static void createAndShowUI()
{
JButton submit = new JButton( "Submit" );
submit.setEnabled( false );
JTextField textField1 = new JTextField(10);
JTextField textField2 = new JTextField(10);
DataEntered de = new DataEntered( submit );
de.addTextField( textField1 );
de.addTextField( textField2 );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textField1, BorderLayout.WEST);
frame.add(textField2, BorderLayout.EAST);
frame.add(submit, BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Just remember using these approach you don't get any message if data is missing.
I'm sure I could rig something together, whereby when the submit button is pressed it checks the relevant JTextAreas for any input at all and assigns focus to the first one that still requires data, but I was hoping that there was a more elegant way to check input?
This is a perfectly reasonable approach as it give the user visual feedback where the problem is.
Related
I have two Jframes where frame1 has some text fields and when a button on frame1 is clicked, I open another JFrame which contains a search box and a JTable containing search results.
When I click on a result row on JTable, I want that particular values to be reflected in the frame1 text fields.
I tried passing the JFrame1's object as a parameter but I have no clear idea on how to achieve this.
Any help would be highly appreciated.
Thanks
First of all, your program design seems a bit off, as if you are using a JFrame for one of your windows where you should in fact be using a JDialog since it sounds as if one window should be dependent upon the other.
But regardless, you pass references of GUI objects the same as you would standard non-GUI Java code. If one window opens the other (the second often being the dialog), then the first window usually already holds a reference to the second window and can call methods off of it. The key often is when to have the first window call the second's methods to get its state. If the second is a modal dialog, then the when is easy -- immediately after the dialog returns which will be in the code immediately after you set the second dialog visible. If it is not a modal dialog, then you probably want to use a listener of some sort to know when to extract the information.
Having said this, the details will all depend on your program structure, and you'll need to tell us more about this if you want more specific help.
For a simple example that has one window open another, allows the user to enter text into the dialog windows JTextField, and then places the text in the first window's JTextField, please have a look at this:
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class WindowCommunication {
private static void createAndShowUI() {
JFrame frame = new JFrame("WindowCommunication");
frame.getContentPane().add(new MyFramePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// let's be sure to start Swing on the Swing event thread
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MyFramePanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton openDialogeBtn = new JButton("Open Dialog");
// here my main gui has a reference to the JDialog and to the
// MyDialogPanel which is displayed in the JDialog
private MyDialogPanel dialogPanel = new MyDialogPanel();
private JDialog dialog;
public MyFramePanel() {
openDialogeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openTableAction();
}
});
field.setEditable(false);
field.setFocusable(false);
add(field);
add(openDialogeBtn);
}
private void openTableAction() {
// lazy creation of the JDialog
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
dialog = new JDialog(win, "My Dialog",
ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
}
dialog.setVisible(true); // here the modal dialog takes over
// this line starts *after* the modal dialog has been disposed
// **** here's the key where I get the String from JTextField in the GUI held
// by the JDialog and put it into this GUI's JTextField.
field.setText(dialogPanel.getFieldText());
}
}
class MyDialogPanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton okButton = new JButton("OK");
public MyDialogPanel() {
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okButtonAction();
}
});
add(field);
add(okButton);
}
// to allow outside classes to get the text held by the JTextField
public String getFieldText() {
return field.getText();
}
// This button's action is simply to dispose of the JDialog.
private void okButtonAction() {
// win is here the JDialog that holds this JPanel, but it could be a JFrame or
// any other top-level container that is holding this JPanel
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
win.dispose();
}
}
}
You'd do a very similar technique to get information out of a JTable.
And again, if this information doesn't help you, then please tell us more about your program including showing us some of your code. The best code to show is a small compilable example, an SSCCE similar to what I've posted above.
I have two Jframes where frame1 has some text fields and when a button on frame1 is clicked, I open another JFrame which contains a search box and a JTable containing search results.
When I click on a result row on JTable, I want that particular values to be reflected in the frame1 text fields.
I tried passing the JFrame1's object as a parameter but I have no clear idea on how to achieve this.
Any help would be highly appreciated.
Thanks
First of all, your program design seems a bit off, as if you are using a JFrame for one of your windows where you should in fact be using a JDialog since it sounds as if one window should be dependent upon the other.
But regardless, you pass references of GUI objects the same as you would standard non-GUI Java code. If one window opens the other (the second often being the dialog), then the first window usually already holds a reference to the second window and can call methods off of it. The key often is when to have the first window call the second's methods to get its state. If the second is a modal dialog, then the when is easy -- immediately after the dialog returns which will be in the code immediately after you set the second dialog visible. If it is not a modal dialog, then you probably want to use a listener of some sort to know when to extract the information.
Having said this, the details will all depend on your program structure, and you'll need to tell us more about this if you want more specific help.
For a simple example that has one window open another, allows the user to enter text into the dialog windows JTextField, and then places the text in the first window's JTextField, please have a look at this:
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class WindowCommunication {
private static void createAndShowUI() {
JFrame frame = new JFrame("WindowCommunication");
frame.getContentPane().add(new MyFramePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// let's be sure to start Swing on the Swing event thread
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MyFramePanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton openDialogeBtn = new JButton("Open Dialog");
// here my main gui has a reference to the JDialog and to the
// MyDialogPanel which is displayed in the JDialog
private MyDialogPanel dialogPanel = new MyDialogPanel();
private JDialog dialog;
public MyFramePanel() {
openDialogeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openTableAction();
}
});
field.setEditable(false);
field.setFocusable(false);
add(field);
add(openDialogeBtn);
}
private void openTableAction() {
// lazy creation of the JDialog
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
dialog = new JDialog(win, "My Dialog",
ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
}
dialog.setVisible(true); // here the modal dialog takes over
// this line starts *after* the modal dialog has been disposed
// **** here's the key where I get the String from JTextField in the GUI held
// by the JDialog and put it into this GUI's JTextField.
field.setText(dialogPanel.getFieldText());
}
}
class MyDialogPanel extends JPanel {
private JTextField field = new JTextField(10);
private JButton okButton = new JButton("OK");
public MyDialogPanel() {
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
okButtonAction();
}
});
add(field);
add(okButton);
}
// to allow outside classes to get the text held by the JTextField
public String getFieldText() {
return field.getText();
}
// This button's action is simply to dispose of the JDialog.
private void okButtonAction() {
// win is here the JDialog that holds this JPanel, but it could be a JFrame or
// any other top-level container that is holding this JPanel
Window win = SwingUtilities.getWindowAncestor(this);
if (win != null) {
win.dispose();
}
}
}
You'd do a very similar technique to get information out of a JTable.
And again, if this information doesn't help you, then please tell us more about your program including showing us some of your code. The best code to show is a small compilable example, an SSCCE similar to what I've posted above.
I don't want the user to select the content on JTextArea. I use setEditable(false) but it's not working. How to disable this feature of JTextArea component. Could you give me advise. Thanks.
If you would like to just disable text selection on any swing control such as JtextArea you can use the coding below:
JtextArea.setHighlighter(null);
This one line of coding will help disable the text selection and can be placed in the constructor or within a initialized method upon Frame execution.
Hope this helps
You can set the "mark" equal to the "dot" of the caret. When these values are equal there is no text selection:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class NoTextSelectionCaret extends DefaultCaret
{
public NoTextSelectionCaret(JTextComponent textComponent)
{
setBlinkRate( textComponent.getCaret().getBlinkRate() );
textComponent.setHighlighter( null );
}
#Override
public int getMark()
{
return getDot();
}
private static void createAndShowUI()
{
JTextField textField1 = new JTextField("No Text Selection Allowed");
textField1.setCaret( new NoTextSelectionCaret( textField1 ) );
textField1.setEditable(false);
JTextField textField2 = new JTextField("Text Selection Allowed");
JFrame frame = new JFrame("No Text Selection Caret");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textField1, BorderLayout.NORTH);
frame.add(textField2, BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Late to the party, but here are my findings. I had tried using setEnabled(false) on a JTextPane displaying static (not user-modifiable) content such as line numbers (for another text component). This one alone prevents the component from getting focus and text selection on it:
JTextArea textArea = new JTextArea("Static text");
textArea.setEnabled(false);
My problem with setEnabled(false) is that it forces a single disabledTextColor for all of the text (I've traced it down to javax.swing.text.GlyphView.paint()), while I want to style individual lines/chunks. I've finally tried setFocusable(false) that appears to satisfy both needs:
Not user focusable and no user selection on it;
Custom text color could be applied to individual parts of the content, or it just doesn't change the text color to the disabled one.
The complete solution needs additional setEditable(false) to prevent the mouse cursor from changing but that's it – two properties:
JTextArea textArea = new JTextArea("Static text");
textArea.setEditable(false);
textArea.setFocusable(false);
I'd like to add to a database, and my editable comboBoxModel when I enter a new name into the comboBox. I have the method for adding to the database down fine, I'm just trying to get it to somehow listen to an entry being added to the ComboBox.
What's the Best way to do this?
I've read the Java tutorial on Editable ComboBoxes, and noted where it said:
An editable combo box fires an action event when the user chooses an item from the menu and when the user types Enter. Note that the menu remains unchanged when the user enters a value into the combo box. If you want, you can easily write an action listener that adds a new item to the combo box's menu each time the user types in a unique value.
So I thought to myself, ok lets try this, and looked up some examples. Here is my attempt, essentially copy pasted out of the example I found, with my variable names:
playerNameComboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxEdited")) {
System.out.println("Adding new player!");
IController.Util.getInstance().addNewPlayer();
playerNameComboBox.insertItemAt(playerNameComboBox.getSelectedItem(), 0);
}
}
});
When I type in a new name, and press enter, it does nothing. No new database entry and no addtional option on the ComboBox. I haven't attached an action command to the ComboBox as I thought the example above assumed it would have that as default, and so did I.
But how do I get it to shout out that action command when I press enter, with the focus on the comboBox? I would have thought that comboBoxes would have had some sort of default behaviour to shout that out? Do I need to use an if(playerNameComboBox.hasFocus()) statement? Should I implement some kind of keylistener when my comboBox hasFocus()?
I'm very new at Java, so I'm unsure as to how this sort of thing should be done; any help is very much appreciated.
As requested, here is my short example in which names may be added to a JComboBox.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test extends JFrame {
private JComboBox box;
public static void main(String[] args) {
new Test();
}
public Test()
{
super();
setSize(200, 100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
box = new JComboBox();
box.setEditable(true);
getContentPane().add(box);
box.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxEdited")) {
System.out.println("Adding new player!");
box.insertItemAt(box.getSelectedItem(), 0);
}
}
});
setVisible(true);
}
}
I run into a problem using form default button when it (form) includes JFormattedTextField.
On a form with such a field, if it happens to have focus (and was changed), you have to press OK twice to get your default button 'pressed'. I think I know why it happens - it's because first Enter gets consumed in Commit processing.
I was also able to make a workaround - if you change Formatter to commit on each valid edit, then you will get proper behavior, but this a) forces you to specify formatters explicilty, and b) it's not possible to revert to 'old' value (eg. using Escape, or programatically).
Code below demonstrates that: when you run it field at the top commits on each edit and works with single Enter (but you cannot revert), field at the bottom allows reverts, but needs two Enters if edited.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.text.DateFormatter;
public class ExFrame extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ExFrame frame = new ExFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public ExFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JFormattedTextField ff_1, ff_2;
//ff_1 has modified behavior of commit on each (valid) edit
DateFormatter f=new DateFormatter();
f.setCommitsOnValidEdit(true);
ff_1 = new JFormattedTextField(f);
ff_1.setValue(new Date());
//ff_2 has default behavior
ff_2 = new JFormattedTextField(new Date());
contentPane.add(ff_1, BorderLayout.NORTH);
contentPane.add(ff_2, BorderLayout.SOUTH);
JButton btnDefault = new JButton("I am default button");
contentPane.add(btnDefault, BorderLayout.CENTER);
getRootPane().setDefaultButton(btnDefault);
}
}
So the question is: Is there a way to get JFormattedTextField both commit on Enter (so input is verified but only once) and if succesfully validated, activate default button (with single press)?
The basic trick is the fool the keyBinding mechanism into believing that the keyStroke wasn't handled by the textField. To achieve that, we need to subclass the JFormattedTextField and override its processKeyBindings to return false (meaning: "couldn't use").
Something like:
JFormattedTextField multiplex = new JFormattedTextField() {
KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
#Override
protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
int condition, boolean pressed) {
boolean processed = super.processKeyBinding(ks, e, condition,
pressed);
if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW
&& enter.equals(ks)) {
// Returning false will allow further processing
// of the bindings, eg our parent Containers will get a
// crack at them and f.i. route to a default button.
return !isEditValid();
}
return processed;
}
};
multiplex.setValue(new Date());
for Date / Number instance is (in most cases) better use JSpinner rather than JFormattedTextField, then there you can only to set for DateSpinnerModel with Locale or SimpleDateFormat,
and for JSpinner with Number Instance (valid for JTextField) you have to add Document for removing/filtering for unwanted chars
Enter is commonly used to accept the currently value, and therefore
enter in JFTF does this as well.
you can disable it by doing:
ff_1.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), new Object());
ff_2.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), new Object());
bye and be Cool ;)
Neither of your answers solved my application problem. However, it seemed that the problem solution was much simpler in my case:
private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {
add(jTextField1); //this reacts after the "ENTER" gets pressed
jButton1.doClick(); //this activates the button with Enter
jTextField1.setText(""); //this removes the text from a text-field
jTextField1.grabFocus(); //this sets a cursor within a text-field
//Whenever the Enter is pressed, after the typed text, the action is performed again.
}