关闭另一个Controller中的选项卡 - JavaFX

时间:2018-03-16 15:30:43

标签: java model-view-controller javafx fxml

我正在用Java设计一个简单的电子邮件客户端。我使用两个FXML文件作为视图,当然还有2个Java控制器,每个都用于右视图。

我的目标是关闭WriteView选项卡,点击按钮"删除"在Writeview中,但我遇到了一些问题,因为我创建了新的选项卡,我在其中加载了MainVIewController中的WriteView,现在我没有任何id或类型的引用来关闭选项卡,除非" x"选项卡名称旁边的按钮。

以下是代码:

MainView.FXML

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.collections.*?>

<TabPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="ALL_TABS" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainViewController">
<tabs>
    <Tab text="Mailbox" closable="false">
        <content>
            <BorderPane prefHeight="200.0" prefWidth="200.0">
            <top>
                <ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
                    <items>
                    <Button fx:id="update" mnemonicParsing="false" onAction="#updateButton" text="Update" />
                        <Button fx:id="write" mnemonicParsing="false" onAction="#writeButton" text="Write" />
                        <Button fx:id="reply" mnemonicParsing="false" onAction="#replyButton" text="Reply" />
                        <Button fx:id="replyToAll" mnemonicParsing="false" onAction="#replyToAllButton" text="Reply to All" />
                        <Button fx:id="forward" mnemonicParsing="false" onAction="#forwardButton" text="Forward" />
                        <Button fx:id="delete" mnemonicParsing="false" onAction="#deleteButton" text="Delete" />
                    </items>
                </ToolBar>
            </top>
            <bottom>
                <HBox prefHeight="19.0" prefWidth="600.0" BorderPane.alignment="CENTER">
                    <children>
                        <Label text="Client Status:" />
                        <Label fx:id="status" text="" />
                    </children>
                </HBox>
            </bottom>
            <left>
                <Accordion BorderPane.alignment="CENTER">
                    <panes>
                    <TitledPane fx:id="mailboxName" animated="false" text="Inbox: ">
                        <content>
                            <VBox prefHeight="200.0" prefWidth="100.0">
                                <children>
                                    <Label fx:id="inbox" text="Inbox" />
                                    <Label fx:id="drafts" text="Drafts" />
                                    <Label fx:id="bin" text="Bin" />
                                </children>
                            </VBox>
                        </content>
                        </TitledPane>
                    </panes>
                </Accordion>
            </left>
            <center>
                <TableView fx:id="table" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">

                    <columns>
                        <TableColumn prefWidth="247.0" text="Subject">
                            <cellValueFactory><PropertyValueFactory property="subject" /></cellValueFactory>
                        </TableColumn>

                        <TableColumn prefWidth="131.0" text="From">
                            <cellValueFactory><PropertyValueFactory property="receiver" /></cellValueFactory>
                        </TableColumn>

                        <TableColumn minWidth="3.0" prefWidth="118.0" text="Date">
                            <cellValueFactory><PropertyValueFactory property="date" /></cellValueFactory>
                        </TableColumn>
                    </columns>
                    <items>
                        <FXCollections fx:factory="observableArrayList">
                        </FXCollections>
                    </items>
                </TableView>
            </center>
            </BorderPane>
        </content>
    </Tab>
</tabs>
</TabPane>

MainViewController.java:

package controller;

import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import model.Account;
import model.Email;
import java.io.IOException;
import java.net.URL;
import java.util.Observable;
import java.util.Observer;
import java.util.ResourceBundle;

public class MainViewController implements Initializable, Observer {

    // utility https://stackoverflow.com/questions/40557492/mvc-with-javafx-and-fxml
    // we will follow method 1

    public MainViewController() {}

    @FXML // MainView components
    public TabPane root;
    public Button update;
    public Button write;
    public Button reply;
    public Button replyToAll;
    public Button forward;
    public Button delete;
    public Label status;
    public TitledPane mailboxName;
    public Label inbox;
    public Label drafts;
    public Label bin;
    public TableView<Email> table;


    // BUTTONS ---------------------------------------------------------------------------------------------------------

