JavaFX custom component get action from button in custom component - java

I want to get the event of two buttons in my custom component.
the component is a imageview with two buttons to move between images, but I need to get the position of the image that is currently displayed, Im storing the key of the image, but I need to know when a button have been pressed outside the custom component, so I can change a Label outside the custom component.
public class TransitionSlider extends AnchorPane {
#FXML
private AnchorPane transitionSliderPane;
#FXML
private ImageView transitionSliderImageView;
#FXML
private Button prevButton;
#FXML
private Button nextButton;
private Map<Integer,Image> imageMap;
private Image currentImage;
private DropShadow imageViewDropShadow;
private int currentKey = 1;
private Image[] images;
public TransitionSlider() {
FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setController(this);
loader.setLocation(this.getClass().getResource("TransitionSlider.fxml"));
loader.setClassLoader(this.getClass().getClassLoader());
try {
loader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
prevButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= 1){
currentKey = currentKey + 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
nextButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(currentKey <= imageMap.size()){
currentKey = currentKey - 1;
currentImage = imageMap.get(currentKey);
createTransition(transitionSliderImageView, currentImage);
}
}
});
}
// more code here...
}
I want a way to capture the event and get variables inside the component and change a label outside the custom component...
for example:
public class Gallery extends Application {
#FXML
TransitionSlider ts;
Label label;
#Override
public void start(Stage stage) throws Exception {
label = new Label();
TransitionSlider ts = new TransitionSlider();
ts.captureButtonEvent(){ // need a way to capture this
label.setText(ts.getCurrentKey());
}
// more code here....
}

If I understood your question correctly, you want a binding.. Follow these steps:
1) Put bindable field and its getter/setter into TransitionSlider:
private IntegerProperty currentKey = new SimpleIntegerProperty(1);
public int getCurrentKey() {
return currentKey.get();
}
public void setCurrentKey(int val) {
return currentKey.set(val);
}
public IntegerProperty currentKeyProperty() {
return currentKey;
}
2) Bind this property to label's text in Gallery:
label = new Label();
TransitionSlider ts = new TransitionSlider();
label.textProperty.bind(ts.currentKeyProperty().asString());
Alternatively, if you want to do stuff more than just changing label's text, you can add a change listener to currentKeyProperty:
ts.currentKeyProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
label.setText(newValue);
// do other stuff according to "oldValue" and "newValue".
}
});

Related

How to access values from automatically generated Labels?

Here I receive an ObservableList of Products. Then for each Product in the List I create a Label for the name, price and quantity. I also create two Buttons, to add and remove quantity, altering the value in the label. My problem is in the ´escolherProdutos()´ method, where I want to access the value of each Label respective to de quantity so I can know what quantities of each product are being requested, and I cant seem to access these values from outside the populateFlowPane() method. This is probably not the best solution to my problem, and I am a beginner, so if you can help me solve my problem or even have a better way of doing this I would be really grateful.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
#FXML private Button escolherBtn;
private static ArrayList<Label> quantidades = new ArrayList<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
}
});
quantidades.add(qtd);
});
}
public void escolherProdutos()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
quantidades.forEach(qtd -> {
Logic.escolherProdutos(prod.getIdProduto(),
Integer.parseInt(qtd.getText()));
});
});
}
}
I would probably add them to a list and access them accordingly im not sure what you need from them because you are being vague but take a look at the code sample below
public class MySceneController {
#FXML private ListView listView;
private ArrayList<Label> labelList = new ArrayList<>();
#FXML
public void initialize()
{
populateListView();
}
public void populateListView()
{
ObservableList<Products> products = Logic.getProducts();
products.forEach(prod -> {
Label label = new Label(prod.getName());
//Add them to a list here
labelList.add(label);
listView.getItems().addAll(results);
});
}
public void doSomething()
{
for (Label label : labelList) {//Maybe iterate through the list depending on what you need
//do something
}
//Here is where I need to access the label values
}
}
I solved the problem. Maybe it's not the best solution, but it works for me. What I did was create a HashMap where the product ID is the key and the quantity is the value.
Then for the add and remove buttons, I simply replace the value for the corresponding key in the HashMap. Doing this, I can then use it as input for the escolherProdutos() method from the Logic class, to select the products.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
private HashMap<BigDecimal, Integer> quantidades = new HashMap<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
});
}
public void escolherProdutos()
{
Logic.escolherProdutos(quantidades);
}

