I'm having issue with dragging and dropping the label on each other. I could not able to drag the label and drop it onto another label. Event setOnDragDetected is detected and setOnDragDropped is not detected. These are the files.
FXML:
<HBox fx:id="hboxTwo" alignment="CENTER" spacing="100" style="-fx-background-color: orange;">
<padding>
<javafx.geometry.Insets bottom="25" left="25" right="25" top="25" />
</padding>
<children>
<Label fx:id="labelTwo" prefHeight="50.0" text="labelTWO" />
</children>
</HBox>
</children>
</HBox>
</center>
Controller:
public class HomeController implements Initializable {
#FXML
Label labelOne;
#FXML
Label labelTwo;
#FXML
HBox hboxOne;
#FXML
HBox hboxTwo;
#Override
public void initialize(URL url, ResourceBundle rb) {
labelOne.setOnDragDetected((MouseEvent event) -> {
System.out.println("source::" + event.getSource());
});
hboxTwo.setOnDragDropped((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
System.out.println("source::" + event.getSource());
});
hboxTwo.setOnDragEntered((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
System.out.println("source::" + event.getSource());
});
}
}
To make setOnDragDropped work, you have to set the TransferMode previously, and remove the according method call in setOnDragDropped:
boxTwo.setOnDragOver((DragEvent event) -> {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
});
boxTwo.setOnDragDropped((DragEvent event) -> {
Label source = (Label) event.getGestureSource();
boxTwo.getChildren().add(source);
event.setDropCompleted()
event.consume();
});
Just copy and pasting below, the relevant code for drag detection and dropping from the JavaFX 8 Drag and Drop tutorial.
When you compare the sample code to your code, you can see that you are missing stuff:
You aren't constructing a Dragboard containing drag and drop data.
You aren't extracting drag and drop data from a Dragboard on a drop.
You aren't notifying that the drag and drop event was completed.
You aren't appropriately consuming events.
source.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
/* Put a string on a dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
}
});
target.setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data is dragged over the target */
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target &&
event.getDragboard().hasString()) {
/* allow for moving */
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
}
});
target.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data dropped */
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
target.setText(db.getString());
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
}
});
Related
I create two windows and pass the Instance controller to the child window as a parameter. My problem is updating on the fly. I tried calling the refresh() method on treeTblState but nothing came of it.
First window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mo.specdoc.controllers.StateController">
<children>
<TreeTableView fx:id="treeTblState" showRoot="false">
<columns>
....
</columns>
</TreeTableView>
</children>
</AnchorPane>
Controller
public class StateController implements Initializable {
private static Map<Long, TreeItem> subdivisions = new HashMap<>();
private static StateController instance;
#FXML private TreeTableColumn<StateEntity, String> tblClmnTitle,...,tblClmnDelete;
#FXML private TreeTableView<StateEntity> treeTblState = new TreeTableView<>();
//Create root element (property setShow = false в FXML)
private StateEntity root = new StateEntity(0L,"State");
//Pattern Instance
public static StateController getInstance() {
if (instance == null) {
instance = new StateController();
}
return instance;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
tblClmnTitle.setCellValueFactory(new TreeItemPropertyValueFactory<>("postName"));
...
tblClmnDelete.setCellValueFactory(new TreeItemPropertyValueFactory<>("delete"));
treeGreate();
} catch ....
}
public void addChildren(StateEntity state) {
TreeItem<StateEntity> item = null;
for (Long key : subdivisions.keySet()) {
if (key == state.getSubdivisionId()) {
TreeItem<StateEntity> subdiv = subdivisions.get(key);
item = new TreeItem<StateEntity>(state);
subdiv.getChildren().add(item);
}
}
}
private void treeGreate() {
TreeItem<StateEntity> itemRoot = new TreeItem<StateEntity>(root);
treeTblState.setRoot(itemRoot);
//create tree - level 2
//subdivisions
for (SubdivisionEntity subdivision : subdivisionDAO.findAll()) {
StateEntity state = new StateEntity(
subdivision.getId(),
subdivision.getTitle()
);
state.getAdd().setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
createScene("Add post", new StateEntity(), subdivision.getId());
}
});
TreeItem<StateEntity> subdiv = new TreeItem<StateEntity>(state);
itemRoot.getChildren().add(subdiv);
subdivisions.put(state.getSubdivisionId(), subdiv);
//posts
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdiv.getChildren().add(item);
}
}
}
}
private void createScene(String title, StateEntity state, Long subdivisionId) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/state-edit.fxml"));
StateEditController stateEditController = new StateEditController(state, subdivisionId);
loader.setController(stateEditController);
Stage stage = new Stage();
Scene scene = new Scene(loader.load());
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
} catch ....
}
}
Result
Second window
<AnchorPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<children>
...elements...
</children>
</AnchorPane>
Controller
public class StateEditController implements Initializable {
....
#FXML
void saveAction(ActionEvent event) {
currentEntry.setSubdivisionId(subdivisionId);
currentEntry.setMaxAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setMinAmountPersonal(cmbBoxMaxAmount.getSelectionModel().getSelectedItem().intValue());
currentEntry.setAmplification(tglSwitchAmpl.isSelected());
currentEntry.setPostId(cmbBoxPost.getSelectionModel().getSelectedItem().getId());
if (currentEntry.getId() == null) {
stateDAO.save(currentEntry);
} else {
stateDAO.update(currentEntry);
}
stateController.addChildren(currentEntry); //call instance metod
}
#Override
public void initialize(URL location, ResourceBundle resources) {
....
}
}
Result
Everything is entered into the database successfully, but it is not updated when the window is closed 2.
I redraw the child elements using the clear method when adding an item
public void addChildren(StateEntity state) {
subdivisions.get(state.getSubdivisionId()).getChildren().clear();
List<StateEntity> childrens = stateDAO.findByIdSubdiv(state.getSubdivisionId());
if (!childrens.isEmpty()) {
for (StateEntity child : childrens) {
TreeItem<StateEntity> item = new TreeItem<StateEntity>(child);
subdivisions.get(state.getSubdivisionId()).getChildren().add(item);
}
}
}
My UI has a adding button and I want to assign a keyboard shortcut combination for that. I have failed to use the setAcceleartor for this purpose.
What is the easiest way to set up keyboard shortcuts in javafx applications?
button declaration in the UI:
<Button fx:id="addButton" alignment="CENTER" minWidth="-Infinity" mnemonicParsing="false" onAction="#addAction" prefHeight="31.0" prefWidth="130.0" text="Add" HBox.hgrow="ALWAYS" />
Controller button binding:
#FXML
private Button addButton;
The method that wants to setOnAction for the shortcut for the button:
public void addAction(ActionEvent event) throws SQLException, ClassNotFoundException {
if (validateInput()) {
String productName = productField.getText();
double unitPrice = Double.parseDouble(priceField.getText());
int quantity = Integer.parseInt(quantityField.getText());
double total = unitPrice * quantity;
ITEMLIST.add(new Item(productName, unitPrice, quantity, total));
calculation();
resetAdd();
productTableView.getSelectionModel().clearSelection();
ObservableList<Product> productsData = ProductDAO.searchProducts();
populateProducts(productsData);
searchField.setText("");
}
}
initialize() method:
#FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {
setSaveAccelerator(addButton);
}
The code I tried with setAccelerator:
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
In your setSaveAccelerator method, instead of directly calling addAction(ActionEvent event), just instruct the button to fire its event to its listeners such as: button.fire(). For example:
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button is null! "); // check that the button was injected properly through your fxml
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
EDIT
To also avoid the IllegalArgumentException you must attach the accelerator after the button is attached to a scene. I managed to achieve this by creating a public method in the controller to attach the accelerator after the scene has been set. Then, in the class where the scene is loaded the controller's method can be called which sets up this functionality. See the example below:
In the controller class (in my case MainController):
public void setup() {
setSaveAccelerator(button);
}
In your main class when loading the fxml file:
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
controller.setup(); // calls the setup method attaching the accelerators
FULL EXAMPLE
Main class:
public class Main extends Application{
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
primaryStage.setTitle("Shortcut example");
primaryStage.setScene(scene);
controller.setup();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maincontroller:
public class MainController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private Button button;
#FXML
private AnchorPane rootPane;
#FXML
private TextArea textarea;
#FXML
void action(ActionEvent event) {
textarea.setText("Action fired!!");
}
#FXML
void initialize() {
assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert textarea != null : "fx:id=\"textarea\" was not injected: check your FXML file 'MainFXML.fxml'.";
}
public void setup() {
setSaveAccelerator(button);
}
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button null!!");
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
}
MainFXML.fxml
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="button" layoutX="139.0" layoutY="350.0" mnemonicParsing="false" onAction="#action" text="Button" />
<TextArea fx:id="textarea" layoutX="73.0" layoutY="38.0" prefHeight="200.0" prefWidth="200.0" />
</children>
</AnchorPane>
Here is a general structure of a JavaFX android mobile application I am creating.
Using (AppBar or AppBarSearch these interchange dynamically) as nested controllers within Primary Application FXML.
ParentController
- AppBarController
- AppBarSearchController
primary.fxml
- appBar.fxml / appBarSearch.fxml
<AnchorPane fx:id="appBarPane" prefHeight="56.0" prefWidth="350.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<children>
<fx:include source="appbar.fxml" fx:id="appBar" />
<!-- initially shows AppBar but appBarSearch can also be here after clicking search button -->
</children>
</AnchorPane>
A button in each of the child fxml is responsible for changing between the fxml content from appBar/AppBarSearch.
The issue arises when I am dynamically change the content of the appBar to appBarSearch and back. I want the appBar menu button to communicate to the NavigationMenu to slide in and out.
I have been looking into whether I should somehow have an instance of the parentController inside the AppBarController.
I did use the following:
#FXML
private AppBarController appBarController; // injected via <fx:include fx:id="child" ... />
<fx:include source="appbar.fxml" fx:id="appBar" /> <- dynamically changes
//to dynamically change content in Panes
public static void setView(View view, Pane pane) {
try {
pane.getChildren().clear();
pane.getChildren().setAll((Node) FXMLLoader.load(Main.class.getResource(view.getTemplate()), resources));
} catch (IOException e) {
logger.error(e.getMessage());
}
}
#FXML
public void initialize() {
appBarController.setParentController(this);
}
#FXML
private void menuButtonClick (ActionEvent event) {
this.parentController.triggerMenu();
}
Initially the above works but after switching between appBarSearch and appBar it gives me a nullPointer to the parentController instance.
It may be that after switching between controllers dynamically it would not recognise the child controller.
I want the menuBtn in AppBar to open a navigationMenu so it would require to call triggerMenu() from within PrimaryController to start animations of it sliding in and out after the button in AppBar is clicked.
Thanks very much to the comments I was able to fix the issue and improve my understanding of how to use Controllers and included fxml.
Below is the general code I used to connect the child controllers with my primary controller and change views dynamically.
#FXML
private AppBarController appBarController; // injected via <fx:include fx:id="child" ... />
#FXML
private NavMenuController navMenuController; // injected via <fx:include fx:id="child" ... />
#FXML
private MainContentController mainContentController; // injected via <fx:include fx:id="child" ... />
#FXML
public void initialize() {
appBarController.setScreenParent(this);
navMenuController.setScreenParent(this);
mainContentController.setScreenParent(this);
}
/**
* Set View on a Pane Javafx component
*/
public <T extends Pane> boolean setView(View view, T pane) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(view.getTemplate()), resources);
pane.getChildren().clear();
pane.getChildren().setAll((Node) myLoader.load());
ControlledScreen childController = myLoader.getController();
childController.setScreenParent(this);
return true;
} catch (IOException e) {
logger.error(e.getMessage());
return false;
}
}
public void setMainContentView(View view) {
setView(view, mainContentPane);
}
public void setAppBarView(View view) {
setView(view, appBarPane);
}
public void setNavMenuView(View view) {
setView(view, navMenuPane);
}
public void triggerNavMenu() {
if (navMenuPane.getTranslateX() != 0) {
openNav.play();
appBarController.setMenuClosedImage();
} else {
closeNav.setToX(-(navMenuPane.getWidth()));
closeNav.play();
appBarController.setMenuClosedImage();
}
}
I created this small project to test key events. But when I press keys, it isn't behaving as I want. Actually I need key events for my Calculator project. I created a Calculator project and aside from mouse clicks, I want to add a feature where numbers or operators can be typed from a keyboard. Can anyone check this and help make it more functional?
public class FXMLDocumentController implements Initializable{
#FXML
private Label label;
#FXML
private Button backSpace;
#FXML
private Button spaceBar;
#FXML
private Button enter;
#FXML
void typedBS(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText(event.getText() + " typed.");
}
}
#FXML
void typedE(KeyEvent event) {
if (event.getCode()==KeyCode.ENTER) {
label.setText(event.getText() + " typed");
}
}
#FXML
void typedSB(KeyEvent event) {
if (event.getCode()==KeyCode.SPACE) {
label.setText(event.getText()+" typed");
}
}
#FXML
void PressBackSpace(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText("You pressed Back Space key!");
}
}
#FXML
void clickBackSpace(ActionEvent event) {
label.setText("You clicked Back Space key!");
}
#FXML
void clickEnter(ActionEvent event) {
label.setText("You clicked Enter key!");
}
#FXML
void clickSpaceBar(ActionEvent event) {
label.setText("You clicked SpaceBar key!");
}
#FXML
void pressEnter(KeyEvent event) {
if (event.getCode() == KeyCode.ENTER) {
label.setText("You pressed Enter key!");
}
}
#FXML
void pressSpaceBar(KeyEvent event) {
if (event.getCode() == KeyCode.SPACE) {
label.setText("You pressed SpaceBar key!");
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
If you look at this FXML file you can see my root node(The AnchorPane) has an onKeyPressed and onKeyReleased.
<AnchorPane id="AnchorPane" onKeyPressed="#handleOnKeyPressed" onKeyReleased="#handleOnKeyReleased" prefHeight="650.0" prefWidth="855.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atmfx.FXMLDocumentController">
<children>
<BorderPane layoutX="263.0" layoutY="94.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<left>
<AnchorPane fx:id="apLeftDisplay" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
In The Controller my method that handles these two KeyEvents are
#FXML
private void handleOnKeyReleased(KeyEvent event)
{
System.out.println();
Button tempButton = buttons.get(event.getText());
System.out.println("Released key text: " + event.getText());
System.out.println("Released key code: " + event.getCode());
if (tempButton != null) {
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.disarm();
tempButton.setStyle("");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.disarm();
tempButton.setStyle("");
}
}
#FXML
private void handleOnKeyPressed(KeyEvent event)
{
Button tempButton = buttons.get(event.getText());
System.out.println("Pressed key text: " + event.getText());
System.out.println("Pressed key code: " + event.getCode());
if (tempButton != null) {
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.ENTER)) {
tempButton = buttons.get("enter");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.BACK_SPACE)) {
tempButton = buttons.get("backspace");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
else if (event.getCode().equals(KeyCode.SPACE)) {
tempButton = buttons.get("space");
tempButton.arm();
tempButton.setStyle("-fx-background-color: blue");
}
}
In this code I added all of my buttons to a HasMap. That way in my keyevent handlers I was able to retrieve the button depending on the keyboard key I pressed.
Map<String, Button> buttons = new HashMap<>();
TheButtonsParentNode.getChildren().stream().filter((tempNode)
-> (tempNode instanceof Button)).map((
tempNode) -> (Button) tempNode).forEachOrdered((tempButton) -> {
buttons.put(tempButton.getText().toLowerCase(), tempButton);//adding button text and the button to hashmap
});
Define the event and the handling in the same thing.
try something like this:
if u are using a borderpane as an example
borderPane.setOnKeyPressed(this::handleKeyPress);
private void handleKeyPress(KeyEvent event) {
if (event.getCode() == KeyCode.BACK_SPACE) {
label.setText(event.getText() + " typed.");
}
if (event.getCode() == KeyCode.SPACE) {
label.setText("You pressed SpaceBar key!");
}
//TODO
}
Hope it helps
This question already has answers here:
Passing Parameters JavaFX FXML
(10 answers)
Access fields from another Controller in JavaFX
(2 answers)
Closed 8 years ago.
Scene 1 with its Scene1Controller! it has a text field (Customer Name) and a button!
When I click the button in scene 1, a on-screen keyboard will appear without closing the scene!
on-screen keyboard has it has its own controller!
on-screen keyboard has a textfield and complete keyboard
typed "stackoverflow" into the textfield of on-screen keyboard!
after pressing enter in the on-screen keyboard how do I retrieve the textfield value of the on-screen keyboard into the customer name field of scene 1?
SCENE 1:
<TextField fx:id="CustomerName" layoutX="14.0" layoutY="75.0" onAction="#TextBoxTextChanged" prefHeight="29.0" prefWidth="254.0"/>
<Button fx:id="OnScreenKeyBoardButton" layoutX="268.0" layoutY="75.0" mnemonicParsing="false" onAction="#ButtonNameClick" prefHeight="29.0" text="..." />
On-Screen Keyboard:
All the Key's and
Enter Button Code:
<Button fx:id="enterButton" layoutX="796.0" layoutY="210.0" minHeight="18.8" mnemonicParsing="false" prefHeight="40.0" prefWidth="90.0" text="Enter" onAction="#ButtonEnterClick"/>
Scene 1 Controller:
#FXML
public void ButtonNameClick(final ActionEvent event)
{
//opens on-screen keyboard
}
On-Screen Keyboard Controller:
#FXML
public void ButtonEnterClick(final ActionEvent event)
{
//code to be written to get the text field of the on-screen keyboard into the textfield of scene 1
}
Just create a property in the keyboard controller to represent the text, and observe it from the "Screen1Controller":
public class KeyboardController {
private StringProperty text = new SimpleStringProperty(this, "text", "");
public StringProperty textProperty() {
return text ;
}
public String getText() {
return text.get();
}
public void setText(String text) {
this.text.set(text);
}
#FXML
public void buttonEnterClick(ActionEvent event) {
text.set(// text from keyboard) ;
}
// ... everything else as before
}
And
public class Screen1Controller {
#FXML
private TextField customerName ;
// ...
#FXML
public void buttonNameClick(ActionEvent event) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Keyboard.fxml"));
Parent parent = loader.load();
KeyboardController controller = (KeyboardContoller) loader.getController();
controller.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
// update text field with newValue:
customerName.setText(newValue);
}
});
// show keyboard ...
}
// other code...
}