JavaFX Inhalt einer ViewTable durch Aktion einer Menubar ändern welche in einer anderen fxml ist

ZarakiKen

Mitglied
Hallo, ich beschäftige mich seit kurzen mit JavaFX und möchte eine kleine Anwendung erstellen, welche ganz simpel Aufgaben notiert. Oben befindet sich eine Menüleiste und in der Mitte des Fensters eine Tabelle mit jeweils einer Aufgabe in einer Zeile. Wenn man nun in der Menüleiste auf "new" oder "delete" klickt soll die entsprechende Aktion in der Tabelle durchgeführt werden. In SWING hätte ich mir eine GuiManager Klasse und eine controllerklasse für die Aufgabenliste gemacht und diese im Konstruktor übergeben, damit ich auf die Attribute zugreifen und Änderungen vornehmen kann. Beim initialisieren etwas in die Tabelle zu bekommen ist kein Problem. Meine Frage ist nun: Wie kann ich über eine Aktion aus der Menübar auf meine Liste mit Aufgaben zugreifen (löschen, erstelle, editieren etc.)? Ich habe im Controller der TableView versucht statische Methoden zu erstellen aber dadurch sind andere Probleme entstanden und ich glaube, dass ist auch keine vernünftige Lösung. Außerdem konnte ich so nicht auf den "selectedIndex" zugreifen. Ich dachte mir, dass beim Klick auf ein Feld der Menübar, der MenübarController eine Methode aus dem TableViewController aufruft aber das scheint nicht zu funktionieren weil er ihn nicht kennt und ich weiß nicht genau wie ich die Verknüpfung herstellen soll da der Controller der xml in der xml zugewiesen wird.

Hier ist mein Code:

MainApp.java:
Java:
package task_reminder;

import java.io.IOException;

public class MainApp extends Application {
	
	private BorderPane rootLayout;
	private Stage primaryStage;

	@Override
	public void start(Stage primaryStage) throws Exception {
		this.primaryStage = primaryStage;
		primaryStage.setTitle("Task Reminder");
		initRootLayout();
		showTaskTable();
		

	}
	
