Dynamically adding UI controls when button is clicked - java

I a have tabPane with multiple tabs and inside one of the tabPane i have scrollPane. Inside the scrollPane i have a number of UI controls As shown on this image that i want to be dynamically added when i click a button.
This image here shows the UI setup of what i have. The UI controls are in an Hbox which is inside a Vbox.
When a user clicks the add button i would like to add another row with all the controls as shown in this image.
I have looked at post such as these and still cannt get to achieve the same effect on my code.
JavaFX continuous form dynamically add new row with content in gridpane,
Dynamically add elements to a fixed-size GridPane in JavaFX,
How to maintain GridPane's fixed-size after adding elemnts dynamically
When i followed the approach suggested by another stack-overflow member on the first question cited above, to use a listView the code worked. However his suggestion programmatically codes UI controls and when i try to change and use UI controls i created within scene builder it doesn't work. Also when i put the controls inside a tabPane my code doesn't work. Meaning when i click the add button it doesn't add the UI controls as expected.
My question is how can i dynamically add UI controls within a tab inside a tabPane when a button which is also in that tab is clicked.
Here is my code :
public class DynamicalyAddControlsController {
#FXML
private VBox vbox;
#FXML
private HBox hbox;
#FXML
private StackPane stack1;
#FXML
private Label taskid;
#FXML
private TextField taskname;
#FXML
private ComboBox<String> combobox;
#FXML
private TextArea textarea;
#FXML
private Button save;
private ObservableList<Car> cars = FXCollections.observableArrayList();
public void initialize(URL url, ResourceBundle rb) {
cars.addAll(new Car(CAR_TYPE.CAR1), new Car(CAR_TYPE.CAR2), new Car(CAR_TYPE.CAR3));
ListView<Car> carsListView = new ListView<>();
carsListView.setCellFactory(c -> new CarListCell());
carsListView.setItems(cars);
stack1.getChildren().add(carsListView);
}
private class CarListCell extends ListCell<Car> {
private ChoiceBox<CAR_TYPE> choiceBox = new ChoiceBox<>();
public CarListCell(){
choiceBox.setItems(FXCollections.observableArrayList
(CAR_TYPE.values()));
hbox.getChildren().addAll(taskid,taskname,combobox,choiceBox);
vbox.getChildren().addAll(hbox,textarea);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(vbox);
}
#Override
protected void updateItem(Car item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
} else {
setGraphic(vbox);
choiceBox.setValue(item.getType());
save.setOnAction(e -> {
Car newCar = new Car(choiceBox.getValue());
cars.add(newCar);
});
}
}
}
private enum CAR_TYPE {
CAR1, CAR2, CAR3;
}
private class Car {
private CAR_TYPE type;
public Car(CAR_TYPE type) {
this.type = type;
}
public CAR_TYPE getType() {
return type;
}
public void setType(CAR_TYPE type) {
this.type = type;
}
}
public void initialize(URL url, ResourceBundle rb) {
}
}
JavaApplication main class
#Override
public void start(Stage primaryStage) throws Exception{
Parent viewB = FXMLLoader.load(getClass().getResource("DynamicalyAddControls.fxml"));
Stage stageB = new Stage();
stageB.setScene(new Scene(viewB));
stageB.show();
}
public static void main(String[] args) {
launch(args);
}

Related

nullPointerException and how to update label the right way?

