I would like to have this functionality in my program:
I will have a user input field. When the user pressed the button, it will be added to the list, and input will be shown to the user.
The problem is, I would like to deselect/remove those input if the user wants. I could not achieve this.
Here is the code I have written so far, I have removed some functionality unnecessary for the question's scope:
public class AddUserInput extends VerticalLayout{
// The user input will be added to the this list
// later, this list will be sent to the server for some verification
private List<String> emails;
private HorizontalLayout content;
private VerticalLayout rows;
// user input field
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
content = new HorizontalLayout();
rows = new VerticalLayout();
content.setMargin(true);
Button addToListButton= new Button("Add to list");
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// When the user clicks add to list button
// The raw input will be added to the emails list
// The UI component is added to 'rows' component
rows.addComponent(addNewRow(emailField.getValue()));
}
});
content.addComponents(emailField, addToListButton, rows);
addComponent(content);
}
public Component addNewRow(String email){
HorizontalLayout newRow = new HorizontalLayout();
Button deleteRowButton = new Button("-");
deleteRowButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// I can delete from the UI by using the code below
newRow.removeAllComponents();
rows.removeComponent(newRow);
// How to remove from the email list???
}
});
emails.add(emailField.getValue());
Label lastEmail = new Label(emailField.getValue());
emailField.clear();
newRow.addComponents(lastEmail,deleteRowButton);
return newRow;
}
}
Is there any component/library that does this functionality?
I only need a text field, and adding the input to the list, and removing the list item if a user wants to.
The visualization of the code above:
You could use the NativeSelect component for managing the entered Strings.
I modified your AddUserInput-Component to use a NativeSelect and a corresponding DataProvider:
public class AddUserInput extends VerticalLayout {
private HorizontalLayout content = new HorizontalLayout();;
private NativeSelect<String> select = new NativeSelect<>("The List");
private ListDataProvider<String> dataProvider = DataProvider.ofCollection(new ArrayList<>());
private Button addToListButton= new Button("Add to list");
private Button deleteFromListButton = new Button("-");
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
select.setVisibleItemCount(5);
select.setWidth("100px");
select.setDataProvider(dataProvider);
select.setEmptySelectionAllowed(false);
deleteFromListButton.setEnabled(false);
content.setMargin(true);
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
addEmailToList(emailField.getValue());
}
});
deleteFromListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
select.getSelectedItem().ifPresent(selectedItem -> removeSelectedEmailFromList());
}
});
select.addValueChangeListener(new HasValue.ValueChangeListener<String>() {
#Override
public void valueChange(HasValue.ValueChangeEvent<String> valueChangeEvent) {
deleteFromListButton.setEnabled(select.getSelectedItem().isPresent());
}
});
content.addComponents(emailField, addToListButton, select, deleteFromListButton);
addComponent(content);
}
private void addEmailToList(String email){
dataProvider.getItems().add(email);
select.getDataProvider().refreshAll();
emailField.clear();
}
private void removeSelectedEmailFromList(){
select.getSelectedItem().ifPresent(selectedItem -> dataProvider.getItems().remove(selectedItem));
select.setSelectedItem(dataProvider.getItems().isEmpty() ? null : dataProvider.getItems().iterator().next());
select.getDataProvider().refreshAll();
}
}
It looks like the following:
Would that be a possible option for you?
Related
I am just starting to learn Java Fx.
I have a combo box filled with objects. I dealt with toString() method, and I can see that name I wanted to display on the screen. But now I would like to make it editable, that user will enter its own text, and ComboBox will create a new object and put that text into the correct field. I know that problem is in converter FromString or ToString, but I cannot deal with it.
package mnet;
import javafx.application.Application;
import javafx.scene.control.ComboBox;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class sample extends Application {
Stage window;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Sample");
GridPane grid = new GridPane();
User usr1 = new User("Witold", "ciastko");
User usr2 = new User("Michał", "styk");
User usr3 = new User("Maciej", "masloo");
ComboBox<User> combo1 = new ComboBox<User>();
combo1.getItems().addAll(usr1, usr2, usr3);
combo1.setConverter(new StringConverter<User>() {
#Override
public String toString(User usr) {
return usr.getName();
}
#Override
public User fromString(String s) {
User usr = new User(s, "haslo");
combo1.getItems().add(usr);
return usr;
}
});
combo1.setEditable(true);
combo1.valueProperty().addListener((v, oldValue, newValue) -> {
System.out.println(newValue);
});
GridPane.setConstraints(combo1, 2, 1);
grid.getChildren().addAll(combo1);
Scene scene = new Scene(grid, 400, 200);
window.setScene(scene);
window.show();
}
}
package mnet;
public class User {
String user;
String password;
public User() {
this.user="";
this.password="";
}
public User(String user, String password){
this.user=user;
this.password=password;
}
public String getName(){
return this.user;
}
}
If I get rid of StringConverter it works correctly, but instead of name of user I only see address of Object like this "mnet.User#1f3b971"
EDIT: Added appropriately working code
You have a null pointer exception in you stringconverter since you can get a null User.
Your string converter should only convert User to/from String without modifying items since you don't know how many time it will be called.
To add a user I add an on event handler (when you type enter) on the combo that add a new user.
Note that thanks to the string converter you can call getValue on the combobox and get a user with the entered name
You should add a plus button to commit the user instead of my on event handler
here my working example :
public class Main extends Application {
Stage window;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Sample");
GridPane grid = new GridPane();
User usr1 = new User("Witold", "ciastko");
User usr2 = new User("Michał", "styk");
User usr3 = new User("Maciej", "masloo");
ComboBox<User> combo1 = new ComboBox<User>();
combo1.getItems().addAll(usr1, usr2, usr3);
combo1.setConverter(new StringConverter<User>() {
#Override
public String toString(User usr) {
return usr == null ? "" : usr.getName();
}
#Override
public User fromString(String s) {
User usr = new User(s, "haslo");
return usr;
}
});
combo1.setEditable(true);
combo1.addEventHandler(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.ENTER) {
combo1.getItems().add(combo1.getValue());
}
});
GridPane.setConstraints(combo1, 2, 1);
grid.getChildren().addAll(combo1);
Scene scene = new Scene(grid, 400, 200);
window.setScene(scene);
window.show();
}
I have created a text field:
TextField tfPost = new TextField("140 character word count...");
I also have a button with a ActionEvent Handler class:
Button btTweet = new Button("Tweet");
TweetHandlerClass btTweetHandler = new TweetHandlerClass();
btTweet.setOnAction(btTweetHandler);
Here is the body of the handler class:
class TweetHandlerClass implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent ae) {
System.out.println("Tweet button was clicked");
String newTweet = tfPost.getText();
}
}
What I need is for the string inside the TextField to be saved to a string variable when the Tweet button is clicked. How do I do this?
Variable scoping is a design question. The OP's question does not provide sufficient information to understand the full use case. One approach is to store the variable in the TweetHandlerClass. There are various rationales for and against such an approach. Nonetheless, for the specific question posed, it should suffice.
class TweetHandlerClass implements EventHandler<ActionEvent> {
private final TextField tfPost;
public TweetHandlerClass(TextField srcTxtFld)
{
this.tfPost = srcTxtFld;
}
#Override
public void handle(ActionEvent ae) {
System.out.println("Tweet button was clicked");
String newTweet = tfPost.getText();
}
}
Then, in the instantiating the Button:
TextField tfPost = new TextField("140 character word count...");
Button btTweet = new Button("Tweet");
TweetHandlerClass btTweetHandler = new TweetHandlerClass(tfPost);
btTweet.setOnAction(btTweetHandler);
If you really want the event handler to be a top-level class like that, you could give it a reference to a TextField as in #KevinO's answer. A slight variant on that would just be to have a reference to a Supplier<String>, which is more semantically what you really need:
class TweetHandlerClass implements EventHandler<ActionEvent> {
private final Supplier<String> tweetSupplier ;
TweetHandlerClass(Supplier<String> tweetSupplier) {
this.tweetSupplier = tweetSupplier ;
}
#Override
public void handle(ActionEvent ae) {
System.out.println("Tweet button was clicked");
String newTweet = tweetSupplier.get();
}
}
and then
Button btTweet = new Button("Tweet");
TweetHandlerClass btTweetHandler = new TweetHandlerClass(tfPost::getText);
btTweet.setOnAction(btTweetHandler);
This is pretty much overkill though. Normally you would just define the event handler with a lambda expression, instead of an explicit class:
Button btTweet = new Button("Tweet");
btTweet.setOnAction(e -> {
System.out.println("Tweet button was clicked");
String newTweet = tfPost.getText();
// ..
});
I'm looking for a way to pass fields with enter key in VerticalLayout or others. In vaadin book there an example with Shortcut and Handler listeners but I don't know how to implement that.
I'm trying this.
public class MyWindow extends Window implements Handler{
private Action action_enter; //pass fields with enter
private Action action_esc;
private TextField name, lastName;
public MyWindow(){
super("this window is opened");
VerticalLayout vLayout = new VerticalLayout();
setContent(vLayout);
center();
setModal(true);
setClosable(false);
setDraggable(false);
setResizable(false);
//actions
action_enter = new ShortcutAction("Enter key", ShortcutAction.KeyCode.ENTER, null);
action_esc = new ShortcutAction("Esc key", ShortcutAction.KeyCode.ESCAPE, null);
addActionHandler(this);
//fields
name = new TextField("Name");
lastName = new TextField("Last name");
name.focus();
vLayout.addComponent(name);
vLayout.addComponent(lastName);
}
#Override
public Action[] getActions(Object target, Object sender) {
return new Action[] { action_enter, action_esc };
}
#Override
public void handleAction(Action action, Object sender, Object target) {
/** close window with esc key */
if(action == action_esc){
close();
}
/** pass fields with enter key */
if(action == action_enter){
//here pass fields with enter key
}
}
}
any idea ?
try this way with ShortcutListener:
ShortcutListener skEnterListener = new ShortcutListener("Enter", ShortcutAction.KeyCode.ENTER, null){
#Override
public void handleAction(Object sender, Object target) {
if (target instanceof VerticalLayout) { // VerticalLayout or other
// sending fileds here
}
}
};
addShortcutListener(skEnterListener);
change focus of TextField using Enter instead Tab:
final TextField tf1 = new TextField("tf1");
tf1.setId("tf1");
final TextField tf2 = new TextField("tf2");
tf2.setId("tf2");
ShortcutListener skEnterListener = new ShortcutListener("Enter", ShortcutAction.KeyCode.ENTER, null){
#Override
public void handleAction(Object sender, Object target) {
if (target instanceof TextField) {
TextField field = (TextField) target;
if ("tf1".equals(field.getId())) {
tf2.focus();
}
if ("tf2".equals(field.getId())) {
tf1.focus();
}
}
}
};
addShortcutListener(skEnterListener);
There is no interface which does provide an accessor that would allow you finding out the currently focused component. Focus information can be acquired for some (but not all) field components through the com.vaadin.event.FieldEvents.FocusListener and com.vaadin.event.FieldEvents.BlurListener interfaces.
You could add for all possible fields a FocusListener and remember every time it's invoked, the current field in a variable. (Problem: not all fields provide a FocusListener.) Then when ENTER is pressed focus the next component according to the current focused field (remember the variable) that has to be focused (with the help of a simple List, LinkedList, Map, switch-case and so forth). To make it even better add a BlurListener as well to know when not to focus the next field.
Hope that helps.
I have a Navigator class and a custom DialogBox class which is descended from GridPane.
public DialogBox(final JDialog jdialog) {
Label lblKeyName = new Label("Enter New Key");
Label lblKeyType = new Label("Select Key Type");
TextField txtKeyName = new TextField();
ComboBox cboKeyType = new ComboBox();
txtKeyName.getText();
Button btnOk = new Button("OK");
Button btnCancel = new Button("Cancel");
btnOk.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
//TODO: Somehow return the values in the ComboBox and TextField
}
});
btnCancel.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
jdialog.setVisible(false);
}
});
txtKeyName.prefWidth(300);
cboKeyType.prefWidth(300);
this.add(lblKeyName, 0, 0);
this.add(lblKeyType, 0, 1);
this.add(txtKeyName, 1, 0);
this.add(cboKeyType, 1, 1);
this.add(btnOk, 0, 2);
this.add(btnCancel, 1, 2);
}
This is the constructor for my DialogBox.
JFXPanel fxPanel = new JFXPanel();
testBox = new DialogBox(jdialog);
fxPanel.setScene(new Scene(testBox));
jdialog.add(fxPanel);
jdialog.setVisible(true);
How can I retrieve the values in the TextField and ComboBox? I can slightly recall a long ago class where the professor mentioned a technique involving the calling class (Navigator in this case) implementing an Interface and then passing itself to the DialogBox class to retrieve values. Unfortunately I have not found anything and cannot remember how it is done.
Assuming that the dialog is modal, basically, once btnOk or btnCancel button is pressed you need to change some kind of state flag which you can interrogate to determine how the dialog was closed...
// This will also handle the use case where the user presses the "x" button...
private boolean wasCancelled = true;
//...
public boolean wasCancelled() {
return wasCancelled;
}
In you action listeners, you need to set the state appropriately.
btnOk.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
wasCancelled = false;
jdialog.setVisible(false);
}
});
btnCancel.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
wasCancelled = true;
jdialog.setVisible(false);
}
});
Now, once the dialog returns, you need to check this flag...
jdialog.add(fxPanel);
jdialog.setVisible(true);
if (!jdialog.wasCancelled()) {
//...
}
You then need to supply "getter" methods to allow a caller to extract the values from the dialog...
public String getKey() {
return txtKeyName.getText();
}
public String getType() {
return cboKeyType.getSelectionModel().getValue();
}
This will mean you will need to create these two fields as instance variables
My use case is that a List<String> is passed to a Jpanel and for each String in the List, the JPanel renders a UI component. This UI component consists of 3 buttons and my current code for my given use case is as follows. -- The code for the 'UI component' follows --
public class MacroEditorEntity implements ActionListener {
private String macro;
private JButton upButton;
private JButton downButton;
private JButton MacroDetailsButton;
public MacroEditorEntity(String macro) {
this.macro = macro;
upButton = new JButton("Up");
downButton = new JButton("Down");
MacroDetailsButton = new JButton(macro);
upButton.addActionListener(this);
downButton.addActionListener(this);
MacroDetailsButton.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent evt) {
if(evt.getSource().equals(MacroDetailsButton))
{
System.out.println(macro);
}
}
public JButton GetUpButton()
{
return upButton;
}
public JButton GetDownButton()
{
return downButton;
}
public JButton getMacroDetailsButton()
{
return MacroDetailsButton;
}
}
The code for my Panel is as follows --
public class MacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private List<String> stringlist;
public MacroEditor(List<String> list) {
this.stringlist = list;
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
for(String string : stringlist)
{
MacroEditorEntity entry = new MacroEditorEntity(string);
Box entryBox = Box.createHorizontalBox();
entryBox.add(entry.GetUpButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.getMacroDetailsButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.GetDownButton());
allButtons.add(entryBox);
}
add(allButtons);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
The code works fine for all Strings in the passed List. I want my Panel to pick up any change that may happen to the List like additions or deletions and add/remove relevant corresponding UI components accordingly. I think this can be done by using PropertyChangeListener but have not been able to account for that in my code.
Any ideas or suggestions on how i can make my Panel render/rerender stuff as soon as there are changes to the List would be of help.
What you need here is an observable collection. This should do it: http://commons.apache.org/dormant/events/apidocs/org/apache/commons/events/observable/ObservableCollection.html
Edit:
Here's the code snippet you requested:
public class ObservableListExample implements StandardPostModificationListener,
StandardPreModificationListener {
public static void main(String[] args) {
new ObservableListExample();
}
public ObservableListExample() {
ObservableList list = ObservableList.decorate(new ArrayList<>(),
new StandardModificationHandler());
list.getHandler().addPostModificationListener(this);
list.getHandler().addPreModificationListener(this);
//....
}
#Override
public void modificationOccurring(StandardPreModificationEvent event) {
// before modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
#Override
public void modificationOccurred(StandardPostModificationEvent event) {
// after modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
}
By the way: Another concept that helps to bind buisness objects to your GUI and react to modifications (bidirectionally) is Data Binding. Have a look at this, a Data Binding Library commonly used with Swing.