	public void initRootLayout() {
        try {
            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(MainApp.class.getResource("/view/RootLayout.fxml"));
            rootLayout = (BorderPane) loader.load();
            
            // Show the scene containing the root layout.
            Scene scene = new Scene(rootLayout);
            scene.getStylesheets().add("/css/tableStyle.css");
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	
	
	public void showTaskTable() {
	  try {
          // Load person overview.
          FXMLLoader loader = new FXMLLoader();
          loader.setLocation(MainApp.class.getResource("/view/TaskView.fxml"));
          GridPane taskOverview = (GridPane) loader.load();
          
          // Set person overview into the center of root layout.
          rootLayout.setCenter(taskOverview);
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

RootLayout.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>

<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="control.RootController">
<top><MenuBar BorderPane.alignment="CENTER">
  <menus>
    <Menu mnemonicParsing="false" text="File">
      <items>
        <MenuItem mnemonicParsing="false" text="load list of tasks" fx:id="load"/>
      </items>
      <items>
        <MenuItem mnemonicParsing="false" text="export as..." fx:id="export"/>
      </items>
      <items>
        <MenuItem mnemonicParsing="false" text="Close" fx:id="close" onAction="#closeApplication"/>
      </items>
    </Menu>
    <Menu mnemonicParsing="false" text="Edit" fx:id="edit">
    <items>
        <MenuItem mnemonicParsing="false" text="New" fx:id="new" onAction="#addTask"/>
      </items>
      <items>
        <MenuItem mnemonicParsing="false" text="Delete" fx:id="delete"/>
      </items>
    </Menu>
    <Menu mnemonicParsing="false" text="Help" fx:id="help">
     <items>
        <MenuItem mnemonicParsing="false" text="?" fx:id="item_help" onAction="#showHelp"/>
      </items>
      <items>
        <MenuItem mnemonicParsing="false" text="About" fx:id="info" onAction="#showInfo"/>
      </items>
    </Menu>
  </menus>
</MenuBar>
</top></BorderPane>

TaskView.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>


<GridPane alignment="CENTER" hgap="5.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml" xmlns="http://javafx.com/javafx/8" fx:controller="control.TaskController">
    <padding>
        <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
    </padding>
    <Label style="-fx-font: NORMAL 20 Tahoma;" text="Tasks" GridPane.columnIndex="0" GridPane.rowIndex="0" >
    </Label>
    <TableView fx:id="tableView" GridPane.columnIndex="0" GridPane.rowIndex="1"  style="-fx-background-color: 'DBDBDB'">
     <columns>
          <TableColumn text="Name" prefWidth="200" fx:id="col_name">
          </TableColumn>
          <TableColumn text="Due Date" prefWidth="150" fx:id="col_dueDate">
          </TableColumn>
          <TableColumn text="Prio" prefWidth="100" fx:id="col_prio">
          </TableColumn>
     </columns>    
    </TableView>
</GridPane>

RootController.java
Java:
package control;

import java.net.URL;

public class RootController{
	

	@FXML
	protected void showInfo(){
		Alert alert = new Alert(AlertType.INFORMATION);
		alert.setTitle("about us");
		alert.setHeaderText("Task Reminder");
		alert.setContentText("\u00a9 Testname, \ncontact: [email]test@gmx.de[/email]");
		alert.initModality(Modality.APPLICATION_MODAL);
		alert.showAndWait();
    }
	
	@FXML protected void showHelp() {
		
	}
	
	@FXML protected void closeApplication() {
		
	}
	
	@FXML protected void addTask() {
	
	}
	
}

TaskController.java
Java:
package control;

import java.net.URL;

public class TaskController implements Initializable {
		
    @FXML
	TableView<Task> tableView;
	 
	@FXML
	TableColumn col_name;
	 
    @FXML
	TableColumn col_dueDate;
	    
	@FXML
	TableColumn col_prio;

	
	ObservableList<Task> data;
		
	
	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		 // Set up the table data
        col_name.setCellValueFactory(
            new PropertyValueFactory<Task,String>("name")
        );
        col_dueDate.setCellValueFactory(
            new PropertyValueFactory<Task,String>("dueDate")
        );
        col_prio.setCellValueFactory(
            new PropertyValueFactory<Task,String>("prio")
        );
  
        data = FXCollections.observableArrayList();
        tableView.setItems(data);
        
        Task t = new Task("Einkaufen","20.03.2015","mittel");
        data.add(t);
        
        Task r = new Task("Aufräumen","04.04.2015","hoch");
        data.add(r);
        
        tableView.setItems(data);
	}
	
	
	protected  void addTask() {
        //???
	}
	
	protected  void deleteTask() {
        //???
	}

}
 
Da gibt es jetzt mehrere Möglichkeiten wie du das handhaben könntest.

Erste Möglichkeit du lädst mit deinem FXMLLoader deine Controller und verknüpft sie.

Oder du machst es direkt im FXML mit dem fx:include und kannst dann die Controller injecten lassen.

Kleines Beispiel (Mit dem fx:include):

Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.CenterViewController">
   <center>
      <Label fx:id="label" BorderPane.alignment="CENTER" />
   </center>
</BorderPane>
Java:
package controllers;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class CenterViewController {

  @FXML
  private Label label;

  @FXML
  public void initialize() {
    changesText("init");
  }

  public void changesText(String text) {
    label.setText(text);
  }
}
Java:
package controllers;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class ControllerInteraction extends Application {

  @Override
  public void start(Stage stage) throws Exception {
    Scene scene = new Scene(FXMLLoader.load(getClass().getResource("MainView.fxml")));

    stage.setScene(scene);
    stage.show();
  }

  public static void main(String[] args) {
    launch(args);
  }
}
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.MainViewController">
  <top>
    <fx:include fx:id="menuBar" source="MenuBar.fxml" />
  </top>
  <center>
    <fx:include fx:id="centerView" source="CenterView.fxml" />
  </center>
</BorderPane>
Java:
package controllers;

import javafx.fxml.FXML;

public class MainViewController {

  @FXML
  private CenterViewController centerViewController;
  @FXML
  private MenuBarController menuBarController;

  @FXML
  private void initialize() {
    menuBarController.setMainViewController(centerViewController);
  }

}
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<MenuBar xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.MenuBarController">
  <menus>
    <Menu mnemonicParsing="false" onAction="#handleClickOnMenu" text="Actions">
         <items>
            <MenuItem mnemonicParsing="false" onAction="#handleClickOnMenu" text="1" />
            <MenuItem mnemonicParsing="false" onAction="#handleClickOnMenu" text="2" />
            <MenuItem mnemonicParsing="false" onAction="#handleClickOnMenu" text="3" />
         </items></Menu>
  </menus>
</MenuBar>
Java:
package controllers;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.MenuItem;

public class MenuBarController {

  private CenterViewController mainView;

  @FXML
  private void handleClickOnMenu(ActionEvent e) {
    if (e.getSource() instanceof MenuItem) {
      MenuItem menuItem = (MenuItem) e.getSource();
      mainView.changesText(menuItem.getText());
    }
  }

  public void setMainViewController(CenterViewController mainView) {
    this.mainView = mainView;
  }
}
 
Vielen Dank für die schnelle Antwort! Das hört sich nach einer guten Idee an für meine Zwecke und ich habe mal versucht meinen Code dementsprechend umzubauen. Ich bekomme aber eine NullPointerException beim initialisieren im MainController:

java.lang.NullPointerException
at control.MainController.initialize(MainController.java:15)


Hast du vielleicht eine Idee woran das liegen könnte?

[Java]package control;

import javafx.fxml.FXML;

public class MainController {

@FXML
private TaskController taskController;
@FXML
private MenuController menuController;

@FXML
private void initialize() {
System.out.println("init");
menuController.setMainViewController(taskController);
}
}
[/Java]
 
Ich habe das Problem gefunden! Die Controller in der MainController-Klasse hatten den falschen Variablennamen. Wenn ich also den Controller "taskController" nenne muss die ID in der fxml-Datei "task" sein. Jetzt funktioniert erstmal alles wie ich es mir gedacht habe. Vielen Dank!
 
Hi

Ich möchte meinen Senf auch noch dazugeben: Bei kleineren Applikationen ist dein Weg sicher ausreichend, aber bei richtig grossen wüsste ich persönlich nicht mehr, ob ich das noch so machen wollen würde.
Dazu gab es vor Kurzem hier im Forum schon mal eine Diskussion und ich habe daraufhin etwas recherchert und bin in einem meiner Posts zu dem Schluss hier gekommen

ich hat gesagt.:
Der Interface-Weg klingt gut, wird aber schnell unübersichtlich, wenn du eine tiefere Struktur als die von dir beschriebene hast.
Um ehrlich zu sein, bin ich hier auch noch nicht 100%ig endgültig sicher, was der beste Weg ist, obwohl ich auch schon etwas komplexere Projekte (aber auch nicht zu extrem) damit gemacht hab. Bisher war mein Weg, einen zentralen Event-Dispatcher zu verwenden. Den könnte man entweder aus einer Factory erhalten oder via DI (Guice, etc. oder Konstruktor - bei letzeren müsste man aber am FXMLLoader eine ControllerFactory angeben) in den Controller bringen.

Auf StackOverflow gibt es dazu bereits so einige Diskussionen:
dependency injection - Passing Parameters JavaFX FXML - Stack Overflow

(Bitte auch den Links mal folgen, einige sind interessant)

Fazit daraus in Kürze:
- kleine Applikationen: Es genügt, die Parameter beim FXMLLoader zu übergeben
- grössere: DI und/oder EventBus (meine Variante scheint also gar nicht so doof gewesen zu sein )

Bei Interesse kann ich ja mal meinen Event-Dispatcher posten, auch wenn er streng genommen noch ein paar Schwachstellen hat (keine Asynchronität; Listener zwar Parametrisiert, aber keine Unter-Parameter möglich (hab mir schon so wie er ist mit den Generics einen Knoten inst Hirn gemacht))...

Der Rest des Threads: http://www.java-forum.org/awt-swing...0-controller-klasse-splitten.html#post1050293 (mein Post ist der Anker)

Grüsse
 

Neue Themen


Zurück
Oben