I am working on a form that provides "real-time" validation to the user and I have one problem.
The goal is to put a label near the field (in this case a JSpinner), to show the user if the data is accepted or denied, in the same way that javascript-based validators do.
The problem is that for archieving this, I need to set the value for the corresponding label and the only way I have found to do this is to create as many verifiers as fields, this way:
class MyVerifier extends InputVerifier{
static final double MAX_VALUE = 30;
#Override
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
Double value = Double.parseDouble(tf.getText().replace(',', '.'));
return (value>1);
}
#Override
public boolean shouldYieldFocus(JComponent input) {
boolean isValid = super.shouldYieldFocus(input);
if (isValid) {
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("resources/accept.png")));
jLabel1.setText("");
} else {
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("resources/exclamation.png")));
jLabel1.setText("The number of items must be greater than 1");
}
return true;
}
}
Then, the same code for jLabel2... It must be another way to do this.
Thanks in advance.
You could have a Hashmap for the text field and its related label component. Then in the shouldYieldFocus method you retrieve the related label for the text field being validated. You can then set the text/icon of the label appropriately.
You would probably also need a secound Hashmap containg the label and the text message for the error.
You could also use a JDialog as a popup next to the JComponent you are validating. This popup JDialog will have a JLabel that will encapsulate the message you want to display next to the respective Jcomponent. All you have to do is to calculate the position of the popup relative to the Jcomponent you are validating.
You can find a good example here
Related
I have an input form with a combobox which displays integer values. I want the user to be able to add new integer values to this combobox. I need to validate whether the input are only numbers (and not letters) - if it isnt a valid integer I want to display an error message.
For textfields i figured it out quickly - i get an error message even while typing a "wrong" input! But i cannot find a solution for the combobox in combination with the addCustomValueSetListener.
I'm using Vaadin 14.1.21 and Java JDK+JRE 1.8.
Right now if I input a custom value with letters, I don't get an error message displayed beneath the box and it just silently ignores the input value when I want to "save" it / store it in the database.
public class MyForm extends Div {
private TextField tf;
tf =new TextField("TF");
tf.setWidth("100%");
tf.setRequired(true);
tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
tf.setValueChangeMode(ValueChangeMode.EAGER);
private ComboBox<Integer> combo_int;
combo_int= new ComboBox<>();
combo_int.setItems(114, 12383, 65432189);
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener(
event -> combo_int.setValue(Integer.parseInt(event.getDetail()))
// since I need to parse new values here, I cannot use a validator upon binding
);
binder = new BeanValidationBinder<>(MyData.class);
binder.forField(tf)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("needs to be integer!"))
.bind("tf_data_integer");
binder.forField(combo_int)
.bind("integer_data");
}
So i figured out a very dirty McGyver solution... but it still does not work 100%.
The main problem being that:
I have to parse (and hence validate) custom input in the addCustomValueSetListener
I cannot do it at the bean-binder as the custom input comes as a
String and the bean-binder expectes an Integer
Using withConverter(StringToInteger) does not work as I still want
to be able to select an Integer item
from the Drop-Down menu.
So I had to
build my own warning label
reset the label manually upon switching
between objects which are displayed in the form
use setInvalid to highlight the wrong input & reset it after
highjack the Validator to reset the status_label (to fix the issue of the label remaining if the user used the drop-down menu after selecting a wrong one - the label stayed...)
I still have the problem that binder.hasChanges() doesn't register an invalid input as a change as we catch it beforehand in the parser and it never makes it to the binder.
Perhaps I shall find a solution for that too, tomorrow.
public class MyForm extends Div {
private Label status_label = new Label();
private TextField tf;
tf =new TextField("TF");
tf.setWidth("100%");
tf.setRequired(true);
tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
tf.setValueChangeMode(ValueChangeMode.EAGER);
private ComboBox<Integer> combo_int;
combo_int= new ComboBox<>();
combo_int.setItems(114, 12383, 65432189);
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener((event -> {
if (isInteger(event.getDetail())){
status_label.setText(""); // reset on success
warengruppen_id.setValue(Integer.parseInt(event.getDetail()));
warengruppen_id.setInvalid(false);
} else {
status_label.setText("Custom Format-Error!"); // set to error
status_label.getStyle().set("color", "red");
combo_int.setInvalid(true); // red background coloring
}
}
);
binder = new BeanValidationBinder<>(MyData.class);
binder.forField(tf)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("needs to be integer!"))
.bind("tf_data_integer");
binder.forField(combo_int).
withValidator(event -> {
// we highjack the Validator to reset the field on correct input
status_label.setText("");
return true; // never raise the validator message
}, "")
.withNullRepresentation(null)
.bind("integer_data");
}
My final solution was to use Strings to display them in the frontend and build a custom Converter where I disable the "setGroupingUsed" to stop Vaadin from inserting thousands-seperators into my IDs in the UI.
Turns out it was easier than I thought, I just had to use a different data format for the UI than for the backend + a custom converter.
private static class Id_StringToInteger_Converter extends StringToIntegerConverter {
public Id_StringToInteger_Converter() {
super("Input has to be an Integer!");
}
#Override
protected NumberFormat getFormat(Locale locale) {
final NumberFormat format = super.getFormat(locale);
format.setGroupingUsed(false); // disable thousands-seperator!
return format;
}
}
combo_int= new ComboBox<>();
combo_int.setItems("6583212", "114514", "879278");
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener(event -> combo_int.setValue(event.getDetail()));
binder.forField(kreditoren_id)
.withNullRepresentation("")
.withConverter(new Id_StringToInteger_Converter())
.bind("kreditoren_id");
How can i set formatted text in JTextFiled , as like when posting answer in this website we can change style of font and adding numbering etc.
Google is your best help. Steps to construct this will follow something similar.
1: Create identifier, some character combination/symbol to define what's what(bold, italicized, underlined).
2: When text is passed into your program have something that checks to see if your identifiers were used.
3: If you find and identifier, then use an if statement, switch or something to change the font to what you want and remove the identifier from your string.
4. Then display the string where you want it.
Cheers.
I don't really get your question . You need to explain in detail so that other viewers are able to help you out.
Are you making your TextField as bold inputs?
Are you trying to make your textfield accept only numbers ?
Elaborate more on
Bullets and numebering .
if you are trying to create your own inbuild function like textField accept only numbers.
You have to create a few java classes.
For an example
template.java
private JTextField createText(boolean acceptOnlyNumbers){
JTextField userInput;
if(acceptOnlyNumbers != false){
userInput.addKeyListener(new acceptNumberOnly(userInput))
}
return userInput;
}
You can create this within the same java class, but the best way is to asign your listener to a different class so that whenever you want to modify dynamically, you could just make changes to that file instead of searching it .
class acceptNumberOnly implements addKeyListener{
JTextField textField ;
public acceptNumberOnly(JTextField textField){
this.textField = textField
}
textField.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
char c = e.getKeyChar();
if (!(Character.isDigit(c) || (c == KeyEvent.VK_BACK_SPACE) || (c == KeyEvent.VK_DELETE))) {
e.consume();
}
}
});
}
I feel this is one of the best practice to use, categorised your components as class. so you can reuse this components over and over again and codes will be much neater.
I have here a code I found in the stackoverflow which allows the table to have a custom cell editor as JTextField.
I have been reading some of the articles about cell editor and I understand some the behavior of each abstract method.
class tableText extends AbstractCellEditor implements TableCellEditor {
JComponent component = new JTextField();
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,int rowIndex, int vColIndex) {
((JTextField) component).setText((String) value);
return component;
}
public Object getCellEditorValue() {
return ((JTextField) component).getText();
}
}
This code allowed me to add a JTextField when I want to edit a cell in my table but I am looking to add some code to it but Im not exactly sure where to put them.
The behavior I wanted to add was this:
When the cell is clicked and the JTextField appears, if the user pressed a numerical key, it will replace the old value with a new one.
If the cell's value was left blank, the original value will be retained.
I know how to make these codes, but I'm not sure where to put them.
Anyone can guide me on this?
If the user pressed a numeric key, it will replace the old value with a new one.
As shown here, you should use a DefaultCellEditor with a JTextField for your cell editor. Override the table's editCellAt() method and select the editor's text so that the old value will be replaced immediately as the user types.
final Component editor = getEditorComponent();
…
((JTextComponent) editor).selectAll();
If necessary, add a DocumentListener to examine individual keystrokes or a DocumentFilter to enforce numeric entry.
If the cell's value was left blank, the original value will be retained.
Press the Escape key to cancel editing and restore the original value.
I am writing a program that contains a JButton. Every time the button is clicked, a new JTextField is added to a JPanel.
My problem is that, after the user has created all the JTextFields and filled them with information, I need to get the text of each field. How can I get access to the JTextFields when they are dynamically generated, as they don't have instance names? Is there a better way to get the text of each one, without knowing their instance name.
Here is the code of the actionPerformed event of the JButton...
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
JTextField x = new JTextField();
x.setColumns(12);
p.add(x);
validate();
}
You say you want to get the text from each field. So, when you create the new instances x, why don't you keep a collection of them, such as adding the JTextFields to an ArrayList?
Alternatively, assuming that p is a JPanel, you should be able to get all the children, which would be the JTextFields that you're adding. Try using getComponents() like so...
Component[] children = p.getComponents();
for (int i=0;i<children.length;i++){
if (children[i] instanceof JTextField){
String text = ((JTextField)children[i]).getText():
}
}
You can find them all by looping through the components of the panel (or whatever "p" is). If necessary, check if each is a text box. That is, do p.getComponents and then loop through the returned array. Like:
Component[] components=p.getComponents();
for (Component c : components)
{
if (c instanceof JTextField)
{
String value=((JTextField)c).getText();
... do whatever ...
}
}
If they are interchangeable, that should be all you need. If you need to distinguish them, I think the cleanest thing to do would be to create your own class that extends JTextField and that has a field for a name or sequence number or whatever you need.
HI all,
I'm trying to write a simple star rating component. I'm fairly new to the Java language and I'm not sure if what i want to accomplish can even be done in Java. Is it possible for me to add a JLabel inside an array of JLabel, and each JLabel in the array will have a mouse event listener. Now is it possible to set it up so that when the mouse event fires on say Label[3] that i can get the index value of it?
Here is how I built my Panel
public Rating(Integer max,int size) {
JLabel position = new JLabel[max];
this.setLayout(new FlowLayout());
for(int i=0; i != max;i++){
position[i]=new JLabel(icons.getIcon("star-empty", size));
position[i].setOpaque(true);
position[i].addMouseListener(this);
add(position[i]);
}
}
#Override
public void mouseEntered(MouseEvent e) {
JLabel a= (JLabel) e.getComponent();
//****Have some code in here to tell me where in the position array the event came from????***
int index = ?????
}
Thoughts/Idea/Suggestions please.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
THanks.
Instead of using the same listener for each label like you did:
position[i].addMouseListener(this);
...you can create a special listener class that takes the index number, and allows you to find it later:
position[i].addMouseListener(new RatingMouseListener(i));
Each label will have a separate instance of the listener with a different index value. The code for the inner class would look like something like this:
private class RatingMouseListener extends MouseAdapter {
private final int index;
public RatingMouseListener(int index) {
this.index = index;
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered for rating " + index);
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited for rating " + index);
}
}
Then, you just override any method in MouseAdapter.
Also, like other people said, you might want to use JButtons instead of JLabels because they have better support for action events. You can still give them icons.
You could name each JLabel according to its index using its setName method, then use the MouseEvent's getComponent method to get the originating JLabel back, use getName on it and there's your index. That would be one way, but would involve storing the index information in two places (implicitly in its placement in the array, and explicitly as the label's name), so it's pretty much begging for inconsistency to arise.
You could also search through the array for the JLabel reference you get from getComponent, but that's not so great either, especially for large arrays.
The way I usually do it is:
int i;
for (i = 0; i <max; i++)
if (position[i] == e.getcomponent())
break;
now position[i] is the label you are looking for.
Just know that JButtons can look any way you'd like. They can have ImageIcons and don't even have to look like buttons.
Why is the index important? You know how to get the component, so just loop through the array to get the index.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
How does using a button solve the problem of determining the index? However, I also agree using a button is better than a label and then you would use an ActionListener instead of a MouseListener. You can make the button look like a label by using:
button.setBorderPainted( false );
Now if you use an ActionListener you can use the setActionCommand(...) method to store the index value of the button. Then in the event you use the getActionCommand(...) method.