I was looking on the internet but I didn't find good information for it. I'm trying to detect key presses every time the app is running. I'm using JavaFX and running it with FXML. I tryed a lot of thing but none work. Please help me.
You should check out the Ensemble sample. Here's the key listener code.
/**
* Copyright (c) 2008, 2012 Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*/
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
/**
* A sample that demonstrates various key events and their usage. Type in the
* text box to view the triggered events: key pressed, key typed and key
* released. Pressing the Shift, Ctrl, and Alt keys also trigger events.
*
* #see javafx.scene.input.KeyCode
* #see javafx.scene.input.KeyEvent
* #see javafx.event.EventHandler
*/
public class KeyEventsSample extends Application {
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
//create a console for logging key events
final ListView<String> console = new ListView<String>(FXCollections.<String>observableArrayList());
// listen on the console items and remove old ones when we get over 20 items in the list
console.getItems().addListener(new ListChangeListener<String>() {
#Override public void onChanged(Change<? extends String> change) {
while (change.next()) {
if (change.getList().size() > 20) change.getList().remove(0);
}
}
});
// create text box for typing in
final TextField textBox = new TextField();
textBox.setPromptText("Write here");
textBox.setStyle("-fx-font-size: 34;");
//add a key listeners
textBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
console.getItems().add("Key Pressed: " + ke.getText());
}
});
textBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
console.getItems().add("Key Released: " + ke.getText());
}
});
textBox.setOnKeyTyped(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
String text = "Key Typed: " + ke.getCharacter();
if (ke.isAltDown()) {
text += " , alt down";
}
if (ke.isControlDown()) {
text += " , ctrl down";
}
if (ke.isMetaDown()) {
text += " , meta down";
}
if (ke.isShiftDown()) {
text += " , shift down";
}
console.getItems().add(text);
}
});
VBox vb = new VBox(10);
vb.getChildren().addAll(textBox, console);
root.getChildren().add(vb);
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
This worked for me:
At the FXML add the onKeyPressed attribute in the element. Here is an example, note that the onKeyPressed attribute is in the AnchorPane element.
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" onKeyPressed="#handleKeyPressed" xmlns:fx="http://javafx.com/fxml" fx:controller="com.jtetris.jtetris.FXMLController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
Next, add the method (handleKeyPressed) inside the controller
public class FXMLController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#FXML
private void handleKeyPressed(KeyEvent ke){
System.out.println("Key Pressed: " + ke.getCode());
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Finally, load the fxml in the start method.
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml"));
Scene scene = new Scene(root);
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I am using TitledPanes ScrollPanes and TableViews and I have the problem, when I collapse a titledPane, the horizontal ScrollBar of the TableView resets.
Here is a code example where you can verify it:
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private AnchorPane content;
#FXML
private TitledPane titledPane;
#FXML
private TableView<Object> tableView;
#Override
public void initialize(URL location, ResourceBundle resources) {
titledPane.prefHeightProperty().bind(content.heightProperty());
tableView.prefWidthProperty().bind(content.widthProperty());
tableView.getColumns().forEach(col -> col.setPrefWidth(300)); // to have enough "space" to scroll
tableView.setItems(FXCollections.observableArrayList(new Object()));
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.testscroll.Controller"
fx:id="content">
<TitledPane fx:id="titledPane">
<TableView fx:id="tableView">
<columns>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
</columns>
</TableView>
</TitledPane>
</AnchorPane>
Any idea how can I prevent the scroll of the tableview to reset every time I collapse the pane?
After a bit of digging, it looks like some layout optimization in VirtualFlow might be the reason (all seems to be fine if the scrolled content is not a TableView - not thoroughly analyzed, though)
What happens is:
during collapse, the TitledPane's content is resized vertically to 0
in VirtualFlow's layoutChildren a zero height/width is special cased to do nothing except hide everything, including the scrollBars
an internal listener to the scrollBar's visiblilty resets its value to 0
A tentative (read: dirty and might have unwanted side-effects, totally untested beyond this quick outline!) hack around is a custom TableViewSkin that tries to "remember" the last not-zero value and resets it on getting visible again.
An example:
public class TitledPaneTableScroll extends Application {
public static class TableViewScrollSkin<T> extends TableViewSkin<T> {
DoubleProperty hvalue = new SimpleDoubleProperty();
public TableViewScrollSkin(TableView<T> control) {
super(control);
installHBarTweak();
}
private void installHBarTweak() {
// Note: flow and bar could be legally retrieved via lookup
// protected api pre-fx9 and post-fx9
VirtualFlow<?> flow = getVirtualFlow();
// access scrollBar via reflection
// this is my personal reflective access utility method - use your own :)
ScrollBar bar = (ScrollBar) FXUtils
.invokeGetFieldValue(VirtualFlow.class, flow, "hbar");
bar.valueProperty().addListener((s, o, n) -> {
if (n.intValue() != 0) {
hvalue.set(n.doubleValue());
// debugging
// new RuntimeException("who is calling? \n").printStackTrace();
}
//LOG.info("hbar value: " + n + "visible? " + bar.isVisible());
});
bar.visibleProperty().addListener((s, o, n) -> {
if (n) {
bar.setValue(hvalue.get());
}
});
}
}
int counter;
private Parent createContent() {
TableView<Object> table = new TableView<>(FXCollections.observableArrayList(new Object()) ) {
#Override
protected Skin<?> createDefaultSkin() {
return new TableViewScrollSkin<>(this);
}
};
table.getColumns().addAll(Stream
.generate(TableColumn::new)
.limit(10)
.map(col -> {
col.setPrefWidth(50);
col.setText("" + counter++);
return col;
})
.collect(Collectors.toList()));
TitledPane titled = new TitledPane("title", table);
titled.setAnimated(true);
BorderPane content = new BorderPane(titled);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent(), 400, 400));
// stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TitledPaneTableScroll.class.getName());
}
I know this question has been asked a multiple times, but I was unable to get help from any of the article.
My Main.FXML is
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<TreeView fx:id="treeView" layoutX="51.0" layoutY="24.0" onContextMenuRequested="#mouseClick" onMouseClicked="#mouseClick" prefHeight="352.0" prefWidth="493.0" />
</children>
</AnchorPane>
My Controller.java is
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseEvent;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable
{
#FXML TreeView<String> treeView;
#Override
public void initialize(URL location, ResourceBundle resources)
{
TreeItem<String> root = new TreeItem<>("root");
TreeItem<String> nodeA = new TreeItem<>("nodeA");
TreeItem<String> nodeB = new TreeItem<>("nodeB");
TreeItem<String> nodeC = new TreeItem<>("nodeC");
root.getChildren().add(nodeA);
root.getChildren().add(nodeB);
root.getChildren().add(nodeC);
treeView.setRoot(root);
root.setExpanded(true);
}
#FXML
private void mouseClick(MouseEvent mouseEvent)
{
TreeItem<String> item = treeView.getSelectionModel().getSelectedItem();
System.out.println(item.getValue());
}
}
My Main.java is
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have seen article that teaches how to add the Drag and Drop feature to TreeItem via TreeCell by adding Cell Property. But the processes were quiet complicated and I being a layman to JavaFX was unable to understand those.
So, it will be quite helpful if anyone can help me out with this.
Thanks in Advance.
Add in your Controller code responsible for setting a custom cell factory, that will attach handlers to Drag/MouseEvents.
treeView.setCellFactory(param -> {
// creating cell from deafult factory
TreeCell<String> treeCell = TextFieldTreeCell.forTreeView().call(param);
// setting handlers
treeCell.setOnDragDetected(this::onDragDetected);
treeCell.setOnDragOver(this::onDragOver);
treeCell.setOnDragDropped(this::onDragDropped);
return treeCell;
});
Basic handlers taken from DragEvent javadoc page:
private void onDragDetected(MouseEvent event) {
TreeCell<String> source = (TreeCell<String>) event.getSource();
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString(source.getItem());
db.setContent(content);
event.consume();
}
private void onDragOver(DragEvent dragEvent) {
Dragboard db = dragEvent.getDragboard();
if (db.hasString()) {
dragEvent.acceptTransferModes(TransferMode.COPY);
}
dragEvent.consume();
}
private void onDragDropped(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
System.out.println("Dropped: " + db.getString());
success = true;
}
event.setDropCompleted(success);
event.consume();
}
With the help of #kozmatteo I was able to acquire the drag and drop feature of TreeView in JavaFX. The Controller code is as below :
Controller.java
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.input.*;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable
{
#FXML TreeView<String> treeView;
private TreeCell<String> source, treeCell;
#Override
public void initialize(URL location, ResourceBundle resources)
{
TreeItem<String> root = new TreeItem<>("root");
TreeItem<String> nodeA = new TreeItem<>("nodeA");
TreeItem<String> nodeB = new TreeItem<>("nodeB");
TreeItem<String> nodeC = new TreeItem<>("nodeC");
root.getChildren().add(nodeA);
root.getChildren().add(nodeB);
root.getChildren().add(nodeC);
treeView.setRoot(root);
root.setExpanded(true);
treeView.setCellFactory(param -> {
// creating cell from deafult factory
treeCell = TextFieldTreeCell.forTreeView().call(param);
// setting handlers
treeCell.setOnDragDetected(this::onDragDetected);
treeCell.setOnDragOver(this::onDragOver);
treeCell.setOnDragDropped(this::onDragDropped);
return treeCell;
});
}
private void onDragDetected(MouseEvent event)
{
source = (TreeCell<String>) event.getSource();
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString(source.getItem());
db.setContent(content);
System.out.println("Dragging: " + db.getString());
event.consume();
}
private void onDragOver(DragEvent dragEvent)
{
Dragboard db = dragEvent.getDragboard();
if (db.hasString())
{
dragEvent.acceptTransferModes(TransferMode.COPY);
}
dragEvent.consume();
}
private void onDragDropped(DragEvent event)
{
Dragboard db = event.getDragboard();
String targetNode = ((TreeCell<String>)event.getGestureTarget()).getItem();
boolean success = false;
if (db.hasString()
&& !targetNode.equalsIgnoreCase(source.getItem()))
{
System.out.println("Dropped on: " + targetNode);
success = true;
}
event.setDropCompleted(success);
event.consume();
}
}
Demonstration for the code above:
But One of the problem that occurs if the onDragOver code is changed a bit. I want to print the name of the node upon which the dragging node is dragged over.
onDragOver(); method changed:
private void onDragOver(DragEvent dragEvent)
{
Dragboard db = dragEvent.getDragboard();
if (db.hasString())
{
dragEvent.acceptTransferModes(TransferMode.COPY);
String targetNode = ((TreeCell<String>)event.getGestureTarget()).getItem(); // On adding this piece of code the DragOver event is not working.
}
dragEvent.consume();
}
Demonstration after adding the code above:
Finally:
I want to know the name of the node on which the source is being dragged over. If anyone can help me with this then it will be very much helpful.
Scenario: Im passing in comma separated values to a table. 2 columns, one with the original value, one with a text field with the values populated inside. They are lined up so I can add/change values, and copy the changes to a string.
I cannot figure out how to capture the TextField data after they are put in the table. Code below:
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("/application/MainFxml.fxml")); //this is the file that
Scene scene = new Scene(root,800,800); ////100,100 is width and height of window
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Controller:
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
public class MainFxmlController implements Initializable {
public static int count=-1;
#FXML public TableView<tableClass> table = new TableView<tableClass>();
#FXML private TableColumn<tableClass, String>col1;
#SuppressWarnings({ "unchecked", "rawtypes" })
#FXML public TableColumn<tableClass, Row> col2 = new TableColumn("Row");
#FXML public TextField txt;
#FXML public Button btn, btn2;
#FXML public ListView<String> listView = new ListView<String>();
public static ArrayList<String> input = new ArrayList<String>();
final HBox hb = new HBox();
public ObservableList<tableClass> obList = FXCollections.observableArrayList(); // each column contains an observable list object
public ObservableList<tableClass> loadTable(){
return obList; //return data object
}////END loadData
#Override
public void initialize(URL url, ResourceBundle rb) {
table.setEditable(true);
col1.setCellValueFactory(cellData -> cellData.getValue().getCol1());
col2.setCellFactory((param) -> new TextFieldCell<tableClass, Row>(EnumSet.allOf(Row.class)));
col2.setCellValueFactory(new PropertyValueFactory<tableClass, Row>("Row"));
col2.setOnEditCommit(
new EventHandler<CellEditEvent<tableClass, Row>>() {
#Override
public void handle(CellEditEvent<tableClass, Row> t) {
((tableClass) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setCol2(t.getNewValue());
}
}
);
tableClass Entry = new tableClass(" ", Row.Row1); //create the table using getters/setters from Table Class
table.getItems().addAll(Entry);
table.setItems(loadTable());
col1.setCellFactory(TextFieldTableCell.<tableClass>forTableColumn()); //Makes the columns themselves editable
col1.setOnEditCommit(
new EventHandler<CellEditEvent<tableClass, String>>() {
#Override
public void handle(CellEditEvent<tableClass, String> t) {
((tableClass) t.getTableView().getItems().get( t.getTablePosition().getRow())).setCol1(t.getNewValue());
}
}
);
col1.setStyle( "-fx-alignment: BOTTOM-RIGHT;"); //to alight text next to textArea
txt.setText("fsad,0,0,gfds,43,4,4,fdsg,rtewrtwe,0,67,3,4,4,,4,44,,4"); //TO BE ROMOVED UPON COMPLETION
}//end initialize
public void buttonAction(ActionEvent e){
if(txt.getText() != ""){
System.out.println(txt.getText());
ArrayList<String> myList = new ArrayList<String>(Arrays.asList(txt.getText().split(",")));
input = myList;
}
for(int i =0; i< input.size(); i++){
Row.Row1.equals(input.get(i).toString());
obList.add(new tableClass(input.get(i).toString(), Row.Row1));
}//end for
}//end buttonAction
public void captureText(ActionEvent e){
/*
* HERE I NEED TO CAPTURE VALUES FROM THE TEXT FIELDS
* IN COL2
*/
}
public static enum Row { //enum for the dxTable radio button if you want any other options, add another value and another radio buttton will be populated
Row1;
}
public static class TextFieldCell<S,T extends Enum<T>> extends TableCell<S,T>{
private EnumSet<T> enumeration;
public TextFieldCell(EnumSet<T> enumeration) {
this.enumeration = enumeration;
}
#Override
protected void updateItem(T item, boolean empty)
{
super.updateItem(item, empty);
if (!empty)
{
// gui setup
HBox hb = new HBox(7);
hb.setAlignment(Pos.CENTER);
// create a radio button for each 'element' of the enumeration
for (Enum<T> enumElement : enumeration) {
try{
TextField textField = new TextField(input.get(count));
textField.setUserData(enumElement);
hb.getChildren().add(textField);
}catch(IndexOutOfBoundsException e){}
}
// issue events on change of the selected radio button
setGraphic(hb);
count++;
} //end if
else
setGraphic(null);
}//updateItem
}//END TextFieldCell class
public static class tableClass{ ///table object with getters and setters.
public final SimpleStringProperty col1;
public final SimpleObjectProperty<Row> col2 = new SimpleObjectProperty<Row>();
public tableClass(String col1, Row t) { //uses an enum for the second type
this.col1 = new SimpleStringProperty(col1);
this.col2.setValue(t);
}
public StringProperty getCol1() {
return col1;
}
public void setCol1(String i) {
col1.set(i);
}
public void setCol2(Row t) {
col2.set(t);
}
public Row getCol2() {
return col2.get();
}
public String getCol2(int index) {
return "";
}
}//end table class
}//end controller
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="800.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainFxmlController">
<children>
<Button fx:id="btn" layoutX="723.0" layoutY="26.0" mnemonicParsing="false" onAction="#buttonAction" text="btn" />
<TextField fx:id="txt" layoutX="41.0" layoutY="26.0" prefHeight="25.0" prefWidth="647.0" />
<TableView fx:id="table" editable="true" layoutX="41.0" layoutY="106.0" prefHeight="588.0" prefWidth="451.0">
<columns>
<TableColumn fx:id="col1" prefWidth="75.0" text="C1" />
<TableColumn fx:id="col2" prefWidth="114.0" text="C2" />
</columns>
</TableView>
<Button fx:id="btn2" layoutX="507.0" layoutY="669.0" mnemonicParsing="false" onAction="#captureText" text="Button" />
</children>
</AnchorPane>
Any help would be greatly appreciated.
I'm not sure exactly what are trying to do, but I have found a way to get the TextField when they press enter.
In your TextFieldCell class, I added a setOnAction to grab what the user input in the TextField.
TextField textField = new TextField(input.get(count));
textField.setUserData(enumElement);
textField.setOnAction(event -> {
System.out.println("Gotcha");
});
You can use that, instead of the button, to do whatever you want to the text entered.
I have no idea how to use your button to programmatically grab all of the TextField in col2 and grab their text.
But, with the setOnAction you can add whatever you like in there and hopefully do what you need to do.
Edit 1
With some heavy editing of your work, I have done it!
I will leave in the old answer, since it applies directly to your source code.
First, I made col2 Look like this.
#FXML public TableColumn<tableClass, TextField> col2;
Then, I used that to my advantage and setCellValueFactory like so:
col2.setCellValueFactory(new PropertyValueFactory<>("col2"));
Everything to do with col2 I had to update/remove to get it to work with the TextField, and I ended up with a much shorter source code, that can do what you want. Example is in the captureText method.
I edited the tableClass to make use of the TextField, I removed your cellFactory class altogether. Hope this helps.
#FXML public TableView<tableClass> table;
#FXML private TableColumn<tableClass, String>col1;
#SuppressWarnings({ "unchecked", "rawtypes" })
#FXML public TableColumn<tableClass, TextField> col2;
#FXML public TextField txt;
#FXML public Button btn, btn2;
public static ArrayList<String> input = new ArrayList<String>();
public static Group hb = new Group();
public ObservableList<tableClass> obList = FXCollections.observableArrayList(); // each column contains an observable list object
public ObservableList<tableClass> loadTable(){
return obList; //return data object
}////END loadData
#Override
public void initialize(URL url, ResourceBundle rb) {
table.setEditable(true);
col1.setCellValueFactory(cellData -> cellData.getValue().col1Property());
col2.setCellValueFactory(new PropertyValueFactory<>("col2"));
table.setItems(loadTable());
col1.setCellFactory(TextFieldTableCell.<tableClass>forTableColumn()); //Makes the columns themselves editable
col1.setOnEditCommit(
new EventHandler<CellEditEvent<tableClass, String>>() {
#Override
public void handle(CellEditEvent<tableClass, String> t) {
((tableClass) t.getTableView().getItems().get( t.getTablePosition().getRow())).setCol1(t.getNewValue());
}
}
);
col1.setStyle( "-fx-alignment: BOTTOM-RIGHT;"); //to alight text next to textArea
txt.setText("fsad,0,0,gfds,43,4,4,fdsg,rtewrtwe,0,67,3,4,4,,4,44,,4"); //TO BE ROMOVED UPON COMPLETION
}//end initialize
public void buttonAction(ActionEvent e){
if(txt.getText() != ""){
System.out.println(txt.getText());
ArrayList<String> myList = new ArrayList<String>(Arrays.asList(txt.getText().split(",")));
input = myList;
}
for(int i =0; i< input.size(); i++){
obList.add(new tableClass(input.get(i),input.get(i)));
}//end for
}//end buttonAction
public void captureText(ActionEvent e) {
obList.forEach(event -> {
event.setCol1(event.getCol2().getText());
});
/*
* HERE I NEED TO CAPTURE VALUES FROM THE TEXT FIELDS
* IN COL2
*/
}
public static class tableClass{ ///table object with getters and setters.
public final SimpleStringProperty col1;
public final TextField col2;
public tableClass(String col1, String col2) { //uses an enum for the second type
this.col1 = new SimpleStringProperty(col1);
this.col2 = new TextField(col2);
}
public StringProperty col1Property() {
return col1;
}
public String getCol1(){
return col1.get();
}
public void setCol1(String i) {
col1.set(i);
}
public void setCol2(String tx) {
col2.setText(tx);
}
public TextField getCol2() {
return col2;
}
}//end table class
I have spent the last couple of days looking into this and trying my hand at some different solutions found but Im still having a hard time implementing them correctly.
I have a project that's bound to turn into something mid-sized so I want to make sure I am using MVC correctly from the start instead of just hacking it apart "making it work".
As of now the application will only open 3 scenes, 2 on start(no fxml needed) and another for settings(using a FXML). I do need to be careful of strong references to these as this application will be running on low resource devices.
Ultimately my questions are:
Am I using scenes and controllers correctly? and given the way I have initiated the settings scene Im not finding a way to edit it with a controller. What am I doing wrong?
Below is what I have so far and I must say I dont like the way I initialize the first 2 scenes from Main and of course trying to start the settings scene is rather haphazard. My hurdle now is trying to interact with the scenes through their controllers.
Main.java
package sample;
import javafx.application.Application;
import javafx.stage.Stage;
import sample.controllers.StageController;
<pre>
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
StageController newStage = new StageController();
newStage.start(primaryStage);
}
public static void main(String[] args) {
launch(args);
}
}
StageController.java
package sample.controllers;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.List;
import static sample.controllers.HotkeyController.createHotkeys;
import static sample.controllers.WebViewController.createWebView;
/**
* Created by devin on 4/14/16.
*/
public class StageController extends Application
{
#Override
public void start(Stage primaryStage)throws Exception
{
Screen primaryScreen = Screen.getPrimary();
Screen secondaryScreen;
List<Screen> allScreens = Screen.getScreens();
if (allScreens.size() <= 1)
{
System.out.println("Only one screen");
secondaryScreen = primaryScreen;
}
else
{
if (allScreens.get(0).equals(primaryScreen))
{
secondaryScreen = allScreens.get(1);
}
else
{
secondaryScreen = allScreens.get(0);
}
}
configureStage("Primary", primaryStage, primaryScreen);
final Stage secondaryStage = new Stage();
configureStage("Secondary", secondaryStage, secondaryScreen);
}
private void configureStage(final String name, final Stage stage, final Screen screen)
{
Rectangle2D bounds = screen.getBounds();
System.out.println(name + ":" + bounds);
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.initStyle(StageStyle.UNDECORATED);
showStage(name, stage, screen);
stage.show();
stage.setFullScreen(true);
}
private void showStage(final String name, final Stage stage, final Screen screen)
{
//Scene scene = new Scene(new Group());
StackPane root = new StackPane();
Scene scene = new Scene(root);
stage.setScene(scene);
createWebView(name, root);
scene.setRoot(root);
/*
* Use hotkeys to allow application actions
*/
createHotkeys(name, scene);
}
}
WebViewController
package sample.controllers;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.w3c.dom.Document;
/**
* Created by devin on 4/13/16.
*/
public class WebViewController
{
public static void createWebView(final String name, final StackPane root)
{
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
if (name == "Primary") {
webEngine.load("http://google.com");
}
else
{
webEngine.load("http://google.com");
}
webEngine.documentProperty().addListener(new ChangeListener<Document>()
{
#Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc)
{
String heightText = browser.getEngine().executeScript(
"window.getComputedStyle(document.body, null).getPropertyValue('height')"
).toString();
double height = Double.valueOf(heightText.replace("px", ""));
System.out.println("Height of browser on " + name + " " + height);
}
});
root.getChildren().addAll(browser);
/* This is a firebug call if we need to debug the webpage that is being loaded */
// webEngine.documentProperty().addListener(new ChangeListener<Document>() {
// #Override public void changed(ObservableValue<? extends Document> prop, Document oldDoc, Document newDoc) {
// enableFirebug(webEngine);
// }
// });
}
private static void enableFirebug(final WebEngine engine) {
engine.executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}");
}
}
HotkeyController.java
package sample.controllers;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
/**
* Created by devin on 4/13/16.
*/
public class HotkeyController
{
public static void createHotkeys(final String name, final Scene scene)
{
final KeyCombination exitCMD = new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (exitCMD.match(event))
{
System.out.println("CTRL + E was pressed on " + name + " display\n" +
"Exiting the application");
Platform.exit();
}
}
});
final KeyCombination settingsCMD = new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
if (settingsCMD.match(event))
{
System.out.println("CTRL + S was pressed on " + name + " display\n" +
"Opening Settings Scene");
/*
* This is where we need to launch a scene for settings
*/
try
{
Parent root = FXMLLoader.load(getClass().getResource("/sample/view/settingsscreen.fxml"));
Stage settingsStage = new Stage();
settingsStage.setTitle("FTX Signage Settings");
settingsStage.setScene(new Scene(root, 500 , 400));
settingsStage.show();
// SettingsController settingsController = new SettingsController();
// GettersSetters getSet = new GettersSetters();
// settingsController.getText();
// String hostname = getSet.getHostname();
// settingsController.setText(hostname);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
});
}
}
I have a problem with my basic encrypter application. I want to generate an error window if someone type a string in the keyTextField. And also an event to close the error window using OK Button(Window graphic is loading from fxml file)
I've tried making it as shown below, but without success, i was also using close() method. What are best methods to deal with application control? I am using only MainController and i think it is not good idea. Thank you in advance
`package pl.gumisok.cipherController;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import pl.gumisok.cipherMain.CipherManager;
public class MainController implements Initializable {
CipherManager cipher;
#FXML
private ContentPaneController contentPaneController;
#FXML
private ControlPaneController controlPaneController;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// TODO Auto-generated method stub
System.out.println(contentPaneController);
System.out.println(controlPaneController);
Button encryptButton = controlPaneController.getEncryptButton();
Button decryptButton = controlPaneController.getDecryptButton();
Button okButton = controlPaneController.getOkButton();
TextArea cleanTextArea = contentPaneController.getCleanTextArea();
TextArea cryptTextArea = contentPaneController.getCryptTextArea();
TextField keyTextField = controlPaneController.getKeyTextField();
encryptButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
String wiadomosc = cleanTextArea.getText();
System.out.println(wiadomosc);
try {
int key = Integer.parseInt(keyTextField.getText());
System.out.println(key);
} catch (NumberFormatException e) {
System.out.println(e);
FXMLLoader fxmlLoader = new FXMLLoader(getClass()
.getClassLoader().getResource(
"pl/gumisok/cipherView/Error.fxml"));
Parent root;
try {
root = fxmlLoader.load();
Stage sstage = new Stage();
sstage.setOpacity(1);
sstage.setTitle("Error");
sstage.setScene(new Scene(root));
sstage.show();
okButton.setOnAction(x->sstage.hide());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
}
}`
your application layer is not good.
you need bind the button action in fxml file to a controller.
Maybe something like this:
Error.fxml:
<AnchorPane xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="controllers.ErrorController">
<children>
<Label text="ERROR!" />
<Button text="close" onAction="#hide" layoutY="15"/>
</children>
</AnchorPane>
ErrorController.java:
public class ErrorController {
private static Stage stage;
private static Parent root;
public ErrorController(){}
public ErrorController(Window owner) throws IOException {
if (root == null)
root = FXMLLoader.load(ClassLoader
.getSystemResource("views/Error.fxml"));
if (stage == null)
stage = new Stage();
//stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(owner);
stage.setTitle("Error");
stage.setScene(new Scene(root));
}
public void show() {
stage.show();
}
public #FXML void hide() {
stage.hide();
}
}
And then use it
...
error = new ErrorController(node.getScene().getWindow());
...
try {
int key = Integer.parseInt(keyTextField.getText());
System.out.println(key);
} catch (NumberFormatException e) {
error.show();
}
I hope, I understand the question correct,
here is an example how to create an alert dialog
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText(null);
alert.setContentText("I have a great message for you!");
alert.showAndWait();