    /**
    * On click on Update button do something
    */
    @FXML
    public void updateButton() {
        update.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
                status.setText(mess);
            }
        });
    }

    /**
    * On click on Write button do something
    */
    @FXML
    public void writeButton() {
        write.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
                onWriteClick();
            }
        });
    }

    /**
    * On click on Reply button do something
    */
    @FXML
    public void replyButton() {
        reply.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
            }
        });
    }

    /**
    * On click on Reply To All button do something
    */
    @FXML
    public void replyToAllButton() {
        replyToAll.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
            }
        });
    }

    /**
    * On click on Forward button do something
    */
    @FXML
    public void forwardButton() {
        forward.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
            }
        });
    }

    /**
    * On click on Delete label do something
    */
    @FXML
    public void deleteButton() {
        delete.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                String mess = "You clicked: " + e.getSource() + "!";
                System.out.println(mess);
            }
        });
    }

    /**
    * Sets the name of the account in the MainView
    */
    @FXML
    public void setMailboxName() {

    }

    // TODO: implementare lo stato nelle email, perche così possiamo discriminare se sono inbox, bin, drafts

    /**
    * On click on Inbox label do something
    */
    @FXML
    public void inboxLabel() {

    }

    /**
    * On click on Drafts label do something
    */
    @FXML
    public void draftsLabel() {

    }

    /**
    * On click on Bin label do something
    */
    @FXML
    public void binLabel() {

    }

    // INITIALIZING ----------------------------------------------------------------------------------------------------

    /**
    * It initialize all the event of the buttons
    */
    private void initializeButtons(){
        updateButton();
        writeButton();
        replyButton();
        replyToAllButton();
        forwardButton();
        deleteButton();
    }

    /**
    * It calls all the methods that initialize a category of components
    */
    private void initializeAll() {
        initializeButtons();
        loadEmails();
    }

    // IMPLEMENTATIONS -------------------------------------------------------------------------------------------------

    /**
    * It initialize all the necessary for the GUI
    * @param location: The location used to resolve relative paths for the root object, or null if the location is not known.
    * @param resources: The resources used to localize the root object, or null if the root object was not localized.
    */
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initializeAll();
        System.out.println("GUI Loaded"); // DEBUG
    }

    /**
    * Implementation of update method in Observer interface
    * @param o:  the observable object.
    * @param arg: (optional) an argument passed to the notifyObservers method.
    */
    @Override
    public void update(Observable o, Object arg) {

    }

    // ON CLICK --------------------------------------------------------------------------------------------------------

    /**
    * It opens a new Tab with WriteView loaded. It is used to write a new email.
    */
    private void onWriteClick(){
        try{
            Tab tab = new Tab("Write");
            tab.setContent(FXMLLoader.load(getClass().getResource("/view/WriteView.fxml"))); // load the GUI for the Write tab
            tab.setId("writeroot");
            root.getTabs().add(tab); // Add the new tab beside the "Inbox" tab
            root.getSelectionModel().select(tab); // Switch to Write tab
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // POPULATING MAIN VIEW --------------------------------------------------------------------------------------------

    /**
    * It populates the main view with all the email with a specific status
    */
    private void loadEmails() {

    }

} // end class

WriteView.fxml:

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

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

<VBox fx:id="root" 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="controller.WriteViewController">
<children>
    <GridPane fx:id="table">
        <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" maxWidth="296.0" minWidth="10.0" prefWidth="57.0" />
        <ColumnConstraints hgrow="SOMETIMES" maxWidth="543.0" minWidth="10.0" prefWidth="543.0" />
        </columnConstraints>
        <rowConstraints>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
        <children>
            <Label text="To:" />
            <Label text="From:" GridPane.rowIndex="1" />
            <Label text="Subject:" GridPane.rowIndex="2" />
            <TextField fx:id="to" GridPane.columnIndex="1" />
            <TextField fx:id="from" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <TextField fx:id="subject" GridPane.columnIndex="1" GridPane.rowIndex="2" />
        </children>
    </GridPane>
    <Pane prefHeight="272.0" prefWidth="600.0">
        <children>
            <TextArea fx:id="text" prefHeight="277.0" prefWidth="600.0" />
        </children>
    </Pane>
    <ToolBar fx:id="toolbar" nodeOrientation="RIGHT_TO_LEFT" prefHeight="40.0" prefWidth="200.0">
        <items>
            <Button fx:id="send" mnemonicParsing="false" onAction="#sendButton" text="Send" />
            <Button fx:id="saveAsDraft" mnemonicParsing="false" onAction="#saveButton" text="Save as draft" />
        <Button fx:id="delete" mnemonicParsing="false" onAction="#deleteButton" text="Delete" />
        </items>
    </ToolBar>
</children>
</VBox>

WriteViewController.java:

package controller;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import model.Account;
import model.Email;

import java.net.URL;
import java.util.Observable;
import java.util.Observer;
import java.util.ResourceBundle;

public class WriteViewController implements Initializable, Observer{

    public WriteViewController() {

    }

    @FXML
    public VBox root;
    public GridPane table;
    public TextField to;
    public TextField from;
    public TextField subject;
    public TextArea text;
    public ToolBar toolbar;
    public Button send;
    public Button saveAsDraft;
    public Button delete;

    // BUTTONS ---------------------------------------------------------------------------------------------------------

    /**
    * On click on Update button do something
    */
    @FXML
    public void sendButton() {
        send.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Account reciver = new Account(to.getText());
                Account sender = new Account(from.getText());

                Email toSend = new Email(sender, reciver, subject.getText(), text.getText());

                toSend.writeEmail(reciver, subject.getText(), text.getText());

                // Is not necessary to set the state, because, when a new Email is created, it has already the
                // state of new (2). For further information, see Email constructor.
            }
        });
    }

    @FXML
    public void saveButton() {
        saveAsDraft.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Account reciver = new Account(to.getText());
                Account sender = new Account(from.getText());

                Email toSend = new Email(sender, reciver, subject.getText(), text.getText());

                toSend.setState(0); // the email is a draft
            }
        });
    }

    @FXML
    public void deleteButton() {
        delete.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Account reciver = new Account(to.getText());
                Account sender = new Account(from.getText());

                Email toSend = new Email(sender, reciver, subject.getText(), text.getText());

                // here is the problem, Goal: close this tab!

                System.out.println(root.getParent()); // DEBUG
            }
        });
    }

    // INITIALIZING ----------------------------------------------------------------------------------------------------

    /**
    * It initialize all the event of the buttons
    */
    private void initializeButtons(){
        sendButton();
        saveButton();
        deleteButton();
    }

    /**
    * It call all the methods that initialize a category of components
    */
    private void initializeAll() {
        initializeButtons();

    }

    // IMPLEMENTATIONS -------------------------------------------------------------------------------------------------

    /**
    * It initialize all the necessary for the GUI
    * @param location: The location used to resolve relative paths for the root object, or null if the location is not known.
    * @param resources: The resources used to localize the root object, or null if the root object was not localized.
    */
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initializeAll();
    }

    /**
    * Implementation of update method in Observer interface
    * @param o:  the observable object.
    * @param arg: (optional) an argument passed to the notifyObservers method.
    */
    @Override
    public void update(Observable o, Object arg) {

    }
}

