I created a JavaFX application that is nearly completed. I exported it as a runnable JAR. When opening this JAR I only see a blank window. i followed some other answers from stackoverflow but I did not get it working.
It works only in the Eclipse IDE!
My screens controller:
package gui;
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
public class ScreensController extends StackPane {
private HashMap<String, Node> screens = new HashMap<>();
public static String sourcePath = "";
private CoreService coreService;
public ScreensController(){
super();
}
public void addScreen(String name, Node screen) {
screens.put(name, screen);
}
public boolean loadScreen(String name, String resource) {
System.out.println("ID: "+name);
System.out.println("Resource: "+resource);
String file = System.getProperty("user.dir")+"\\bin\\"+resource;
// System.out.println(file);
try {
FXMLLoader myLoader = new FXMLLoader();
File f = new File(file);
URL url = f.toURI().toURL();
myLoader.setLocation(url);
// System.out.println("Location: "+myLoader.getLocation());
Parent loadScreen = (Parent) myLoader.load();
ControlledScreen myScreenControler =
((ControlledScreen) myLoader.getController());
myScreenControler.setScreenParent(this);
addScreen(name, loadScreen);
System.out.println("Anzahl Screens: "+screens.size());
return true;
}catch(Exception e) {
System.out.println("Fehler beim Laden von "+file);
System.out.println(e.getMessage());
return false;
}
}
public boolean setScreen(final String name) {
#SuppressWarnings("unused")
Node screenToRemove;
if(screens.get(name) != null){ //screen loaded
if(!getChildren().isEmpty()){ //if there is more than one screen
getChildren().add(0, screens.get(name)); //add the screen
screenToRemove = getChildren().get(1);
getChildren().remove(1); //remove the displayed screen
}else{
getChildren().add(screens.get(name)); //no one else been displayed, then just show
}
return true;
}else {
System.out.println("Screen hasn't been loaded!!! \n");
return false;
}
}
public boolean unloadScreen(String name) {
if(screens.remove(name) == null) {
System.out.println("Screen didn't exist!!!");
return false;
} else {
return true;
}
}
public void print() {
Set<String> keys = screens.keySet();
Iterator<String> it = keys.iterator();
while (it.hasNext()){
System.out.println("Key: "+it.next());
}
}
public CoreService getCoreService(){
return this.coreService;
}
public void setCoreService(CoreService coreService){
this.coreService = coreService;
}
}
And here I use it:
package gui;
import java.util.Optional;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class MainMenu extends Application {
private Stage mainStage;
private static CoreService coreService;
public static final String MAIN_SCREEN = "main";
public static final String MAIN_SCREEN_FXML = "gui\\MainMenu.fxml";
#Override
public void start(Stage primaryStage) {
this.mainStage = primaryStage;
ScreensController mainContainer = new ScreensController();
mainContainer.loadScreen(MainMenu.MAIN_SCREEN, MainMenu.MAIN_SCREEN_FXML);
mainContainer.setCoreService(MainMenu.coreService);
mainContainer.setScreen(MainMenu.MAIN_SCREEN);
Group root = new Group();
root.getChildren().addAll(mainContainer);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest(confirmCloseEventHandler);
primaryStage.show();
}
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
//Source: http://stackoverflow.com/questions/29710492/javafx-internal-close-request
Alert closeConfirmation = new Alert(
Alert.AlertType.CONFIRMATION,
"Are you sure you want to exit?"
);
Button exitButton = (Button) closeConfirmation.getDialogPane().lookupButton(
ButtonType.OK
);
exitButton.setText("Exit");
closeConfirmation.setHeaderText("Confirm Exit");
closeConfirmation.initModality(Modality.APPLICATION_MODAL);
closeConfirmation.initOwner(mainStage);
closeConfirmation.setX(mainStage.getX() + 150);
closeConfirmation.setY(mainStage.getY() - 300 + mainStage.getHeight());
Optional<ButtonType> closeResponse = closeConfirmation.showAndWait();
if (!ButtonType.OK.equals(closeResponse.get())) {
event.consume();
}
};
public static void main(String[] args, CoreService aService) {
// Weitergeben des CoreServices
coreService = aService;
launch(args);
}
}
I do not see where the error is.
When I start the program from command line it says that the MainMenu.fxml file could not been found.
In my application it is in the package gui. -> gui/MainMenu.fxml
Would be nice if someone find my error!
What the error message tells you, that the FXML file cannot be located.
You could try to:
Change this ...
public static final String MAIN_SCREEN_FXML = "gui\\MainMenu.fxml";
... to ...
public static final String MAIN_SCREEN_FXML = "/gui/MainMenu.fxml";
And to change this ...
FXMLLoader myLoader = new FXMLLoader();
File f = new File(file);
URL url = f.toURI().toURL();
myLoader.setLocation(url);
... to (and you don't need the variables file and f)...
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Some references:
I had an answer here on how to use getResource.
Also you can check the documentation of getResource()
And you can check this question about loading resources from a JAR
Related
First I want to say that I already checked various similar solutions to this problem here, but the code design of the other users that posted this question is so different than mine that I don't understand how to fix the same problem using the solutions posted.
That said, I'm using javafx with gluon scene builder to create my first app. I'll post the code below. This (https://i.imgur.com/lO2mHZI.png) is how the app looks so far. The New button opens this window (https://i.imgur.com/kVZ5tjt.png).
I have a main class called WeightApp:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class WeightApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("foodTab.fxml"));
Scene main = new Scene(root);
primaryStage.setScene(main);
primaryStage.setTitle("App");
primaryStage.setMinWidth(root.minWidth(-1));
primaryStage.setMinHeight(root.minHeight(-1));
primaryStage.show();
}
public static void main(String[] args) {
launch(WeightApp.class);
}
}
A FoodTabController class which loads what's shown in the first picture without the window created by pressing New:
package application;
import application.domain.Aliment;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import java.io.*;
import java.util.Objects;
public class FoodTabController {
#FXML
protected AnchorPane app, foodTab, foodButtonBar;
#FXML
protected TabPane mainWindow;
#FXML
protected Tab summaryTabLabel, foodTabLabel;
#FXML
protected Label alimentsLabel;
#FXML
protected Button deleteButton, refreshButton, newButton, newMealWindow;
#FXML
protected TableView<Aliment> alimentsTableView;
#FXML
protected TableColumn<Aliment, String> alimentsNameCol;
#FXML
protected TableColumn<Aliment, Double> alimentsKcalCol, alimentsFatCol, alimentsCarbsCol, alimentsProteinCol, alimentsFiberCol;
protected ObservableList<Aliment> aliments = FXCollections.observableArrayList();
public void initialize() {
alimentsNameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
alimentsKcalCol.setCellValueFactory(new PropertyValueFactory<>("calories"));
alimentsFatCol.setCellValueFactory(new PropertyValueFactory<>("fat"));
alimentsCarbsCol.setCellValueFactory(new PropertyValueFactory<>("carbohydrate"));
alimentsProteinCol.setCellValueFactory(new PropertyValueFactory<>("protein"));
alimentsFiberCol.setCellValueFactory(new PropertyValueFactory<>("fiber"));
loadAliments();
alimentsTableView.setItems(aliments);
}
// Aliments //
public void newAlimentWindow() throws IOException {
Parent newAlimentWindow = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("newAlimentWindow.fxml")));
Stage stage = new Stage();
stage.setScene(new Scene(newAlimentWindow));
stage.show();
}
public void updateTableView() {
aliments.clear();
loadAliments();
}
public ObservableList<Aliment> alimentObservableList() {
return aliments;
}
public void deleteAliment() {
aliments.remove(alimentsTableView.getSelectionModel().getSelectedItem());
saveAliments();
}
public void saveAliments() {
String COMMA_DELIMITER = ",";
String NEW_LINE_SEPARATOR = "\n";
String FILE_HEADER = "aliment,calories,fat,carbs,protein,fiber";
FileWriter fw = null;
try {
fw = new FileWriter("aliments.csv");
fw.append(FILE_HEADER);
fw.append(NEW_LINE_SEPARATOR);
for (Aliment aliment : aliments) {
fw.append(String.valueOf(aliment.getName()));
fw.append(COMMA_DELIMITER);
fw.append(String.valueOf(aliment.getCalories()));
fw.append(COMMA_DELIMITER);
fw.append(String.valueOf(aliment.getFat()));
fw.append(COMMA_DELIMITER);
fw.append(String.valueOf(aliment.getCarbohydrate()));
fw.append(COMMA_DELIMITER);
fw.append(String.valueOf(aliment.getProtein()));
fw.append(COMMA_DELIMITER);
fw.append(String.valueOf(aliment.getFiber()));
fw.append(NEW_LINE_SEPARATOR);
}
} catch (Exception e) {
System.out.println("Error writing to file");
e.printStackTrace();
} finally {
try {
assert fw != null;
fw.flush();
fw.close();
} catch (IOException e) {
System.out.println("Error while flushing/closing FileWriter.");
e.printStackTrace();
}
}
}
public void loadAliments() {
String COMMA_DELIMITER = ",";
int ALIMENT_NAME = 0;
int ALIMENT_CALORIES = 1;
int ALIMENT_FAT = 2;
int ALIMENT_CARBS = 3;
int ALIMENT_PROTEIN = 4;
int ALIMENT_FIBER = 5;
BufferedReader fileReader = null;
try {
fileReader = new BufferedReader(new FileReader("aliments.csv"));
fileReader.readLine();
String line = "";
while ((line = fileReader.readLine()) != null) {
String[] tokens = line.split(COMMA_DELIMITER);
aliments.add(new Aliment(String.valueOf(tokens[ALIMENT_NAME]), Double.parseDouble(tokens[ALIMENT_CALORIES]), Double.parseDouble(tokens[ALIMENT_FAT]), Double.parseDouble(tokens[ALIMENT_CARBS]), Double.parseDouble(tokens[ALIMENT_PROTEIN]), Double.parseDouble(tokens[ALIMENT_FIBER])));
}
} catch (Exception e) {
System.out.println("Error reading aliments from CSV file");
e.printStackTrace();
} finally {
try {
assert fileReader != null;
fileReader.close();
} catch (IOException e) {
System.out.println("Error while trying to close FileReader");
e.printStackTrace();
}
}
}
// Aliments //
}
Finally, I have the newAlimentWindowController class that is the window the New button opens:
package application;
import application.domain.Aliment;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
public class newAlimentWindowController extends FoodTabController {
#FXML
protected Pane newAlimentPane;
#FXML
protected TextField newAlimentSetName, newAlimentSetCal, newAlimentSetFat, newAlimentSetCarbs, newAlimentSetProtein, newAlimentSetFiber;
#FXML
protected Button addButton;
public void initialize() {
loadAliments();
}
public void addAliment() {
aliments.add(new Aliment(newAlimentSetName.getText(), Double.parseDouble(newAlimentSetCal.getText()), Double.parseDouble(newAlimentSetFat.getText()), Double.parseDouble(newAlimentSetCarbs.getText()), Double.parseDouble(newAlimentSetProtein.getText()), Double.parseDouble(newAlimentSetFiber.getText())));
saveAliments();
updateTableView();
}
}
Also, the Aliment object:
package application.domain;
import java.util.Objects;
public class Aliment {
private String name;
private double weight;
private double calories, fat, carbohydrate, protein, fiber;
public Aliment(String name, double weight, double calories, double fat, double carbohydrate, double protein, double fiber) {
this(name, calories, fat, carbohydrate, protein, fiber);
this.weight = weight;
}
public Aliment(String name, double calories, double fat, double carbohydrate, double protein, double fiber) {
this.name = name;
this.weight = 100;
this.calories = calories;
this.fat = fat;
this.carbohydrate = carbohydrate;
this.protein = protein;
this.fiber = fiber;
}
Everything works fine, except after I type in the textfields in the New window and I press the Add button, the updateTableView method inside the addAliment method doesn't trigger (the Aliment item is added correctly, the observable list just doesn't refresh on the Add button press). However, the updateTableView method does work if I trigger it from inside the FoodTabController class that I linked to the Refresh button.
I don't understand what's happening: I can interact with the aliments observable list in FoodTabController from newAlimentWindowController since aliments.add works and at the same time, the saveAliments method also works, but updateTableView method, that is in the same method as saveAliments and aliments.add, does not work. I'm very confused.
I feel like I'm missing something basic about java programming and as such I'd like to learn what's going on. Any help will be appreciated, thank you very much!
So I've got a method called 'popup' in a javaFX controller class which opens a small popup window on top of the actual application window. This method runs without problem if it's assigned to a button in fxml and the button is clicked, but this is not the way I want to use it.
I've got an other class called 'Timer' with a new task (new thread) which is counting down from a certain number, and at a point it will open a popup window with a message. My purpose is to call and run the 'popup' method from this 'Timer' class. When I call the 'popup' method from here, it starts executing, but the popup window doesn't appear at all. (The method call happens as I get the message "in popup" on console from 'popup' method. )
So why does it work when a button click calls 'popup' method from the fxml file and why not when I call it from an other class? Thanks.
Please see the controller class with 'popup' method and the Timer class below (using Gradle in project):
"SceneController" controller class:
package GradleFX;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
//import java.awt.event.ActionEvent;
public class SceneController implements Initializable {
public static String password = "";
protected static int timercount = 20;
#FXML
private Label PWLabel;
#FXML
private Label bottomLabel;
#FXML
private PasswordField PWField;
#FXML
private Label showPWLabel;
protected static Label myBottomLabel;
private static PasswordField myPWField;
private static Label myShowPWLabel;
private static int tries;
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
}
**/***********************************************************************
/*This method runs if button is pressed in main application,
but can't make it work by calling it from Timer Class */
public void popup() {
System.out.println("in popup");
Stage dialogStage = new Stage();
dialogStage.initModality(Modality.WINDOW_MODAL);
VBox vbox = new VBox(new Text("Hi"), new Button("Ok."));
vbox.setAlignment(Pos.CENTER);
vbox.setPadding(new Insets(15));
dialogStage.setScene(new Scene(vbox));
dialogStage.show();
}
//****************************************************************************
public void showPW() {
myShowPWLabel.setText(myPWField.getText());
}
public void hidePW() {
myShowPWLabel.setText("");
}
public void exit() {
System.exit(0);
}
public void write() {
PWLabel.setText("Mukodik");
}
public void writeInput(String in) {
password = in;
System.out.println("final password text text: " + password);
writeFinally();
}
public void writeFinally() {
System.out.println("This is 'password' : " + password);
//bottomLabel.setText(password);
}
public void bottomLabelWrite() {
bottomLabel.setText(myPWField.getText());
}
public static void setLabel() throws InterruptedException {
myBottomLabel.setText("");
myBottomLabel.setText("Database has been permanently erased.");
//Thread.sleep(3000);
//System.exit(0);
}
public static void noKeyEnteredNote() {
myBottomLabel.setTextFill(Color.BLACK);
myBottomLabel.setText("No key entered. Type Main Key.");
}
public static void rightKey() {
myBottomLabel.setText("Yes, this is the right key.");
}
public static void wrongKey() throws InterruptedException {
tries = MasterKey.numOfTryLeft;
if (tries > 0) {
myBottomLabel.setTextFill(Color.RED);
myBottomLabel.setText("!!!Wrong key!!! You've got " + tries + " tries left!");
}
}
public void simpleTest(String in) {
System.out.println("in simpleTest and in is: " + in);
}
public void getMainKey() throws IOException, InterruptedException {
MasterKey masterKey = new MasterKey();
System.out.println("Inside SceneController");
masterKey.requestKey(myPWField.getText());
}
public void changeScreen(ActionEvent event) throws IOException, InterruptedException {
getMainKey();
if (MasterKey.isRightKey) {
Parent tableViewParent = FXMLLoader.load(getClass().getResource("Menu.fxml"));
Scene tableViewScene = new Scene(tableViewParent);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(tableViewScene);
window.show();
}
}
}
This is Timer class:
package GradleFX;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
public class Timer extends Task {
private ActionEvent actionEvent;
#Override
protected Integer call() throws Exception {
boolean notCalled = true;
while (SceneController.timercount > 0) {
SceneController sceneController = new SceneController();
System.out.println(SceneController.timercount);
Thread.sleep(1000);
SceneController.timercount--;
if (SceneController.timercount < 19) {
System.out.println("Less than 5");
if(notCalled) {
sceneController.popup();
notCalled = false;
}
}
}
System.exit(0);
return null;
}
}
Add this to your code:
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
timerTask.setOnFinished(e->{
popup();
});
}
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 ListView with a TextField above it. If a user enters in a search query into the textfield, the listview will update and filter itself to show relevant results.
The ListView shows items from a FilteredList, which is filled with Employee objects. Each Employee has a first and last name.
package application.ctrl;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import application.Main;
import application.objects.Employee;
import application.objects.EmployeeDatabase;
public class EmployeePickerWidget extends VBox implements Initializable {
#FXML
private TextField textField;
#FXML
private Button addNewEmployee;
#FXML
private ListView<Employee> employeeList;
private FilteredList<Employee> filteredList;
private ContextMenu cm;
private CustomMenuItem item;
private ClickedEmployeeInterface parent;
public EmployeePickerWidget(ClickedEmployeeInterface parent) {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource(
Main.EMPLOYEE_PICKER));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
this.parent = parent;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
setupEmployeeListView();
setupTextField();
}
private void setupEmployeeListView() {
filteredList = new FilteredList<Employee>(EmployeeDatabase.getInstance()
.getObservableList());
employeeList = new ListView<Employee>();
employeeList.setItems(filteredList);
employeeList.setOnMouseClicked(arg0 -> {
if (employeeList.getSelectionModel().getSelectedItem() != null) {
cm.hide();
parent.handleClickedEmployee();
}
});
}
private void setupTextField() {
textField.textProperty().addListener(
(observable, oldValue, newValue) -> {
filteredList.setPredicate(employee -> {
return filterHelper(employee, newValue);
});
});
textField.setText(" ");
textField.setText("");
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
cm = new ContextMenu();
item = new CustomMenuItem();
VBox container = new VBox();
container.setAlignment(Pos.CENTER_RIGHT);
container.getChildren().add(employeeList);
Button defineEmployeeBtn = new Button("Define New Employee");
defineEmployeeBtn.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
Main.DEFINE_NEW_EMPLOYEE));
Parent root = null;
try {
root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
Scene newScene = new Scene(root);
Stage newStage = new Stage();
newStage.setScene(newScene);
newStage.show();
});
container.getChildren().add(defineEmployeeBtn);
item.setContent(container);
cm.getItems().add(item);
}
private boolean filterHelper(Employee employee, String query) {
String first = employee.getFirst().toLowerCase(), last = employee
.getLast().toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
if (length == 1) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
return true;
else
return false;
} else if (length == 2) {
if (first.contains(querySplit[0]) || last.contains(querySplit[0]))
if (first.contains(querySplit[1]) || last.contains(querySplit[1]))
return true;
return false;
} else if (length == 3) {
return false;
}
return false;
}
public Employee getEmployee() {
return employeeList.getSelectionModel().getSelectedItem();
}
#FXML
public void addNewEmployee() {
}
}
interface ClickedEmployeeInterface {
void handleClickedEmployee();
}
If there were 3 employees named "Donald Trump", "Donald Smith", and "Donald Jackson" in the database, then the following needs to happen:
Typing up to the word "Donald" will show all 3 results.
Typing a space after Donald (resulting in "Donald ") will still show 3 results.
Typing a T after the previous query (resulting in "Donald T") should only show 1 result.
The problem is, after I enter in a space, the ListView breaks, and all of my Employees disappear from the ListView. When I click outside of the textfield and click back in again, it triggers this:
textField.setOnMouseClicked(event -> cm
.show(textField, Side.BOTTOM, 0, 0));
And my ListView suddenly works again, showing that one Employee.
How do I make the ListView filter properly without having to click out and back in?
I do not have the FXML file, so I wasn't able to replicate your problem. There are multiple problems with your code and this is the not the optimum solution, still, I have edited your answer to give you hints and help you understand the areas where you might have committed logical errors
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class DemoList extends Application {
#Override
public void start(Stage stage) throws Exception {
GridPane gridPane = new GridPane();
Label label = new Label("Name");
final TextField textField = new TextField();
textField.setFocusTraversable(false);
textField.setPromptText("Please Type Here");
final ContextMenu cm = new ContextMenu();
final ObservableList<String> employeeList = FXCollections
.observableArrayList();
employeeList.addAll("Donald Duck", "Donald Mouse", "Donald Goofy");
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> arg0,
String arg1, String arg2) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
for (String employee : employeeList) {
if (filterHelper(employee, arg2)) {
cm.getItems().add(new MenuItem(employee));
}
}
}
});
textField.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
// To clear the Context Menu so that same items are not added
// multiple times
cm.getItems().clear();
//Adding the data for initial click
for (String employee : employeeList) {
if (filterHelper(employee, textField.getText())) {
cm.getItems().add(new MenuItem(employee));
}
}
cm.show(textField, Side.BOTTOM, 0, 0);
}
});
gridPane.add(label, 0, 0);
gridPane.add(textField, 0, 1);
Scene scene = new Scene(gridPane, 300, 300);
stage.setScene(scene);
stage.show();
}
private boolean filterHelper(String employee, String query) {
//Splitting Employee name to fetch first and last name
String first = employee.split(" ")[0].toLowerCase(), last = employee
.split(" ")[1].toLowerCase();
String[] querySplit = query.replace(",", "\\s").split("\\s+");
int length = querySplit.length;
for (int i = 0; i < length; i++)
querySplit[i] = querySplit[i].toLowerCase();
/**
* Avoid adding unnecessary return statement
* I have removed all the 'return false' statements
* The last return will take care of all the 'return false'
*/
//only single word
if (length == 1) {
if (first.startsWith(querySplit[0])
|| last.startsWith(querySplit[0]))
return true;
}
//two words, considering first word is first name
//and second word is last name
else if (length == 2) {
if (first.startsWith(querySplit[0])
&& last.startsWith(querySplit[1]))
return true;
}
return false;
}
public static void main(String[] args) {
launch(args);
}
}
I'm trying to reproduce a Pagination Sample from oracle samples, but when I imported the project something strange happened that I can not build and run the project:
The complete code is:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
public class PaginationSample extends Application {
private final Pagination pagination;
private Image[] images = new Image[7];
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
VBox outerBox = new VBox();
outerBox.setAlignment(Pos.CENTER);
//Images for our pages
for (int i = 0; i < 7; i++) {
images[i] = new Image(PaginationSample.class.getResource("animal" + (i + 1) + ".jpg").toExternalForm(), false);
}
pagination = PaginationBuilder.create().pageCount(7).pageFactory(new Callback<Integer, Node>() {
#Override public Node call(Integer pageIndex) {
return createAnimalPage(pageIndex);
}
}).build();
//Style can be numeric page indicators or bullet indicators
Button styleButton = ButtonBuilder.create().text("Toggle pagination style").onAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent me) {
if (!pagination.getStyleClass().contains(Pagination.STYLE_CLASS_BULLET)) {
pagination.getStyleClass().add(Pagination.STYLE_CLASS_BULLET);
} else {
pagination.getStyleClass().remove(Pagination.STYLE_CLASS_BULLET);
}
}
}).build();
outerBox.getChildren().addAll(pagination, styleButton);
root.getChildren().add(outerBox);
}
//Creates the page content
private VBox createAnimalPage(int pageIndex) {
VBox box = new VBox();
ImageView iv = new ImageView(images[pageIndex]);
box.setAlignment(Pos.CENTER);
Label desc = new Label("PAGE " + (pageIndex + 1));
box.getChildren().addAll(iv, desc);
return box;
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
But netbeans show me the error "cannot assign a value to final variable pagination" for the following line:
pagination = PaginationBuilder.create().pageCount(7).pageFactory(new Callback<Integer, Node>() {
Someone can explain me what is going wrong??
A final field can be initialized only once. So the best place to initialize it is when its declared
private final Pagination pagination = new Pagination(...);
or it can be done in the constructor, since the constructor is assured to be called once per instance
private final Pagination pagination;
public PaginationSample() {
pagination = new Pagination(...);
}
final field cannot be initialized in a method because a method can be called multiple times once an instance of that class gets created