I'm using a JFXDrawer, SalesDrawer containing a VBox and 2 JFXButtons inside it. The contents of the JFXDrawer have a separate controller, SalesDrawerController.java. The controller file, SalesController.java that contains the JFXDrawer consists of 2 anchor panes that I want to set visible on click of the buttons in the JFXDrawer. Till now I was using a set of static boolean variables and making sure on click of a button in the JFXDrawer one of the variables is set to true. Then in the SalesController.java, I used a TimerTask object to check which of these variables were true and set the needed anchor pane to visible. Is there a better way of doing this?
SalesDrawerController.java
public class SalesDrawerController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void button1Hit(MouseEvent event) {
SalesController.SD[0]=true;
}
#FXML
private void button2Hit(MouseEvent event) {
SalesController.SD[1]=true;
}
}
SalesController.java
public class SalesController implements Initializable {
public static boolean SD[]= {false,false};
static boolean tock=true;
#FXML
private AnchorPane eq_newpane;
#FXML
private AnchorPane eq_delpane;
#FXML
private JFXHamburger SalesHam;
#FXML
private JFXDrawer SalesDraw;
public void initialize(URL url, ResourceBundle rb) {
eq_newpane.setVisible(true);
eq_delpane.setVisible(false);
eq_newpane.setDisable(false);
eq_delpane.setDisable(true);
VBox box = FXMLLoader.load(getClass().getResource("/fxml/SalesDrawer.fxml"));
SalesDraw.setSidePane(box);
HamburgerBackArrowBasicTransition transition = new HamburgerBackArrowBasicTransition(SalesHam);
transition.setRate(-1);
SalesHam.addEventHandler(MouseEvent.MOUSE_PRESSED,(e)->{
transition.setRate(transition.getRate()*-1);
transition.play();
if(SalesDraw.isShown()){
SalesDraw.close();
SalesDraw.toBack();
}
else{
SalesDraw.toFront();
SalesDraw.open();
}
});
threadtock();
}
public void threadtock() {
final java.util.Timer timer = new java.util.Timer();
final TimerTask delayedThreadStartTask;
delayedThreadStartTask = new TimerTask() {
public void run() {
try
{
if(tock){
if(SD[0])
{
eq_newpane.setVisible(true);
eq_delpane.setVisible(false);
eq_newpane.setDisable(false);
eq_delpane.setDisable(true);
}
else if(SD[1]){
eq_delpane.setVisible(true);
eq_newpane.setVisible(false);
eq_newpane.setDisable(true);
eq_delpane.setDisable(false);
}
if(tock)
{
threadtock();
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
};
timer.schedule(delayedThreadStartTask, 500);
};
}
Use synchronization mechanisms so you do not inherit deadlocks in your code from 2 or more threads attempting to operate on the same variable at once.
Figuring out how to properly synchronize your threads can be a pain, and it is very hard to explain, let alone debug. So, my best advice is to use google for "Java Concurrency" and "Java Thread Synchronization".
Otherwise, you should start a thread/threads from your main application to preform your intended operations
Related
I have these two MouseEvents, and I want them "centralized" because I have other nodes that will do the same thing. I know that this can be done if you create multiple MouseEvents, but I reckon that there must be a shorter way.
public class scMain implements Initializable {
#FXML
private Button btnView, btnView2;
#FXML
private HBox hboxView, hboxView2;
public void translateTransition(Node node) {
TranslateTransition translate = new TranslateTransition();
translate.setNode(node);
// ...animation stuff
translate.play();
}
public void fadeOffTransition(Node node) {
FadeTransition fade = new FadeTransition();
fade.setNode(node);
// ...animation stuff
fade.play();
}
public void fadeInTransition(Node node) {
FadeTransition fade = new FadeTransition();
fade.setNode(node);
// ...animation stuff
fade.play();
}
#FXML
void btnMouseEnter(MouseEvent event) {
fadeOffTransition(btnView);
hboxView.setVisible(true);
translateTransition(hboxView);
}
#FXML
void btnMouseExit(MouseEvent event) {
fadeInTransition(btnView);
hboxView.setVisible(false);
}}
What I tried but obviously didn't work :
#FXML
void btnMouseEnter(MouseEvent event, Node first, Node second) {
fadeOffTransition(first);
second.setVisible(true);
translateTransition(second);
}
#FXML
void btnMouseExit(MouseEvent event, Node first, Node second) {
fadeInTransition(first);
second.setVisible(false);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
btnMouseEnter(event, btnView2, hboxView2); //error cannot find symbol
btnMouseExit(event, btnView2, hboxView2); //error cannot find symbol
}
Any suggestions would be appreciated.
Register the event handler in the intialize() method, instead of in the FXML. Remove the onMouseEnter and onMouseExit attributes from the FXML file, and modify the controller as follows:
void btnMouseEnter(Node first, Node second) {
fadeOffTransition(first);
second.setVisible(true);
translateTransition(second);
}
void btnMouseExit(Node first, Node second) {
fadeInTransition(first);
second.setVisible(false);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
btnView2.setOnMouseEnter(e -> btnMouseEnter(btnView2, hboxView2));
btnView2.setOnMouseExit(e -> btnMouseExit(btnView2, hboxView2));
}
You can also make the obvious reductions in code, e.g.:
private void registerMouseHandlers(Button button, Node target) {
button.setOnMouseEnter(e -> btnMouseEnter(button, target));
button.setOnMouseExit(e -> btnMouseExit(button, target));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
registerMouseHandlers(btnView, hboxView);
registerMouseHandlers(btnView2, hboxView2);
}
And if you have a fairly large number of these, it might also be more efficient (in terms of lines of code) to do
#Override
public void initialize(URL location, ResourceBundle resources) {
List<Button> buttons = List.of(btnView, btnView2);
List<Node> targets = List.of(hboxView, hboxView2);
for (int i = 0 ; i < buttons.size() ; i++) {
registerMouseHandlers(buttons.get(i), targets.get(i));
}
}
You could also consider creating a custom component encapsulating your button and hbox, which simply implements the event handlers for a single button-hbox pair, and then including them in the FXML using fx:include.
I'm making an application with JavaFX and Scene Builder.
I have two controllers:Controller and FontController
I have Main class that launch my program and open Stage with first fontroller (Controller)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/card/card.fxml"));
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a label and a button in Controller. When I click on the button a method is called and new window with second controller appears(FontController):
#FXML private Button btnFont;
#FXML private Label category1
#FXML
void changeFont(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("font.fxml"));
Parent rootFont = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Select Font");
stage.setScene(new Scene(rootFont));
stage.show();
} catch (Exception e) {
System.out.println("can't load new window");
}
}
There is the button "OK" and label in FontCOntroller:
#FXML private Label fontLabel;
#FXML private Button btnFontOk;
Please tell me, what should I do to send and apply text from label in FontController when I click on the burtton "OK" to label in Controller?
SOLUTION FOUND:
I created class "Context" in my project directory to make all controllers communicate each other. You can add as many controllers as you want there.
Here it looks like:
package card;
public class Context {
private final static Context instance = new Context();
public static Context getInstance() {
return instance;
}
private Controller controller;
public void setController(Controller controller) {
this.controller=controller;
}
public Controller getController() {
return controller;
}
private FontController fontController;
public void setFontController(FontController fontController) {
this.fontController=fontController;
}
public FontController getFontController() {
return fontController;
}
}
Controller:
I created getters and setters (ALT + Insert in IDEA) for Label that I wanna change
public Label getCategory1() {
return category1;
}
public void setCategory1(Label category1) {
this.category1 = category1;
}
To get FontController variables and methods through Context class I placed line of code
//getting FontController through Context Class
FontController fontCont = Context.getInstance().getFontController();
I registered Controller in Context class through my initialize method (my class implements Initializable)
#FXML
public void initialize(URL location, ResourceBundle resources) {
//register Controller in Context Class
Context.getInstance().setController(this);
}
FontController:
to get Controller variables and methods I placed this code:
//getting Controller variables and methods through Context class
Controller cont = Context.getInstance().getController();
I also registered FontController in Context class through initialize method:
#Override
public void initialize(URL location, ResourceBundle resources) {
//register FontController in Context Class
Context.getInstance().setFontController(this);
}
Method that send text and text color from label in this FontController to label in Controller when I click on button:
#FXML
void applyFont(ActionEvent event) {
cont.getCategory1().setText(fontLabel.getText());
cont.getCategory1().setTextFill(fontLabel.getTextFill());
}
*By creating Context class you can make controllers communicate each other and create as many controllers as you want there. Controllers see variables and methods of each other
I have an app with 3 fxml files.
The 1st is main window of app and most of work im doing here, but the 2 others fxml files allows me to change some settings and add more records to app.
I made it that 2 small fxml files extends the main file.
Basically:
public class FXMLDocumentController implements Initializable {
#FXML
private TextField txt;
#FXML
private Stage stage;
Symulacja symulacja = new Symulacja();
Promocja promocja = new Promocja();
#Override
public void initialize(URL url, ResourceBundle rb) {
txt.textProperty().bindBidirectional(promocja.getProgKwotyZetonow(), new NumberStringConverter());
labelProgKwotyZetonow.textProperty().bind(promocja.getProgKwotyZetonow().asString());
}
#FXML
public void zmienUstawienia() {
symulacja.zmienOkno("FXMLUstawienia.fxml", "Ustawienia");
}
#FXML
public void dodajKlientow() {
symulacja.zmienOkno("FXMLDodajKlienta.fxml", "Dodawanie");
}
}
It's main fxml Controller
public class FXMLUstawieniaController extends FXMLDocumentController{
#FXML
private TextField textProgKwota;
#FXML
private Button btnZatwierdzUstawienia;
#FXML
private TextField textProgIlosc;
#FXML
public void zatwierdzUstawienia() {
promocja.setProgIlosciZetonow(Integer.parseInt(textProgIlosc.getText()));
}
#Override
public void initialize(URL url, ResourceBundle rb) {
textProgKwota.textProperty().bindBidirectional(promocja.getProgKwotyZetonow(), new NumberStringConverter());
}
}
As you see I'm using properties to dynamically change what is showing in labelProgKwotyZetonow and textProgKwota.
When im not using the 2nd window txt and labelProgKwotyZetonow works fine, but when I'm initializing 2nd window I always get 0 in TextField and even if I change it this is not changing labelProgKwotyZetonow.
It's strange to me because when I'm souting (System.out.println) the value of promocja.getProgKwotyZetonow() in some places I get:
In function initialize in the 2nd controller - 0.
In function zmienUstawienia in 1st controller - the value from txt/labelProgKwotyZetonow.
In function zatwierdzUstawienia in 2nd controller - the value from textProgKwota.
I have this function in the controller class of the relevant fxml. I need this function to be fired on focus out from a textfield, but scene builder doesn't have an event similar to onfocusout. How to achieve this using the control class?
#FXML
private void ValidateBikeNo(){
Tooltip error = new Tooltip("This bike no exists");
BikeNoIn.setTooltip(error);
}
You can attach a focusListener to the TextField and then execute the code inside it. The listener can be attached inside the initialize() method of the controller.
public class MyController implements Initializable {
...
#FXML
private Textfield textField;
public void initialize() {
...
textField.focusedProperty.addListener((ov, oldV, newV) -> {
if (!newV) { // focus lost
// Your code
}
});
.....
}
}
You have to use method textField.focusedProperty() instead of textField.focusProperty
public class MyController implements Initializable {
...
#FXML
private Textfield textField;
public void initialize() {
textField.focusedProperty().addListener((ov, oldV, newV) -> {
if (!newV) { // focus lost
// Your code
}
});
}
}
I have a BorderPane, onto which I placed a MenuBar. At the center of the BorderPane I display differnt AnchorPanes depending on the MenuItem selected. So far so good.
Now, how do I make sure that the Menus change behavior in response to the item selected in the child AnchorPane? So for example if the user selects "Edit", there will be a different action depending on whether the item currently higlighted is a user account, a file etc.
So far I made something along these lines:
The BorderPane controller:
public class MenuTest implements Initializable{
#FXML
private BorderPane borderPaneMain;
#FXML
private AnchorPane anchorPaneMain;
#FXML
private Menu menuEdit;
#FXML
private MenuItem itemEdit;
static String menuMode;
static String entityName;
public MenuTest(){
menuMode ="";
entityName = "";
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
AnchorPane anchor;
try {
anchor = (AnchorPane) new FXMLLoader().load(getClass().getResource("views/MainView.fxml"));
borderPaneMain.setCenter(anchor);
} catch (IOException e) {
e.printStackTrace();
}
}
protected static void setMenuMode(String menuMd, String entityNm){
entityName = entityNm;
menuMode = menuMd;
}
#FXML
private void onEditClick(){
if(entityName.equals(AnchorTest.FILE)){
//Launches correct edit view
new FXMLLoader().load(getClass().getResource("views/EditFile.fxml"));
//Passes the name of the entity so that the controller can retrieve its data
FileEditController.setFile(entityName);
}else if(entityName.equals(AnchorTest.PERSON)){
new FXMLLoader().load(getClass().getResource("views/EditPerson.fxml"));
PersonEditController.setFile(entityName);
}
}
}
The child AnchorPane controller:
public class AnchorTest implements Initializable{
public static final String PERSON = "PERSON";
public static final String FILE = "FILE";
ObservableList<String> peopleList;
ObservableList<String> fileList;
#FXML
private ListView<String> listPeople;
#FXML
private ListView<String> listFiles;
#Override
public void initialize(URL location, ResourceBundle resources) {
peopleList = FXCollections.observableArrayList("Frank","Matin","Anne");
fileList = FXCollections.observableArrayList("hello.txt","holiday.jpg","cv.doc");
listPeople.setItems(peopleList);
listFiles.setItems(fileList);
}
#FXML
private void personSelected(){
MenuTest.setMenuMode(this.PERSON, listPeople.getSelectionModel().getSelectedItem());
}
#FXML
private void fileSelected(){
MenuTest.setMenuMode(this.FILE, listFiles.getSelectionModel().getSelectedItem());
}
}
However I'm not sure that it's the best solution, especially considering the if/elseif statement will need to be altered whenever I add a new element type and its corresponding edit options. So is there any way that I can do this better?
I think if your application has only a few (2-4) different types of "things" that are represented by a AnchorPane, then your approach is totally fine. An alternative to your approach is the state pattern. In that case, your currently selected "item type" would be your state.