JavaFX: ColorPicker returns the same value every time

So I'm trying to fill my Rectangle with Color Picker, but it doesn't cooperate.
I do it like this:
#FXML
private ColorPicker colorPicker = new ColorPicker(Color.BLACK);
#FXML
public void changeColor()
{
myRect.setFill(colorPicker.getValue());
System.out.println("color = " + colorPicker.getValue());
}
I binded this function to the ColorPicker in SceneBuilder, but when I choose a color, my rectangle only changes its color to black (or whatever default color I put in constructor) independently of what color I choose. So every time I pick a color I get the output "color = 0xff0000ff". Is it because I'm not using EventHandler (I somehow couldn't get it to work in the initialize function)? In my understanding if I bind it this function to the OnAction field it should work exactly like a Listener.
public class Main extends Application {
Stage window;
#Override
public void start(Stage primaryStage) {
try {
window = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("/MainScreen.fxml"));
Scene scene = new Scene(root);
window.setScene(scene);
window.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
public class Controller{
#FXML
private ColorPicker colorPicker = new ColorPicker(Color.BLACK);
#FXML
private Rectangle myRect;
public void initialize(){}
#FXML
public void changeWaveColor()
{
myRect.setFill(colorPicker.getValue());
System.out.println("Function color = " + colorPicker.getValue());
}
}

Switching view from gluon CharmListView works on Desktop but not in Android

So I have been working on a project and I have a CharmListView that populates with the name of the task to be used. I can login through my login screen, get to the CharmListView and click on the task I want to have open. It opens on the Desktop when I am testing it, but in Android it fails, saying the location is not found, and that:
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.gluonhq.charm.glisten.mvc.View.setName(java.lang.String)' on a null object reference
Here are my charm classes that I have:
This one is the task model essentially.
public class CharmHomeNavTask {
private String taskName;
private String taskDesc;
private static final Image IMAGE_ADMIN = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/admin.png"));
private static final Image IMAGE_AUDIT_TOOL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/auditTool.png"));
private static final Image IMAGE_CONSOLIDATE_PACKAGE = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/consolidatePackage.png"));
private static final Image IMAGE_DISPOSAL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/disposal.png"));
private static final Image IMAGE_EQUIP_MANGAGE = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/equipmentManagement.png"));
private static final Image IMAGE_INTRA_TRANSFER = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/intratransfer.png"));
private static final Image IMAGE_PICKUP = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/pickup.png"));
private static final Image IMAGE_TRU_WASTE_PREP = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/truWastePrep.png"));
private static final Image IMAGE_VISUAL_INSPECTION = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/visualInspection.png"));
private static final Image IMAGE_WALL2WALL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/wall2wall.png"));
private static final Image IMAGE_WASTE_ID = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/wasteIdentification.png"));
private static Image[] listOfImages = {IMAGE_WASTE_ID, IMAGE_VISUAL_INSPECTION, IMAGE_TRU_WASTE_PREP, IMAGE_CONSOLIDATE_PACKAGE, IMAGE_INTRA_TRANSFER, IMAGE_PICKUP, IMAGE_DISPOSAL, IMAGE_ADMIN, IMAGE_WALL2WALL,
IMAGE_EQUIP_MANGAGE, IMAGE_AUDIT_TOOL };
public CharmHomeNavTask(String taskName, String taskDesc){
this.taskName = taskName;
this.taskDesc = taskDesc;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getTaskDesc() {
return taskDesc;
}
public void setTaskDesc(String taskDesc) {
this.taskDesc = taskDesc;
}
public static void setListOfImages(Image[] listOfImages) {
listOfImages = listOfImages;
}
public static Image[] getListOfImages(){
return listOfImages;
}
public static Image getSingleImageFromList(int index){
return listOfImages[index];
}
}
This one is the creation of the tasks into an ObservableList
public class CharmHomeNavTasks {
public static ObservableList<CharmHomeNavTask> tasksList = FXCollections.observableArrayList(
new CharmHomeNavTask("Waste Identification", "Identify you waste from this screen."),
new CharmHomeNavTask("TRU Visual Inspection", "Visual Inspection of TRU waste."),
new CharmHomeNavTask("TRU Drum Preparation", "Prepare your TRU drums here."),
new CharmHomeNavTask("Consolidation/Packaging", "Consolidate and package your waste."),
new CharmHomeNavTask("Intra-Facility Transfer", "Transferring of waste within the same facility."),
new CharmHomeNavTask("Inter-Facility Pickup", "Picking up waste within the same facility."),
new CharmHomeNavTask("Disposal Tasks", "Disposal of waste tasks."),
new CharmHomeNavTask("Administrative Tasks", "Administrators have special tasks they can perform here."),
new CharmHomeNavTask("Wall-Wall Inventory", "Wall to Wall inventory tasks."),
new CharmHomeNavTask("Equipment Management", "Waste related equipment tasks."),
new CharmHomeNavTask("Audit Support Tool", "Tool for audit related tasks."));
public static ObservableList<CharmHomeNavTask> getTasksList() {
return tasksList;
}
}
This is the one that is giving me trouble because I have my onMouseClicked event here and I set the item(or task) to selected. This seems to work fine even on Android as I can the the system to tell me which item was clicked.
public class CharmHomeNavTaskCell extends CharmListCell<CharmHomeNavTask> {
private final ListTile tile;
private final ImageView imageView;
private CharmHomeNavTask item;
public CharmHomeNavTask carryOverItem;
public CharmHomeNavTaskCell(CharmListView listView){
tile = new ListTile();
imageView = new ImageView();
tile.setPrimaryGraphic(imageView);
carryOverItem = item;
tile.setOnMouseClicked(e -> {
System.out.println("******************* Item clicked " + item.getTaskName());
listView.setSelectedItem(item);
});
setText(null);
}
#Override
public void updateItem(CharmHomeNavTask item, boolean empty){
super.updateItem(item, empty);
this.item = item;
imageView.setFitWidth(32);
imageView.setFitHeight(32);
if (item != null && !empty) {
tile.textProperty().setAll(item.getTaskName() + " ", item.getTaskDesc());
tile.setWrapText(true);
final Image[] image = CharmHomeNavTask.getListOfImages();
super.setStyle("-fx-font-weight: bold");
switch (item.getTaskName()) {
case "Waste Identification":
imageView.setImage(image[0]);
break;
case "TRU Visual Inspection":
imageView.setImage(image[1]);
break;
case "TRU Drum Preparation":
imageView.setImage(image[2]);
break;
case "Consolidation/Packaging":
imageView.setImage(image[3]);
break;
case "Intra-Facility Transfer":
imageView.setImage(image[4]);
break;
case "Inter-Facility Pickup":
imageView.setImage(image[5]);
break;
case "Disposal Tasks":
imageView.setImage(image[6]);
break;
case "Administrative Tasks":
imageView.setImage(image[7]);
break;
case "Wall-Wall Inventory":
imageView.setImage(image[8]);
break;
case "Equipment Management":
imageView.setImage(image[9]);
break;
case "Audit Support Tool":
imageView.setImage(image[10]);
break;
}
setGraphic(tile);
} else {
setGraphic(null);
}
}
}
Here is the AppViewManager Class. I am using Glisten and Afterburner just so that you know.
public class AppViewManager {
private static String getLoggedInUser(User user){
if (user != null) {
return user.getId();
} else {
return "Not Logged In";
}
}
public static final AppViewRegistry REGISTRY = new AppViewRegistry();
public static final AppView PRIMARY_VIEW = view("Home", PrimaryPresenter.class, MaterialDesignIcon.HOME, SHOW_IN_DRAWER, HOME_VIEW);
public static final AppView SECONDARY_VIEW = view("Task List", SecondaryPresenter.class, MaterialDesignIcon.LIST, SHOW_IN_DRAWER);
public static final AppView SETTINGS_VIEW = view("Settings", SettingsView.class, MaterialDesignIcon.SETTINGS_APPLICATIONS, SHOW_IN_DRAWER);
public static final AppView INTRAFACILITYTRANSFER_VIEW = view("Intra-Facility Transfer", IntraFacilityView.class, MaterialDesignIcon.EDIT_LOCATION);
private static AppView view(String title, Class<? extends GluonPresenter<?>> presenterClass, MaterialDesignIcon menuIcon, AppView.Flag... flags ) {
return REGISTRY.createView(name(presenterClass), title, presenterClass, menuIcon, flags);
}
private static String name(Class<? extends GluonPresenter<?>> presenterClass) {
return presenterClass.getSimpleName().toUpperCase(Locale.ROOT).replace("PRESENTER", "");
}
public static void registerViewsAndDrawer(MobileApplication app) {
for (AppView view : REGISTRY.getViews()) {
view.registerView(app);
}
Image image = new Image(WcatsAndroidDemo.class.getResourceAsStream("/icon.png"));
NavigationDrawer.Header header = new NavigationDrawer.Header("\nWCATS" + "- " + getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()),
"Waste Management System", new ImageView(image));
// TODO: Add a footer to the drawer that contains settings, help & feedback, and About
NavigationDrawer.Footer footer = new NavigationDrawer.Footer("No tasks currently need to be synchronized.", null);
//Create the sub items for the drawer
NavigationDrawer.Item about = new NavigationDrawer.Item("About", MaterialDesignIcon.INFO.graphic());
NavigationDrawer.Item logOut = new NavigationDrawer.Item("Logout", MaterialDesignIcon.EXIT_TO_APP.graphic());
// TODO: make the rest of the submenu items that go in the header.
DefaultDrawerManager drawerManager = new DefaultDrawerManager(app, header, REGISTRY.getViews()) {
{
NavigationDrawer drawer = getDrawer();
drawer.visibleProperty().addListener((observable, oldValue, newValue) -> {
if (newValue){
header.setTitle("\nWCATS - " + getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()));
}
});
// Add items
drawer.setFooter(footer);
footer.setStyle("-fx-text-size: 6");
drawer.getItems().addAll(new Separator(), about, logOut, new Separator());
// TODO: provide action based on item selected
drawer.selectedItemProperty().addListener(((observable, oldValue, newValue) -> {
if(newValue.equals(about)) {
System.out.println("test");
} else if (newValue.equals(logOut)){
WcatsAndroidDemo.getInstance().userLogout();
} else if (newValue.equals(SECONDARY_VIEW.getMenuItem())){
if (getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()).equals("Not Logged In")) {
AppViewManager.PRIMARY_VIEW.switchView();
} else {
AppViewManager.SECONDARY_VIEW.switchView();
}
}
} ));
}
};
drawerManager.installDrawer();
}
}
Lastly, here is the Presenter class for the view that I am navigation from and want to go to the task that is selected.
public class SecondaryPresenter extends GluonPresenter<WcatsAndroidDemo> {
#FXML
private View homeView;
#FXML
public CharmListView<CharmHomeNavTask, Integer> charmListView;
public void initialize() {
homeView.setShowTransitionFactory(BounceInRightTransition::new);
AppBar appBar = getApp().getAppBar();
homeView.showingProperty().addListener((observable, oldValue, newValue) -> {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> getApp().showLayer(DRAWER_LAYER)));
appBar.setTitleText("Task Selection");
});
charmListView.setFloatingHeaderVisible(false);
charmListView.setItems(CharmHomeNavTasks.getTasksList());
charmListView.setCellFactory(param -> new CharmHomeNavTaskCell(charmListView));
charmListView.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.getTaskName().equals("Intra-Facility Transfer")){
AppViewManager.INTRAFACILITYTRANSFER_VIEW.switchView(ViewStackPolicy.SKIP);
}
});
}
}
Just in case you do need it to figure anything out here is the view that I am trying to navigate to.
public class IntraFacilityView extends GluonPresenter<WcatsAndroidDemo> {
#FXML
public ChoiceBox facilityCMBox;
#FXML
public ChoiceBox storageUnitOrgCMBox;
#FXML
public ChoiceBox storageUnitDestCMBox;
#FXML
public ChoiceBox gridXCMBox;
#FXML
public ChoiceBox gridYCMBox;
#FXML
public ChoiceBox gridZCMBox;
#FXML
public CheckBox organizeUnitCHKBox;
#FXML
public Button viewMoreReqsBTN;
#FXML
public Button viewPendingMovesBTN;
#FXML
public Button resumeTaskBTN;
#FXML
private View intrafacility;
public void initialize(){
intrafacility.setShowTransitionFactory(BounceInRightTransition::new);
intrafacility.showingProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
AppBar appBar = getApp().getAppBar();
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> {
getApp().showLayer(DRAWER_LAYER);
}));
appBar.setTitleText("Intra-Facility Transfer");
appBar.getActionItems().add(MaterialDesignIcon.CLOSE.button(e -> {
AppViewManager.SECONDARY_VIEW.switchView();
}));
}
});
}
public void handleOrganizeUnitsCHKBox(ActionEvent actionEvent) {
}
public void handleViewMoreReqs(ActionEvent actionEvent) {
}
public void handleResumeTask(ActionEvent actionEvent) {
}
public void handlevVewPendingMoves(ActionEvent actionEvent) {
}
}
I know that some Android devices are not the best with JavaFxPorts, but I haven't seen anything about Panasonic toughpads having known issues like Samsung. I did have to do some janky stuff to get the abdroid devices to register the touches as clicks, but that seems to be working fine now.
This has me as a standstill as I can not figure out why Android can not find the location but it works fine on the desktop.
Here is the file structure:
Project structure

