javafx keytyped event not working - java

I have a JAVAFX editable combobox on which key typed and key pressed events are not firing while key released event is firing. However, If I change the combobox to textfield, it works.
FXML:
<ComboBox fx:id="combo_box" editable="true" layoutX="311.0" layoutY="194.0" prefHeight="26.0" prefWidth="300.0" promptText="Enter your name" onKeyTyped="#keyAction" />
FXMLController:
public void keyAction(KeyEvent event)
{
System.out.println("Works");
}
Help?

I found something which works. You can use the "getEditor" method of the combobox, to get the KEY_TYPED event works. Put this code in your controller :
this.combo_box.getEditor().setOnKeyTyped((KeyEvent e) -> {
System.out.println("Works");
});
Hope it helps

Related

Painting points on a MapLayer using GluonHQ Maps with JavaFX does not work

I am using GluonHQ Maps 1.0.3 in JavaFX and I have a problem painting nodes on the map.
I have a class GridMapController which has a MapView called gridMap.
I also have a class GridPaintLayer that extends the MapLayer and where all paintings are done. The GridPaintLayer is added to the GridMapController's gridMap with the method gridMap.addLayer(gridPaintLayer). This class (GridPaintLayer) has a method addNode(NodeInputModel) to which I pass a node (that contains lat/lon values) that shall be painted. The mentioned gridMap (MapView) can be right-clicked, a popup menu opens where I can choose "Create Node". This calls a method createNodeItemClicked() which is implemented in the GridPaintLayer (see below). When I click the "Create Node" menu item, everything works fine and a point appears on the map.
In another part of the programm I have an observable object that notifies its observers whenever a node is added. The notification message is handled in the GridMapController which then calls the addNode() method from the GridPaintLayer. But now, when I want the GridPaintLayer's baseMap to calculate the the points position on the scene, the method baseMap.getMapPoint(double lat, double lon) returns null because the baseMap's scene is null. So the node cannot be painted.
This happens i.e. when I want to create a node at another part of the application. The observable notifies the GridMapController (among others) correctly but then the problem appears as described above.
The methods below are both implemented in the GridPaintLayer class.
public void addNode(NodeInputModel nodeInputModel) {
Circle circle = new Circle(7, Color.RED);
circle.setCursor(Cursor.HAND);
circle.setVisible(true);
paintedNodes.put(nodeInputModel, circle);
Point2D mapPoint = baseMap.getMapPoint(nodeInputModel.getLat(), nodeInputModel.getLon());
if(mapPoint != null) {
circle.setTranslateX(mapPoint.getX());
circle.setTranslateY(mapPoint.getY());
} else {
System.out.println("Could not calculate node position, baseMap scene = null: " + (baseMap.getScene() == null));
}
AtomicReference<Double> orgSceneX = new AtomicReference<>(0d);
AtomicReference<Double> orgSceneY = new AtomicReference<>(0d);
circle.setOnMousePressed(mousePressedEvent -> {...});
circle.setonMouseClicked(mouseEvent -> {...});
this.getChildren().add(circle);
this.markDirty();
}
public void createNodeItemClicked(MouseEvent mouseEvent) {
// Convert mouseEvent position to geo position
MapPoint mapPoint = baseMap.getMapPosition(mouseEvent.getX(), mouseEvent.getY());
LatLon latLon = new LatLon(mapPoint.getLatitude(), mapPoint.getLongitude());
Point geoPosition = Utils.latlonToPoint(latLon);
// Create NodeInputModel
NodeInputModel nodeInputModel = new NodeInputModel();
nodeInputModel.setGeoPosition(geoPosition);
// TODO: set the other parameters
// Add node to Map
addNode(nodeInputModel);
// Send update message to GridModel
UpdateMessage updateMessage =
new UpdateMessage(null, Arrays.asList(nodeInputModel), UpdateMessage.UpdateType.ADD);
gridMapController.sendUpdateMessage(updateMessage);
}
I have tried several things to "reactivate" the scene but nothing worked. Is there a way to do that properly?
I hope I have explained everything clearly. If not, please feel free to ask.
Edit:
I am using multiple FXML files. The root FXML (MainView.fxml):
<VBox xmlns="http://javafx.com/javafx/8.0.172-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:id="main"
fx:controller="edu.ie3.controller.main.MainController">
<fx:include fx:id="io" source="IoView.fxml"/>
<fx:include fx:id="tool" source="ToolView.fxml"/>
<HBox prefHeight="${main.height}">
<!-- vertical button bar for grid info -->
<ButtonBar fx:id="leftButtonBar" rotate="-90">
<buttons>
<Button fx:id="gridInfoButton" text="Grid Info"/>
</buttons>
</ButtonBar>
<SplitPane fx:id="splitPane" prefWidth="${main.width}">
<fx:include fx:id="gridTab" source="GridTabView.fxml"/>
</SplitPane>
</HBox>
<fx:define>
<fx:include fx:id="gridInfo" source="GridInfoView.fxml"/>
<fx:include fx:id="gridMap" source="GridMapView.fxml"/>
<fx:include fx:id="gridSchema" source="GridSchemaView.fxml"/>
</fx:define>
</VBox>
The Splitpane which contains the GridMapView:
<TabPane
xmlns="http://javafx.com/javafx/8.0.172-ea"
xmlns:fx="http://javafx.com/fxml/1"
tabClosingPolicy="UNAVAILABLE"
fx:id="gridTabPane"
fx:controller="edu.ie3.controller.gridTab.GridTabController">
<Tab fx:id="gridMapTab" text="Map View">
<fx:include fx:id="gridMapLayer" source="GridMapView.fxml"/>
</Tab>
<Tab fx:id="gridSchemaTab" text="Schema View">
<fx:include fx:id="gridSchema" source="GridSchemaView.fxml"/>
</Tab>
</TabPane>
The GridMapView.fxml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.gluonhq.maps.MapView?>
<MapView xmlns="http://javafx.com/javafx/8.0.172-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:id="gridMap"
fx:controller="edu.ie3.controller.gridTab.gridMap.GridMapController">
</MapView>
I have already tried commenting some statements out to avoid double including the GridMapView but nothing worked.
I solved it.
The problem was that I included the GridMapController once in the MainController and once in the GridTabController. I removed the fx:include from the MainController and now the GridMapController only initializes once.
Nevertheless thank you very much, José Pereda, you brought me on the right way.