I have 2 scenes :
The first one has a "Balance" Label, which displays the balance from a variable.
The second scene is the deposit scene where the user adds to the balance.
(Each scene has its controller class)
I want the balance to be updated when the user goes back to the first scene.
what's the best way to do so? I couldn't find an event for the scene shown, I found online only a stage example which triggers an event when the window is closed, but here I am just changing scenes by changing the mainstage scene.
I have tried making an object of the first scene class inside the second scene's class and calling a method to change the label text when I click the back button but that didn't work.
Here's the code for the first scene where lbBalance is the label I want to update, and updateBal is the method I am using in the second scene class.
public class accountController extends Controller implements Initializable {
#FXML private Label gilbert;
#FXML private Label lbBalance;
#FXML private Button deposit;
#FXML private Button btn_showBalance;
private application.depositController depositController;
#Override
public void initialize(URL location, ResourceBundle resources) {
lbBalance.setText(String.valueOf(BAL));
}
#FXML
public void handleDeposit(ActionEvent event) throws IOException {
Parent depositParent = FXMLLoader.load(getClass().getResource("deposit.fxml"));
depositScene = new Scene(depositParent);
mainStage.setScene(depositScene);
mainStage.show();
}
public void updateBal() {
lbBalance.setText(String.valueOf(BAL));
}
}
Here's the second scene's class
accountController backtoscene= new accountController();
#FXML private Label info;
#FXML private Button btn_depositfinal;
#FXML private TextField depositamount;
#FXML private Button btn_back;
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
#FXML
public void handleDepositFinal(ActionEvent event) {
deposit(Integer.parseInt(depositamount.getText()));
info.setVisible(true);
}
#FXML
public void handleBackButton(ActionEvent event) throws IOException {
backtoscene.updateBal();
mainStage.setScene(newscene);
}
TL;DR calling the method is giving me a nullPointerException, is there any other way to update the balance label when getting back to previous scene?
NOTE: I haven't tested the code, I just wrote it freehand, but it gives you a general idea.
Your main issue is that you are creating a new AccountController in the DepositController. Meaning it's a different one than the one you originally instantiated.
public class AccountController extends Controller implements Initializable {
#FXML private Label gilbert;
#FXML private Label lbBalance;
#FXML private Button deposit;
#FXML private Button btn_showBalance;
private application.DepositController depositController;
#Override
public void initialize(URL location, ResourceBundle resources) {
lbBalance.setText(String.valueOf(BAL));
}
#FXML
public void handleDeposit(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("deposit.fxml"));
Parent depositParent = loader.load();
depositScene = new Scene(depositParent);
depositController = loader.getController();
depositController .setAccountController(this);
mainStage.setScene(depositScene);
mainStage.show();
}
public void updateBal() {
lbBalance.setText(String.valueOf(BAL));
}
}
Here's the second class where you need to set the AccountController to be the one you originally initialized :
public class DepositController extends Controller implements Initializable {
AccountController backtoscene;
#FXML private Label info;
#FXML private Button btn_depositfinal;
#FXML private TextField depositamount;
#FXML private Button btn_back;
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
public void setAccountController(AccountController controller){
backtoscene = controller;
}
#FXML
public void handleDepositFinal(ActionEvent event) {
deposit(Integer.parseInt(depositamount.getText()));
info.setVisible(true);
}
#FXML
public void handleBackButton(ActionEvent event) throws IOException {
backtoscene.updateBal();
mainStage.setScene(newscene);
}
}
Now you have access to the AccountController you originally initialized at the start, and the AccountController has access to the correct DepositController.

Adding listener in custom cell implementation of JavaFX listview