JavaFX TableView Issues Adding Rows + Organizing Multi Controller and FXML App

I'm new to JavaFX and I've been at this code for about 8 hours now and I've become a bit delusional with the code. My two main problems are:
Can't add new items to the TableView using my popUp box display().
Feels messy and unorganized. Any tips for better communication between FXML and Controllers? (Again I'm new so it could be that I've stared too long at it)
My main class
public class Main extends Application {
public static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) throws Exception {
try {
primaryStage = new Stage();
window = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("Fissto.fxml"));
Scene scene = new Scene(root);
window.setTitle("Fissto - the File Storage App!");
window.setScene(scene);
window.show();
}catch(Exception e){
e.printStackTrace();
}
// C.setLibraryStage();
}
}
My main Controller class (I have two sub ones that connect in the Fissto.fxml)
public class Controller implements Initializable{
Main main;
#FXML LibraryController libraryController = new LibraryController();
#FXML MergePageController mergePageController = new MergePageController();
private AddImageController addImageController = new AddImageController();
#FXML public void initialize(URL location, ResourceBundle resources){
System.out.println("View is now loaded!");
main = new Main();
libraryController.init(this);
mergePageController.init(this);
addImageController.init(this);
}
//Interface Initialization
public void setMergeStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/MergePage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
public void setLibraryStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/LibraryPage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
//Closing a window
public void closeWindow(){
main.primaryStage.close();
}
}
And finally the controller for the page that holds the TableView
public class LibraryController {
private Controller main;
//Library TableView Controllers
#FXML public TableView<Image> library;
#FXML private TableColumn<Image, String> NameColumn = new TableColumn<>();
#FXML private TableColumn<Image, ArrayList<String>> TagsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> CommentsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> FileLocationColumn = new TableColumn<>();
#FXML private TableColumn<Image, Integer> PointsColumn = new TableColumn<>();
public void init(Controller main){
System.out.println("LibraryPage Loading");
this.main = main;
addDataToColumns();
library = new TableView<>();
library.getItems().setAll(getImages());
System.out.println("LibraryPage Loaded");
}
//Initializes the column titles
private void addDataToColumns(){
NameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
TagsColumn.setCellValueFactory(new PropertyValueFactory<>("tags")); //TODO Convert to String format
CommentsColumn.setCellValueFactory(new PropertyValueFactory<>("comments"));
FileLocationColumn.setCellValueFactory(new PropertyValueFactory<>("filelocation"));
PointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
}
//Gets all of the images
private ObservableList<Image> getImages() {
//TODO: Add where to actually get the data from
ObservableList<Image> images = FXCollections.observableArrayList();
String s = "Dog, Cat, Jumping Jack,";
ArrayList<String> list = Image.getTagOrganizer(',', s);
images.add(new Image("Test", list, "Comment", "No File Location, yet!", 10));
String k = "Calculus, Complex Numbers, Hard dude,";
ArrayList<String> list2 = Image.getTagOrganizer(',', k);
images.add(new Image("Number2", list2, "I love MathClub", "No File Location, yet!", -10));
return images;
}
This last class is the popup menu that takes in input to put in the GridPane
public class AddImageController {
private Controller main;
public void init(Controller main){
System.out.println("ImagePage Loaded");
this.main = main;
}
//Submitting an image to the library from the AddImagePage
public TextField nameInput;
public TextField tagsInput;
public TextField commentInput;
public TextField pointsInput;
public Label errorMessage;
/** TODO: Make it so that it writes to file then theoretically, the main controller should read from file every so often
* Main functionality for adding the information from the form to the database */
public void submitImage(){
if(!(nameInput.getText().trim().isEmpty()) && !(tagsInput.getText().trim().isEmpty()) && !(pointsInput.getText().trim().isEmpty())) {
if (isInt(pointsInput)) {
// System.out.print("Sent to database, *whoosh!*");
LibraryController c = new LibraryController();
ArrayList<String> s = Image.getTagOrganizer(',', tagsInput.getText());
Image image = new Image(nameInput.getText(), s, commentInput.getText(),"Location Needed", Integer.parseInt(pointsInput.getText()));
c.library.getItems().add(image);
clearInputs();
}
}else {
errorMessage.setText("Fill every field");
}
}
//Clears the input fields in the AddImagePage
public void clearInputs(){
nameInput.clear();
tagsInput.clear();
commentInput.clear();
pointsInput.clear();
errorMessage.setText("");
}
//Submission format verifiers
private boolean isInt(TextField input){
try{
int i = Integer.parseInt(input.getText());
errorMessage.setText("");
return true;
}catch (NumberFormatException e){
System.out.println("Oh no: " + input.getText() + " is not an integer");
errorMessage.setText("Points must be a number");
return false;
}
}
//Image Selection Handler
public void imageSelectionHandler(){
}
}
I understand it may be hard to read, so any feedback on how to make it easier to read in the future is much appreciated.

JavaFX Switch Scene in a SplitPane?

At the Base I have an AnchorPane then a SplitPane. On the left pane I have a listView and depending on the list element selected, the right pane displays the appropriate content. The way I have done this is by overlapping AnchorPanes and setting them to .setVisible(false) initially and as they are selected I set them to .setVisible(true) like so :
public void listSelection() {
String selection = listView.getSelectionModel().getSelectedItem();
switch(selection) {
case "Speed of sound":
disableOld(); // disables old AnchorePane
response.setText("Speed of sound conversion");
AnchorPane1.setVisible(true);
break;
case "Temperature conversion":
disableOld();
response.setText("Temperature conversion");
AnchorPane2.setVisible(true);
break;
}
}
I would like to know how to produce the same effect visually but with different scenes as I would like for each new AnchorPane to have it's own FXML and ControllerClass.
You can implement something like this :
Your main class :
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Title");
primaryStage.setScene(createScene(loadMainPane("path_of_your_fxml")));
primaryStage.show();
}
private Pane loadMainPane(String path) throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(path));
return mainPane;
}
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(mainPane);
return scene;
}
public static void main(String[] args) {launch(args); }
Then you can create a separate class call Navigator to store all your fxml paths:
public class Navigator {
private final String P1;
private final String P2;
//then you can implement getters...
public String getP1() {
return P1;
}
public String getP2() {
return p2;
}
private static FxmlController Controller;
public static void loadPane(String fxml) {
try {
FxmlController.setPane(
(Node) FXMLLoader.load(Navigator.class.getResource(fxml)));
} catch (IOException e) {
e.printStackTrace();
}
}
public Navigator() throws IOException {
this.P1 = "p1.fxml";
this.P2 = "p2.fxml";}
In your main FxmlController(which is the controller of the permanent layer of your application , rest of the stack-panes-{p1 and p2} will load on your permanent layer )
This is how you load layers on the main FxmlController :
#FXML
private StackPane stackPaneHolder;
...
public void setPane(Node node) {
if (stackPaneHolder.getChildren().isEmpty()) {
//if stackPaneHolder is empty
stackPaneHolder.getChildren().add(node);
} else {
if (stackPaneHolder.getClip() != node) {
//if stackPaneHolder is not empty then remove existing layer and add new layer
stackPaneHolder.getChildren().remove(0);
stackPaneHolder.getChildren().add(0, node);
}
}
}
Then you can load panes by pressing a button like below :
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
This is how it works :

Categories