Javafx button event needs double clicking

I have a javafx scene with several buttons within. The only way that the events of the button will be activated is by double cliking. In fxml the button is using the following action method onAction="#Button1Action". How can I change the functionality of the button1Action event from double click to just one click?
The function onAction:
#FXML
private void Button1Action(ActionEvent event) {
}
and the fxml code:
<Button id="button1" fx:id="button1" maxHeight="1.79.." maxWidth="1.79.." mnemonicParsing="false" onAction="#Button1Action" text="Answer" GridPane.columnIndex="3" GridPane.columnSpan="3" GridPane.halignment="CENTER" GridPane.rowIndex="7" GridPane.valignment="CENTER">
<GridPane.margin>
<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
</GridPane.margin>
</Button>
You did not posted the body of Button1Action, but most probably it is looking like:
#FXML
private void Button1Action(ActionEvent event) {
button1.setOnAction(e -> System.out.println("Button clicked"));
}
What happens here, that you assign the listener inside the listener, so the actual listener body will be executed on the second click.
Trivial fix is:
#FXML
private void Button1Action(ActionEvent event) {
System.out.println("Button clicked");
}

Java FX Scene Builder How to make event key pressed

I'm new in Java and Java FX and I'm trying to make a panel with buttons using scene builder. I want my application to respond only on arrow key pressed. I made the following method in my Controller class:
public void keyPressed(KeyEvent key) {
switch(key.getCode()) {
...some code here
}
}
After that I selected this method in scene builder, but when I run my application nothing happens when I press an arrow key.
Can somebody help me?
Without seeing the rest of your code and FXML it is difficult to tell, here is full example
Possible things you missed
Adding keyPress as an action in the FXML
Adding the #FXML annotation to the keyPressed() method
Code
public class Main extends Application {
private class Controller {
#FXML // <== perhaps you had this missing??
void keyPressed(KeyEvent event) {
switch (event.getCode()) {
case LEFT:
case KP_LEFT:
System.out.println("to the left");
break;
case RIGHT:
case KP_RIGHT:
System.out.println("to the right");
break;
default:
break;
}
}
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/foo.fxml"));
loader.setController(new Controller());
primaryStage.setScene(new Scene(loader.load()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane onKeyPressed="#keyPressed" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button mnemonicParsing="false" text="Button" />
</children>
</GridPane>
KeyCode also allows to compare with a specific key
#FXML
private void keyPressed(KeyEvent keyEvent)
if (keyEvent.getCode() == KeyCode.ENTER) {
// do some actions
}
}
You can get all the key code from here. Without using switch it's one of the good way.
KeyCode has a method isArrowKey(), so if you're calling your keyPressed method from your event handler, you could do:
public void keyPressed(KeyEvent key){
if(key.getCode().isArrowKey()){
...some code here
}
}
If you need to do different things based on which arrow key is pressed, make sure your switch cases are comparing to KeyCode.UP/DOWN/LEFT/RIGHT. If they are, it's likely that either you didn't set the event handler properly or your GUI is hung because of a threading problem. Post where your handling the event if you need more help.

JavaFX Thread with progressindicator not spinning, work done in non FXThread

I would like to indicate a loading process with the JavaFX progressindicator.
The problem is that the indicator isn't rotating when the code is executed the first time.
On the second time it is rotating that means the bindings are working, both the disable and the visible-property.
I also change the state in the FX thread and do the work on a seperate thread so i can
see no error here.
Does anyone see the problem?
Controller:
//vars
#FXML private ProgressIndicator loadingIndicator;
private BooleanProperty isSaving = new SimpleBooleanProperty(false);
#Override
public void initialize(URL location, ResourceBundle resources) {
parentToDisable.disableProperty().bind(isSaving);
loadingIndicator.visibleProperty().bind(isSaving);
}
#FXML
void onSave(ActionEvent event) {
isSaving.set(true); //<<<<<<<<<problem
// separate non-FX thread
new Thread() {
// runnable for that thread
public void run() {
//++++++++++//long running task......+++++++++++++++
// update ProgressIndicator on FX thread
Platform.runLater(new Runnable() {
public void run() {
isSaving.set(false); //<<<<<<<<<problem
}
});
}
}.start();
}
Fxml:
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="600.0"
prefWidth="500.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com
/fxml/1">
<content>
<StackPane>
<children>
<VBox fx:id="parentToDisable">
<!-- shortened -->
<Button fx:id="btnSave" mnemonicParsing="false" onAction="#onSave"
text="Speichern" />
<!-- shortened -->
</VBox>
<ProgressIndicator fx:id="loadingIndicator"
maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="60.0" prefWidth="60.0"
visible="false" />
</children>
</StackPane>
</content>
</ScrollPane>
That looks like a bug: RT-33261. It seems to be fixed in the latest pre-release of JDK 8u20.
My guess is it's to do with the progress indicator not being visible when the scene graph is first constructed. As a workaround, remove the visible="false" attribute from the ProgressIndicator in the fxml file, and wrap the binding in a Platform.runLater(...) call:
public void initialize(...) {
Platform.runLater(() -> loadingIndicator.visibleProperty().bind(isSaving));
}

Java FX TableView not editable with FXML

I am currently developing a Java FX Application that includes a TableView. In this TableView I want to use an editable CheckBoxTableCell.
First I had all components added in the Java Code using MiG Layout. With that configuration everything was editable (size of columns, order of columns, checkbox). When I transferred all the code to FXML using Scene Builder everything worked fine except the TableView. I could not figure out how to set the CellFactory and the CellValueFactory in the FXML file (I've seen a couple of examples, but couldn't get it to work). So I decided to set those factories in the initialize() method:
voteCol.setCellValueFactory(new Callback<CellDataFeatures<DataItem, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(
CellDataFeatures<DataItem, Boolean> arg0) {
return arg0.getValue().voteProperty();
}
});
voteCol.setCellFactory(new Callback<TableColumn<DataItem, Boolean>, TableCell<DataItem, Boolean>>() {
#Override
public TableCell<DataItem, Boolean> call(
TableColumn<DataItem, Boolean> arg0) {
return new CheckBoxTableCell<DataItem, Boolean>();
}
});
voteCol.setEditable(true);
dataTableView.setEditable(true);
Here is the FXML Code generated by Scene Builder
<TableView fx:id="dataTableView" editable="true"
mouseTransparent="true" pickOnBounds="false" prefHeight="-1.0"
prefWidth="-1.0" AnchorPane.bottomAnchor="41.0"
AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0"
AnchorPane.topAnchor="80.0">
<columns>
<TableColumn fx:id="voteCol" maxWidth="5000.0" minWidth="10.0"
prefWidth="32.0" text="Vote" visible="true" />
...
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
The databinding with the DataItem is working correctly.
If anyone could point me in the right direction I'd be very thankful.
Most probably your initialization is never called.
Make sure your Controller implements Initializable interface and initialize() method has correct signature:
public class MyController implements Initializable {
#Override
public void initialize(URL location, Resources resources) {
//....
}
}

Categories

Resources