I'm implementing a desktop chat application using JavaFX.
I'm using a listview to show contacts.
I customized the list cell by following this link JavaFX custom cell factory with custom Objects.
When a contact becomes online/offline, the server notifies me appropriately, so I need to change the color of online icon accordingly.
Below is my code...
File: MainController.java
public class MainController implements Initializable {
#FXML
private ListView<ContactInfo> contactListView;
private ObservableList<ContactInfo> contactList = FXCollections.observableArrayList();
this.contactListView.setCellFactory( listView -> {
return new ContactCell();
}
}
File: ContactCell.java
public class ContactCell extends ListCell<ContactInfo> {
private final ContactItemController controller = new ContactItemController();
public ContactCell() {
}
#Override
protected void updateItem(ContactInfo item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
controller.setItem(item);
setGraphic(controller.getView());
}
}
}
File: ContactItemController.java
public class ContactItemController {
#FXML
private Pane pane;
#FXML
private ImageView contactImage;
#FXML
private Label contactName;
#FXML
private FontAwesomeIconView onlineIcon;
public ContactItemController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/snippets/ContactItem.fxml"));
fxmlLoader.setController(this);
try {
pane = fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setItem(ContactInfo item) {
this.contactName.setText(item.getName());
if(item.getOnline().getOnline())
this.onlineIcon.setFill(Color.LIGHTGREEN);
else
this.onlineIcon.setFill(Color.web("#838383"));
}
public Pane getView() {
return this.pane;
}
}
File: ContactInfo.java
public class ContactInfo {
private String name;
private BooleanProperty online;
// Getters and setters
..............
..............
}
I tried to add change listener to each item's boolean property inside setItem() method of the ContactItemController class.
But the listener is getting added more than once....
Is this the right way to do this?

Javafx: How to group mouse actions over an image

I am a newbie in Javafx and I am trying to fix a cropping over an image. I found an interesting solution for cropping with rubber band and now I try to modified this in my scenario.
I have two controllers. One controller has the crop button (CropController) and another controller which contain an image as ImageController.
CropController:
public class CropController {
#FXML
private void initialize() {}
#FXML
private void handleCrop(){
}
}
ImageController:
public class ImageController {
#FXML
private ImageView MainImage;
#FXML
private void initialize() {
modelSharedOne.imageProperty()
.addListener((obs, oldImage,newImage)-> MainImage.setImage(newImage));
}
#FXML
private void handleMousePresses(){}
#FXML
private void handleMouseDragged(){}
#FXML
private void handleMouseReleased(){}
}
Based on the rubber band solution I need to group those three mouse handlers. I do not know what could be the best solution for this. I have tried many solutions like creating a model between these two controllers but I have not managed to solve this solution.
Anyone knows how can I solve this?
My current solution
CropController:
public class ToolboxController {
#FXML
private void initialize() {
}
#FXML
private void handleCrop(){
SharedModelTwo.callHandlerInWorkstation();
}
}
SharedModelTwo:
public class CropImageModel {
ImageController imageController;
public void callHandlerInImage(){
imageController = new ImageController();
imageController.handleCrop();
}
}
ImageController:
public class ImageController {
#FXML
private ImageView MainImage;
#FXML
private void initialize() {
modelSharedOne.imageProperty()
.addListener((obs, oldImage,newImage)-> MainImage.setImage(newImage));
}
public void handleCrop() {
Image image = getImage();
ImageView imageView = new ImageView(image);
rect = new Rectangle( 0,0,0,0);
rect.setStroke(Color.BLUE);
rect.setStrokeWidth(1);
rect.setStrokeLineCap(StrokeLineCap.ROUND);
rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
Group imageLayer = new Group();
imageLayer.getChildren().add(imageView);
rubberBandSelection = new RubberBandSelection(imageLayer);
}
}
And rubberBandSelection is a class as it was in the linked. when I run that it does not hit when mouse pressed on image.

How to set String Or Text in JavaFX TextField Controller from another JavaFX Controller

In my javafx Application we have two FXML files first.fxml and second.fxml, same firstController.java and secondController.java now the main problem is first.fxml contain TextField name and on Button
when user will click on that button second.fxml display in second.fxml I have one ComboBox and one Button when user click second.fxml button I want to set that combobox value to first.fxml name TextField.
I am finding solution on Google from last three days but didn't get proper solution. In Java swing I was doing this using static public field that allowed me to access JFrame from another JFrame.
Eagerly waiting for helpful reply.
Expose a StringProperty from your SecondController. When the button is pressed, set its value:
public class SecondController {
private final StringProperty selectedValue = new SimpleStringProperty(this, "selectedValue", "");
public final StringProperty selectedValueProperty() {
return selectedValue ;
}
public final void setSelectedValue(String value) {
selectedValue.set(value);
}
public final String getSelectedValue() {
return selectedValue.get();
}
#FXML
private final ComboBox<String> comboBox ;
#FXML
private void handleButtonPress() {
selectedValue.set(comboBox.getValue());
}
}
In your FirstController, provide a method for setting the text:
public class FirstController {
#FXML
private TextField textField ;
public void setText(String text) {
textField.setText(text);
}
}
Now when you load the FXML files, just observe the property in the SecondController and call the method in FirstController when it changes:
FXMLLoader firstLoader = new FXMLLoader(getClass().getResource("first.fxml"));
Parent first = firstLoader.load();
FirstController firstController = firstLoader.getController();
FXMLLoader secondLoader = new FXMLLoader(getClass().getResource("second.fxml"));
Parent second = secondLoader.load();
SecondController secondController = secondLoader.getController();
secondController.selectedValueProperty().addListener((obs, oldValue, newValue) ->
firstController.setText(newValue));

JavaFX custom component get action from button in custom component

I want to get the event of two buttons in my custom component.
the component is a imageview with two buttons to move between images, but I need to get the position of the image that is currently displayed, Im storing the key of the image, but I need to know when a button have been pressed outside the custom component, so I can change a Label outside the custom component.
public class TransitionSlider extends AnchorPane {
#FXML
private AnchorPane transitionSliderPane;
#FXML
private ImageView transitionSliderImageView;
#FXML
private Button prevButton;
#FXML
private Button nextButton;
private Map<Integer,Image> imageMap;
private Image currentImage;
private DropShadow imageViewDropShadow;
private int currentKey = 1;
private Image[] images;
public TransitionSlider() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setController(this);
loader.setLocation(this.getClass().getResource("TransitionSlider.fxml"));
loader.setClassLoader(this.getClass().getClassLoader());
try {
loader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
prevButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= 1){
currentKey = currentKey + 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= imageMap.size()){
currentKey = currentKey - 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
}
// more code here...
}
I want a way to capture the event and get variables inside the component and change a label outside the custom component...
for example:
public class Gallery extends Application {
#FXML
TransitionSlider ts;
Label label;
#Override
public void start(Stage stage) throws Exception {
label = new Label();
TransitionSlider ts = new TransitionSlider();
ts.captureButtonEvent(){ // need a way to capture this
label.setText(ts.getCurrentKey());
}
// more code here....
}
If I understood your question correctly, you want a binding.. Follow these steps:
1) Put bindable field and its getter/setter into TransitionSlider:
private IntegerProperty currentKey = new SimpleIntegerProperty(1);
public int getCurrentKey() {
return currentKey.get();
}
public void setCurrentKey(int val) {
return currentKey.set(val);
}
public IntegerProperty currentKeyProperty() {
return currentKey;
}
2) Bind this property to label's text in Gallery:
label = new Label();
TransitionSlider ts = new TransitionSlider();
label.textProperty.bind(ts.currentKeyProperty().asString());
Alternatively, if you want to do stuff more than just changing label's text, you can add a change listener to currentKeyProperty:
ts.currentKeyProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
label.setText(newValue);
// do other stuff according to "oldValue" and "newValue".
}
});

Categories