Client.java:

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

import java.io.IOException;

/**
*
* @author lorenzotabasso
*/

public class Client extends Application{

    @Override
    public void start(Stage primaryStage) throws IOException{
        Parent root = FXMLLoader.load(getClass().getResource("view/MainView.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {
        launch(args);
    }

}

1 个答案:

答案 0 :(得分:1)

您可以将事件处理程序传递给单击删除按钮时触发的控制器:

private Runnable tabCloseHandler;

public void setTabCloseHandler(Runnable handler) {
    tabCloseHandler = handler;
}

@FXML
public void deleteButton(ActionEvent e) {
    // you don't set the handler here; this method is invoked as handler
    String mess = "You clicked: " + e.getSource() + "!";
    System.out.println(mess);

    if (handler != null) {
        handler.run();
    }
}
final Tab tab = new Tab("Write");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/WriteView.fxml"));
tab.setContent(loader.load());
loader.<WriteViewController>getController().setTabCloseHandler(() -> root.getTabs().remove(tab));

或者,您可以迭代到场景的根目录,并在找到TabPane后立即关闭显示的标签

public static TabPane findEnclosingTabPane(Node n) {
    while (n != null && !(n instanceof TabPane)) {
        n = n.getParent();
    }
    return (TabPane) n;
}
TabPane tabPane = findEnclosingTabPane(root);
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());