I'm looking into the various ways to validate input for an editable JComboBox. Currently, I need to restrict input to numbers in a specified range. Thus far I've found 3 distinct ways. Any thoughts on the best way to approach this?
JComboBox comboBox = new JComboBox(
new Object[] {"Donnie", "Danny", "Joey", "Jordan", "Jonathan"}
);
comboBox.setEditable(true);
Get control over the user input by implementing a specialized Document overriding the methods insertString and remove.
// get the combo boxes editor component
JTextComponent editor =
(JTextComponent) comboBox.getEditor().getEditorComponent();
// change the editor's document
editor.setDocument(new BadDocument())
Replace the JTextField of JComboBox by JFormattedTextField.
You can use an input verifier as an alternative to a custom formatter
// set the input verifier
setInputVerifier(verifier);
class MyVerifier extends InputVerifier implements ActionListener
{
public boolean shouldYieldFocus(JComponent input) {}
}
Thanks.
This is what the InputVerifier was designed to do. I'd start with one of them, but really that should do the trick. Any particular reason in your requirements as to why it would not work?
Related
I'm trying to create a Java GUI dynamically by taking values from a result set and using it to generate a checklist. I've created a small demo program to demonstrate what I've done:
SQL Commands
CREATE USER 'test'#'localhost' IDENTIFIED BY 'testpw';
CREATE DATABASE combotest;
USE combotest;
CREATE TABLE combotable (
id INT(5) NOT NULL PRIMARY KEY auto_increment,
type VARCHAR(50) NOT NULL);
INSERT INTO combotable (id, type) VALUES
(default, 'Label'),
(default, 'Textfield'),
(default, 'Combo'),
(default, 'Label'),
(default, 'Textfield'),
(default, 'Combo'),
(default, 'Combo');
GRANT SELECT ON combotest.* TO 'test'#'localhost';
For your convenience if you'd like to test it yourself I've put all the SQL commands above.
Now, for my Java code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.*;
import javax.swing.*;
public class resToComboDemo implements ActionListener {
//JDBC Variables
static Connection connect = null;
static Statement statement = null;
static ResultSet res = null;
#SuppressWarnings("rawtypes")
//Other Variables
JComboBox comboBox;
JButton submit;
JFrame frame;
JLabel label;
JTextField textField;
Container pane;
public static void main(String[] args) throws SQLException {
new resToComboDemo();
}
public resToComboDemo() throws SQLException {
try {
Class.forName("com.mysql.jdbc.Driver");
// Setup the connection with the DB
connect = DriverManager
.getConnection("jdbc:mysql://localhost/combotest?"
+ "user=test&password=testpw");
statement = connect.createStatement();
//Note: in this specific case I do realize that "order by id" is not necessary. I want it there, though.
res = statement.executeQuery("SELECT * FROM combotable ORDER BY id");
createStuff(res);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error 1: "+e, "Error!", JOptionPane.ERROR_MESSAGE);
} finally {
connect.close();
}
}
#SuppressWarnings({"rawtypes", "unchecked" })
public void createStuff (ResultSet res) throws SQLException {
frame = new JFrame("Testing dynamic gui");
Dimension sD = Toolkit.getDefaultToolkit().getScreenSize();
int width = sD.width;
int height = sD.height - 45;
frame.setSize(width,height);
pane = frame.getContentPane();
pane.setLayout(new GridLayout(0, 2));
while (res.next()) {
Object[] options = { "Pass", "Fail"};
String type = res.getString("type");
JLabel label = new JLabel("<html><small>"+type+"</small></html>");
JLabel blank = new JLabel(" ");
blank.setBackground(Color.black);
blank.setOpaque(true);
if (type.equals("Label")) {
label.setBackground(Color.black);
label.setForeground(Color.white);
label.setOpaque(true);
pane.add(label);
pane.add(blank);
} else if (type.equals("Combo")) {
pane.add(label);
comboBox = new JComboBox(options);
pane.add(comboBox);
} else if (type.equals("Textfield")) {
pane.add(label);
textField = new JTextField(20);
pane.add(textField);
}
}
JLabel blank2 = new JLabel(" ");
pane.add(blank2);
submit = new JButton("Submit");
submit.addActionListener(this);
pane.add(submit);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
Now, everything works great with creating the GUI here. However, I need to be able to treat the Combobox and Textfield components as their own separate entities. Meaning, I want to be able to get user input from each different component. Right now, if I were to request information from textfield, it just gives me the information from the last textfield. This makes perfect since, because that's how java reads it. I have no problem with that.
I just can't for the life of me figure out how to get each component's input separately. Perhaps by taking the result set and adding the results to some type of array? I've attempted this multiple times in different flavors and I can't get it to come out the way I need it to. Some of you are going to request that I show you what I've tried... but honestly, it's not worth it.
And, before anybody asks: No, I will not use FlowLayout. :)
Any help is greatly appreciated!
There are probably a few ways to achieve this based on what you want to do...
If you are only performing a batch update, you could use a Map keyed to the id of the row and mapping to the Component.
This way, when you want to save the values back to the database, you would simply iterate the Maps key values, extract the Component associated with each key and then extract the value of the Component...
I might consider making a wrapper interface which has a simple getText method and wrap the component within it, making the implementation of the wrapper responsible for extracting the text, but that's just me ;)
If you want to perform updates when a individual component is updated, you would need to swap the mapping, so that the Component would the key and the id would be mapped to it.
This would mean that when some kind of event occurred that would trigger and update (ie a ActionEvent), you could extract the source from the event and look up the id in the Map based on the Component that caused the event...
Now...frankly, I would simply use a JTable and create a custom TableModel which could model all this.
This would require you to create POJO of the table, maintaining the id, type and value within a single object. This would define a basic row in the table.
The only problem is you would need to create a (reasonably) complex TableCellEditor that could take the type and return an appropriate editor for the table. Not impossible, it's just an additional complexity beyond the normal usage of a table.
This would all the information you need is available in a single object of a single row in the table.
Take a look at How to use tables for more details
Equally, you could use a similarly idea with the Map ideas above...
You could also simply create a self contained "editor" (extending from something like JPanel), which maintain information about the id and type and from which you could extract the value and simply keep a list of these....for example...
what about interrogating the Container ( pane) which contains the components
getComponents() method and loop through the sub component and check for JComobox and JTextField do the required cast and retrieve the value
Just an idea in case you are against adding the sub-components into a kind of list
You only have a reference to the last text field or combo box that you create, since you are reusing the variables that hold them. I would put them in an ArrayList, store each new text field and combbox as you create them, then you can go back and get input from all of them after you're done.
---------- (after the OP's response to the above paragraph)
No, there is no "place to refer you" -- it's your set of requirements, it would be pretty remarkable to find code that already existed that did this exact thing. Java and Swing give you the tools, you need to put things together yourself.
You don't show your "actionPerformed" routine, but let's hypothesize about it for a minute. It is called by the framework when an action is done, and it is passed an "ActionEvent" object. Looking through its methods, we find that it has "getSource()", so it will give you a reference to the component which generated the event.
Let's further think about what we have -- a set of components in the UI, and ones which can generate events are interesting to us. We want to, in this case, retrieve something from the component that generated the event.
If we have the component (from actionEvent.getSource()) and we want to do something with it, then we can, at worst do something like the following in the actionPerformed() method:
Component sourceComponent = actionEvent.getSource();
if (sourceComponent instanceof JComboBox)
{ JComboBox sourceBox = (JComboBox) sourceComponent;
// get the value from the combo box here
}
else if (sourceComponent instanceof JTextField)
{ JTextField sourceTextField = (JTextField) sourceComponent;
// get the value from the text field here
}
// or else do nothing -- our action was not one of these.
Done this way, you don't even need to keep a list of the components -- the UI is keeping a reference to all of them, and you just use that reference when the actionEvent occurs.
Now, this is not the only or even the best or the simplest way of doing this. If you wanted to extend JComboBox and JTextField with your own classes, you could have those classes both implement an interface that defined something like getValue() or getText; then you would not need the ugly instance of operator, which can usually be done away with by better design and planning.
I am making a registration page but I am fairly new to Java, I have a combo box for peoples Titles such as "Mr, Mrs, Miss, etc" one of the options is "Other..." And I have a text field next to the combo box to specify your title, I would like the text field to not be editable unless someone selects "Other..." in the combo box, How would I do this?
What it looks like at the moment:
I can't see what I'm doing wrong?
TitleSpecifyChoiceField.setEditable(false);
TitleSpecifyChoiceField.setText("Please specify title...");
TitleChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Mr", "Mrs", "Miss", "Ms", "Dr", "Other..." }));
TitleChoice.setToolTipText("");
TitleChoice.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e) {
if (TitleChoice.getSelectedItem().equals("Other...")){
TitleSpecifyChoiceField.setEditable(true);
};
You would do this the same way you'd respond to any changes in a JComboBox -- by adding a listener to the JComboBox as per the Swing combo box tutorial. Inside the listener, change the setEnabled(...) setting on the JTextField depending on the selected item. i.e., by calling getSelectedItem() on the JComboBox and testing whether calling equalsIgnoreCase("other") is true.
Note that I recommend that you use setEnabled(...) not setEditable(...) as the former will give the user visual cues as to whether the JTextField should be edited or not.
Edit
Regarding your code:
TitleSpecifyChoiceField.setEditable(false);
TitleSpecifyChoiceField.setText("Please specify title...");
TitleChoice.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Mr", "Mrs", "Miss", "Ms", "Dr", "Other..." }));
TitleChoice.setToolTipText("");
TitleChoice.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent e) {
if (TitleChoice.getSelectedItem().equals("Other...")){
TitleSpecifyChoiceField.setEditable(true);
}
}
});
Some problems and issues:
Does your JComboBox use Strings or does it hold other types of items?
You will want to add debugging code into your code to try to isolate the problem. For instance, inside of your ItemListener, add a System.out.println(...) to print out the selected item to be sure that the listener is working as expected.
You are checking if the item .equals("Other..."), a String literal. Instead consider making a String constant, OTHER that the JComboBox uses and that you test in the listener to be sure that the tested String and and the displayed are the same.
Again, I suggest you use setEnabled(...) not setEditable(...).
You should learn and follow Java naming conventions including beginning all variable names with lower case letter as this will help us to better understand your code.
You should fix your posted code indentation so that it is regular and makes sense (note my code above vs yours). Why do you want to make it harder for folks who are trying to help you to understand your code? Your job is to make ours as easy as possible as we're all volunteers.
Create and post an sscce to get the best and fastest help.
Add a listener to the combo box. When the selected item changes, call setEditable() on the text field.
You could try adding an ItemListener to your JComboBox and (as #HovercraftFullOfEels suggested) use setEnabled as opposed to setEditable. For a general idea, you could do something like this:
JTextField textField = ...;
JComboBox<String> comboBox = ...;
comboBox.addItemListener(
new ItemListener(){
public void itemStateChanged(ItemEvent e){
final String selected = (String)comboBox.getSelectedItem();
textField.setEnabled(selected.equals("other"));
}
}
);
Or if you are using Java 8 you could use this:
JTextField textField = ...;
JComboBox<String> comboBox = ...;
comboBox.addItemListener(
e -> {
final String selected = (String)comboBox.getSelectedItem();
textField.setEnabled(selected.equals("other"));
}
);
I created JComboBox in my column and it works fine. The problem occurs when I tried to add one more editor in the same column. Scenario, user need to choose value from ComboBox as their remark. If they choose Others, another textbox should appear below the ComboBox for user to type.
Code for ComboBox
TableColumn col5 = jTable1.getColumnModel().getColumn(4);
String[] options = new String[]{"Font Issue","Text Issue","Image Issue","AI Issue","Others"};
JComboBox combo1 = new JComboBox(options);
JComboBox combo2 = new JComboBox(options);
col5.setCellEditor(new DefaultCellEditor(combo1));
col5.setCellRenderer(new ComboBoxRenderer(combo2));
combo2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JComboBox nameCombo = (JComboBox)e.getSource();
String newSelection = (String)nameCombo.getSelectedItem();
if(newSelection.equalsIgnoreCase("others"))
{
}
}
});
When i add one more editor.
MyTableCellEditor textEditor = new MyTableCellEditor ();
col5.setCellEditor(textEditor );
It overwrite the dropdownlist. I want to have something like this.
Swing editors are designed to occupy the space of a single cell. If you want to display a panel with two components then you will need to create a popup editor. Read the section from the Swing tutorial on Using Other Editors for an example of how you might do this.
I need to save the values in my jcombobox at the runtime. What I am trying to do is after clicking on a button, I am setting it to editable = true. Then type the value in the combobox, but it doesn't save.
private void btadbknameActionPerformed(java.awt.event.ActionEvent evt) {
if(evt.getSource()== btadbkname){
cb_bkname.setEditable(true);
cb_bkname.getText();
cb_bkname.addItem(evt);
}else{
cb_bkname.setEditable(false);
}
}
I have already added some elements in it on the designing level, but it's limited if some random value comes then its a problem.
Because it is possible to add / remove Item(s) to / from the DefaultComboBoxModel underlaying the JComboBox, the same action (by default) is possible from outside.
You have to use MutableComboBoxMode to add / remove Item(s) to / from JComboBox that fires event from itself (view_to_model).
There are excellent examples of MutableComboBoxModel by #Robin here and here.
For better help sooner post an SSCCE, for future readers, otherwise search for extends AbstractListModel implements MutableComboBoxModel.
it can't possibly work the way you're trying it.
the comboBox has to be editable before you click the button then you just need this line
cb_bkname.addItem(((JTextField)cb_bkname.getEditor().getEditorComponent()).getText());
Try this
private void btadbknameActionPerformed(java.awt.event.ActionEvent evt) {
if(evt.getSource()== btadbkname){
cb_bkname.setEditable(true);
String newItem=cb_bkname.getText();
cb_bkname.addItem(newItem);
}else{
cb_bkname.setEditable(false);
}
}
I have a jFormattedTextField in my program and I need to update a jLabel's text when jFormattedTextField value has been changed validly.
Actually jFormattedTextField gets a number and jLabel displays diffrence between this number and another number.
I currently do this by listenning to "FocusLost" event of jFormatted text.
How can i do this?
register a PropertyChangeListener for the property "value" to the formattedField
PropertyChangeListener l = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
String text = evt.getNewValue() != null ? evt.getNewValue().toString() : "";
label.setText(evt.getNewValue());
}
};
formattedTextField.addPropertyChangeListener("value", l);
Do not use DocumentListener nor FocusListener: the former is notified too often (on every keytyped, before parsing happened) the latter is too brittle.
Probably the easiest way to do this is to use a javax.swing.event.DocumentListener that you attache to the text field. Then, as the user types, the label can be updated.
I don't remember the exact sequence, but the listener's insertUpdate() may be called before the formatted text field is validated. So, you may also need to check for valid numbers in your listener too.