I have been trying to add a remove button so that it removes a selected row in my tableview. My problem is slightly different from those i have found elsewhere. My problem lies behind the fact that in my application i have used 1 FXML file as the basis of several different interfaces. When i initialize 1 of these and use the functionality of the remove button it removes the tableView rows fine and how it is supposed to. But when i initialize a second interface (still using the same FXML and henceforth same variable names) it only lets me delete items in the 2nd tableView and not in the first. I have a good idea as to why this is, but i do not know how to fix it.
Here are a few methods i have tried:
public void removeProject(ActionEvent event){
int index = projectTable.getSelectionModel().getSelectedIndex();
if(index >=0){
projectTable.getItems().remove(index);
}else
//Show warning
}
}
Another different approach:
public void removeProject(ActionEvent event){
ObservableList<Project> currentlySelected, allProjects;
currentlySelected = projectTable.getSelectionModel().getSelectedIndex();
allProjects = projectTable.getItems();
currentlySelected.forEach(allProjects::remove);
}
Also please keep in mind that both of these methods work fine until i initialize a second tableView. After this point the value i get from both my ObservableList<Project> currentlySelected and my int Indexare -1 when i am trying to select a row in a table which isn't the most recent initialization of the interface. Sorry if it sounds a bit confusing but it is a bit confusing, if i can clear anything up ill add an edit later
Cheers
Edit 1:
Here is an example where i am trying to remove from the table based on which interface it is in currently:
ObservableList<Project> itemsSelected;
switch(counter){
case 1:
itemsSelected = projectTable.getSelectionModel().getSelectedItems();
itemsSelected.forEach(projTableStorage.getProj1()::remove);
break;
case 2:
itemsSelected = projectTable.getSelectionModel().getSelectedItems();
itemsSelected.forEach(projectTableStorage.getProj2()::remove);
break;
A few things to note:
The projectTableStorage.getProj() is used to store all of the data in each table, the returned value is an ObservableList and i use this to set the items of the table whenever that interface is loaded so the data is not lost when swapping between interfaces, perhaps there is more efficient ways to go about it, this is just how i did it
There are 7 of these interfaces, i am just testing with 2 to make testing shorter and simpler for now at least
Edit 2:
Loading FXML files:
public AnchorPane initLayouts(FXMLLoader loader, AnchorPane projectLayout, MainLayoutController mainLay) throws IOException{
loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/control/view/ProjectLayout.fxml"));
loader.setController(mainLay);
projectLayout = (AnchorPane) loader.load();
return projectLayout;
}
in MainLayoutController:
public AnchorPane loadLayout(AnchorPane projectLayout, Project project, FXMLLoader loader)throws IOException{
projectLayout = project.initLayouts(loader, projectLayout, this);
return projectLayout;
}
load layout is called whenever a button is pressed
Edit 3:
Here is the 'removeProjec' code again
public void removeProject(ActionEvent event){
ObservableList<Project> itemsSelected, currentProject;
itemsSelected = getProjectTable().getSelectionModel().getSelectedItems();
itemsSelected.forEach(getProjectTable().getItems()::remove);
System.out.println("value of itemsSelected is : " + itemsSelected);
}
and my project table storage:
ObservableList<Project> project1= FXCollections.observableArrayList();
ObservableList<Project> project2 = FXCollections.observableArrayList();
public void setProject1(Project project){
project1.add(project);
}
public void setProject2(Project project){
project2.add(project);
}
public ObservableList<Project> getProject1(){
return project1;
}
public ObservableList<Project> getProject2(){
return project2;
}
And also just in case the getProjectTable method(Tried with and without annotation):
#FXML
public TableView<Project> getProjectTable(){
return projectTable;
}
Edit 4:
public void createNewProjectLayout(ActionEvent event) throws IOException{
if(event.getTarget() == newProjectLayoutButton1){
projectLayout1 = loadOrReloadProjectLayout(newProjectLayoutButton1, project1, projectLayout1, 1);
setTable(Counter);
}else if(event.getTarget() == newProjectLayoutButton2){
projectLayout2 = loadOrReloadProjectLayout(newProjectLayoutButton2, project2, projectLayout2, 2);
setTable(Counter);
}
A few things to note:
The loadOrReload is simply to load the file the first time it is clicked using the loadLayout method previously mentioned, and then reload the result of loadLayout for the next time it is pressed
The setTable is used to set any data stored previously in the table to be put in the table again using the observable lists from the ProjectTableStorage class
Related
Ok so this is the first question I've asked on StackOverflow so apologies if its unclear.
Basically, I am making a program in JavaFx that is an ordering system for a fake Cafe. Its for an assignment and it really doesn't have to make conventional sense because my curriculum doesn't really care if it is actually useful or not, they just want to see you code some random stuff.
Anyways, the problem i am having atm is that I am trying to make it so when I open the Main page called MainPage.fxml, 4 things will be pre-disabled/enabled. These elements are PinPane Which contains the sign-in buttons and labels), PrimaryPane (which contains all buttons leading to different ordering pages), SettingsBtn (Sends user to settings), and LogoutBtn (Self expanatory).
This is important because when the program is first opened, MainPage is the first thing that is started. Once a user Signs in, and heads off to another page to select an item however, when they come back to the MainPage, where the current-order is displayed in PrimaryPage (I haven't actually done any code for that yet), I want to ensure that the disabled/enabled states of all 4 elements remains the same as when user left to go to another Page.
Currently, I am using a static class called DataContainer.java, which contains all data shared by the program, and I thought i could put 3 boolean variables which basically just tell the program on the opening of MainPage what is disabled and enabled.
However, my Primary problem is, I can't seem to be able to change the state of any of these elements on startup, and i have no idea how to do that other wise.
My code for the MainPageController.java is below:
'''
public class MainPageController {
#FXML private Label Price; //fx:if -> Price
#FXML public Pane PrimaryPage, PinPane; //fx:id -> PrimaryPage
#FXML public Label Pin; //fx:id -> Pin
#FXML public Button LogoutBtn, SettingsBtn;
public void Check(ActionEvent event) throws IOException{
// This is the method I use to check the entered pin against current
// saved pins.
DataContainer.DataContainer();
// This is just a method i use for testing, it adds a manager account that i can sign in with
// each time the program is opened because I haven't introduced account creation and saving
// yet
int pin = Integer.parseInt(Pin.getText());
int i = DataContainer.Users.size();
int x = 0;
while (x <= i-1){
if (DataContainer.Users.get(x).PinNumber == pin){
// In this, once the pin is verified, each element is enabled and disabled, and the
// boolean variables are set as well for future use
System.out.println("test");
DataContainer.UserIndex = x;
PrimaryPage.setDisable(false);
LogoutBtn.setDisable(false);
DataContainer.PrimaryPage = true;
Pin.setText("");
PinPane.setDisable(true);
DataContainer.PinPane = false;
break;
}
x = x + 1;
}
if (DataContainer.Users.get(DataContainer.UserIndex).Position.equals("Manager")){
SettingsBtn.setDisable(false);
DataContainer.SettingsBtn = true;
}
'''
This is the code for DataContainer.java
'''
public class DataContainer{
public static void main(String args[]){
}
public static void DataContainer(){
Users.add(owner);
System.out.println("test");
}
static boolean PinPane = true, PrimaryPage = false, SettingsBtn = false;
// These boolean values are relevant to the MainPage application
// Their purpose is to retain the information of the state in which the user
// left the main page, i.e, if the PinPage is disabled, the PrimaryPage is enabled, etc.
// this is important as if these variables don't exist the MainPage and its elements
// go back to their default state and the user has to re-sign in.
static String firstname, lastname, position;
static int PinNo, PhoneNo, UserIndex;
public static UserVariables user = new UserVariables(firstname, lastname, position, PinNo,
PhoneNo);
static UserVariables owner = new UserVariables("Test", "User", "Manager", 1234,
0434553);
public static ArrayList<UserVariables> Users = new ArrayList<UserVariables>();
}
'''
And finally this is the code for FInalIA.java (Main class):
'''
public class FInalIA extends Application implements Serializable {
public static void main(String args[]) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// StackPane root = new StackPane();
Parent root = (Parent) FXMLLoader.load(getClass().getResource("MainPage.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Main Page");
stage.setScene(scene);
stage.setResizable(false);
stage.show();
MainPageController.Open();
}
}
'''
And Finally, this is the code i was thinking of using, by making a public static method called 'Open()', and making all the panes static, and just just calling this method when ever MainPage is opened.
'''
public static void Open(){
if (DataContainer.PinPane == false){
PinPane.setDisable(true);
}
else{
PinPane.setDisable(false);
}
if(DataContainer.PrimaryPage == false){
PrimaryPage.setDisable(true);
LogoutBtn.setDisable(true);
}
else{
PrimaryPage.setDisable(false);
LogoutBtn.setDisable(false);
}
if(DataContainer.SettingsBtn == false){
SettingsBtn.setDisable(true);
}
else{
SettingsBtn.setDisable(false);
}
}
'''
Thanks to whoever helps me out with this (Also can you guys plz tell me if what i am writing is to non-concise and irrelevant or if its actually good)
Step one make Open non-static. You're going to create an instance of your controller and it will manage the associated items.
public class MainPageController implements Initializable{
#FXML private Label price; //fx:if -> Price
#FXML public pane primaryPage, pinPane; //fx:id -> PrimaryPage
#FXML public Label pin; //fx:id -> Pin
#FXML public button bogoutBtn, settingsBtn;
public void Check(ActionEvent event) throws IOException{
//why is this method included but not open?
}
#Override
public void initialize(URL url, ResourceBundle rb){
if (DataContainer.PinPane == false){
pinPane.setDisable(true);
} else{
pinPane.setDisable(false);
}
primaryPage.setDisable( ! DataContainer.PrimaryPage );
logoutBtn.setDisable(! DataContainer.PrimaryPage);
//etc etc
}
}
I've made your controller implement Initializable, that way it has a method initialize that gets called when you start. I've also improved the naming, eg Pin should be named pin. If this doesn't work for you, I can replace this with a small enclosed example.
You don't need to implement Initializable javafx will automatically call an appropriate initialize method.
I created a TableTree that contains object of class Component that has a boolean property "selected".
I want to hide the rows from the table where the rows component is not selected.
I tried this:
componentTree.setRowFactory(new Callback<TreeTableView<Component>, TreeTableRow<Component>>() {
#Override
public TreeTableRow<Component> call(TreeTableView<Component> param) {
TreeTableRow<Component> row = new TreeTableRow<Component>() {
#Override
protected void updateItem(Component component, boolean empty) {
if(!empty) {
if (!component.isSelected()) {
setVisible(false);
setManaged(false);
System.out.println("hide");
} else {
setVisible(true);
setManaged(true);
System.out.println("show");
}
}
}
};
return row;
}
});
On system.out I can see a lot of "show" and "hide" messages, but this doesn't affect the table, all rows are shown as before.
Any idea on this topic?
Thanks!
I used eclipse's fx.ui.controls library for the same achieve the same goal before.
<dependency>
<groupId>at.bestsolution.eclipse</groupId>
<artifactId>org.eclipse.fx.ui.controls</artifactId>
<version>2.2.0</version>
</dependency>
The library provides a class: FilterableTreeItem<T> under the tree package. This class was designed to be used in cases like yours. You can provide a Predicate to the root of the tree and the items will get hidden when the value given changes:
// Children
final FilterableTreeItem<Component> childNode1 = new FilterableTreeItem<>(component1);
final FilterableTreeItem<Component> childNode2 = new FilterableTreeItem<>(component2);
final FilterableTreeItem<Component> childNode3 = new FilterableTreeItem<>(component3);
// Root
final FilterableTreeItem<Component> root = new FilterableTreeItem<>(rootComponent);
root.getInternalChildren().setAll(childNode1, childNode2, childNode3);
root.setPredicate((parent, value) -> value.isSelected());
// TreeTableView
final TreeTableView<Component> treeTableView = new TreeTableView<>(root);
Note that you have to use getInternalChildren() to add children and the default getChildren().
FilterableTreeItem<T> also provides a predicateProperty() that you can bind to another property in case you need to update the how items are shown or hidden.
Another advatage of this class is that it shows the whole path up to the root of the items matching that predicate.
I need an icon for my MenuItem's.
This is like a "worker class" to get the ImageView of the icon :
public class IconFactory {
private static ImageView HLP_BOOK_JFX;
public enum ICONS {
BASCET_REMOVE, BASCET_PUT, SAVE, OPEN, ARROW_RIGHT, ARROW_LEFT, ARROW_UP, ARROW_DOWN, CLOCK, ANALOG_SIGNAL, DIGITAL_SIGNAL, REFRESH, GREEN_PLUS, NETWORK, OK, CANCEL, RIGHT_NAV2, LEFT_NAV2, PLAY, PAUSE, LIST_ADD, PAGE_FIND, SET_PARAM, DOWNLOAD, UPLOAD, LOG_FILE, WARNING, INFO, LOG_DIAG, DATA_TRANS, TREE, FILTER, SEARCH, PARAM, ERASE, RESETDEF, RESETDEF2, DEBUG_BUG, INTERNATIONAL, CLOSE, HLP_BOOK
}
public static ImageView getImage(ICONS en) {
switch (en) {
case HLP_BOOK:
if (HLP_BOOK_JFX == null)
HLP_BOOK_JFX = new ImageView(new Image(IconFactory.class.getResourceAsStream("help_book.png")));
return HLP_BOOK_JFX;
}
return null;
}
When I use myMenuItem.setGraphic(IconFactory.getImage(ICONS.HLP_BOOK)) for a single menu item it works perfectly.
But then, when I want to generate two menus in a loop and set the same graphic, one MenuItem has no icon displayed. (the first one in loop in the code below).
My code:
while (keys.hasMoreElements()) {
// that will do 2 loops, do not care about how
MenuItem subMenuHelp = new MenuItem("MenuItem");
subMenuHelp.setGraphic(IconFactory.getImage(ICONS.HLP_BOOK));
subMenuHelp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// do not care
openHelpFile(link);
}
});
System.out.println(((ImageView) subMenuHelp.getGraphic()).toString());
myMenu.getItems().add(subMenuHelp);
}
As you can see, I added a System.out.println to see if a graphic was set for the current item.
Result in console : both lines (MenuItem) with the same ImageView:
ImageView#79814766[styleClass=image-view]
ImageView#79814766[styleClass=image-view]
I did exactly the same in Swing (but with Icons and .setIcons() function) and it worked very well. I've also looked for a "repaint" function to force displaying but no way.
Hope you can help me!
This is because the same Node cannot be attached to the scene-graph multiple times and - as you even state - you are adding the same ImageView object.
From the documentation of Node:
If a program adds a child node to a Parent (including Group, Region,
etc) and that node is already a child of a different Parent or the
root of a Scene, the node is automatically (and silently) removed from
its former parent.
The solution is to modify getImage method of IconFactory to return a new ImageView instance on each call or to return Image instances rather than ImageView instances (the second one fits better to the name "IconFactory" I think).
You could store the Image instance instead of storing the ImageView to avoid re-loading the Image itself. You could check this question as reference: Reusing same ImageView multiple times in the same scene on JavaFX
A possible update on IconFactory:
public class IconFactory {
private static HashMap<ICON, Image> images = new HashMap<ICON, Image>();
public enum ICON {
BASCET_REMOVE, BASCET_PUT, SAVE, OPEN, ARROW_RIGHT, ARROW_LEFT, ARROW_UP, ARROW_DOWN, CLOCK, ANALOG_SIGNAL, DIGITAL_SIGNAL, REFRESH, GREEN_PLUS, NETWORK, OK, CANCEL, RIGHT_NAV2, LEFT_NAV2, PLAY, PAUSE, LIST_ADD, PAGE_FIND, SET_PARAM, DOWNLOAD, UPLOAD, LOG_FILE, WARNING, INFO, LOG_DIAG, DATA_TRANS, TREE, FILTER, SEARCH, PARAM, ERASE, RESETDEF, RESETDEF2, DEBUG_BUG, INTERNATIONAL, CLOSE, HLP_BOOK
}
public static Image getImage(ICON en) {
if (!images.containsKey(en)) {
switch (en) {
case HLP_BOOK:
images.put(en, new Image(IconFactory.class.getResourceAsStream("help_book.png"))); break;
default:
return null;
}
}
return images.get(en);
}
}
Usage after the update:
subMenuHelp.setGraphic(new ImageView(IconFactory.getImage(ICONS.HLP_BOOK)));
I have some trouble using javaFX to fill a tableColumn with some data according to a selected index from an other table.
the Table starts empty.
and then I want to fill it when the user press a button. (so far, so good)
here's what the button controller looks like :
#FXML
private void handleNextRequest() {
int selectedIndex = headingTable.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0)
mainApp.updateEntity(headingColumn.getCellData(selectedIndex));
entityTable.setItems(mainApp.getEntity());
}
So this calls a function from the main class which update my observable list.
The selectedIndex parameter is used to determine which data I have to load in the list (those data are located on a database which I can access via a web service, hence the "api" (which works fine)).
So here's what this function looks like :
public void updateEntity(String header){
try {
this.entity.clear();
int i = 0;
while(header != heading.get(i).getName()){
i++;
}
api.getEntity(new URL(heading.get(i).getURL()), this.entity, primaryStage);
} catch(MalformedURLException e){}
}
And up to this point everything is functional. when pressing the button the function is called properly and the observable list (entity) is updated correctly. (checked and re-checked)
and then... boom.
the "setItems" function (back to the button controller) doesn't seems to like whatever I've done and throw a NullPointerException.
If someone could help me understand what the problem might be here I would be delighted !
EDIT :
here's the initialize code that I have :
I have this in the initialize method :
#FXML
private void initialize() {
headingColumn.setCellValueFactory(CellData -> CellData.getValue().nameProperty());
entityColumn.setCellValueFactory(CellData -> CellData.getValue().nameProperty());
showTableDetails(null);
headingTable.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> showTableDetails(newValue));
}
and both entityTable and entityColumn declared properly.
Okay I figured it out.
Pretty dumb mistake but the fx:id="entityTable" was missing in the fxml file.
thank you for helping me realizing that !
The content of the tab is formed and displayed when the application is loaded. Later the content of the tab may be changed by other actions. I want to show the newer content after each action. And each time when I click the tab sheet, the content should be refresh/updated. But I failed.
//the content of the tab from the "reprintsTab" class
//in the "reprintsTab" it query data from database and print out
//later I update the data in the database from somewhere else, and I want the tab shows the new content
//I want to click the tab sheet to reload the "reprintTab" class and print out the new content
//here is what I did:
public TabSheet sheet;
//add tab and add the content from "reprintTab" into this tab
sheet.addTab(new reprintsTab());
//add the listener
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
//I know it does not work, because it only reload the class. but not put the content under the tab I want
new reprintsTab();
}
});
What should I do? please help me, thanks.
You can use TabSheet.replaceComponent method to do this:
//Field to store current component
private reprintsTab currentComponent;
//during initialization
currentComponent = new reprintsTab();
sheet.addTab(currentComponent);
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
reprintsTab newComponent = new reprintsTab();
sheet.replaceComponent(currentComponent, newComponent);
currentComponent = newComponent;
}
});
Also, you might want to reload this tab only when it's shown:
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
if (event.getTabSheet().getSelectedTab() == currentComponent) {
//here goes the code
}
}
});
This should work for you, but I would suggest a cleaner approach: implement reprintsTab as a container for components, create method reload or buildInterface method to refresh its' state, so you can just call:
currentComponent.reload();
when you need to update interface.
Also, I hope reprintsTab is just an example name, java class names starting with lowercase letter look ugly.