ผ่านพารามิเตอร์ JavaFX FXML


194

ฉันจะส่งพารามิเตอร์ไปที่หน้าต่างรองใน javafx ได้อย่างไร มีวิธีสื่อสารกับคอนโทรลเลอร์ที่เกี่ยวข้องหรือไม่?

ตัวอย่างเช่น: ผู้ใช้เลือกลูกค้าจาก a TableViewและหน้าต่างใหม่เปิดขึ้นโดยแสดงข้อมูลลูกค้า

Stage newStage = new Stage();
try 
{
    AnchorPane page = (AnchorPane) FXMLLoader.load(HectorGestion.class.getResource(fxmlResource));
    Scene scene = new Scene(page);
    newStage.setScene(scene);
    newStage.setTitle(windowTitle);
    newStage.setResizable(isResizable);
    if(showRightAway) 
    {
        newStage.show();
    }
}

newStageจะเป็นหน้าต่างใหม่ ปัญหาคือฉันไม่สามารถหาวิธีบอกคอนโทรลเลอร์ได้ว่าจะหาข้อมูลของลูกค้าได้ที่ไหน (โดยการส่ง id เป็นพารามิเตอร์)

ความคิดใด ๆ


ตรวจสอบว่ามันใช้งานได้ดีหรือไม่: stackoverflow.com/questions/14370183/…
Dynelight

@Alvaro: คุณได้รับทางออกของคุณ? คุณสามารถส่งพารามิเตอร์ได้หรือไม่ จากคอนโทรลเลอร์หนึ่งไปยังไฟล์คอนโทรลเลอร์อื่น?
Java Man

3
ใช่. jewelsea ให้คำอธิบายระดับหนังสือ นั่นเป็นเหตุผลที่ฉันยอมรับคำตอบของเขา
Alvaro

คำตอบ:


276

แนวทางที่แนะนำ

คำตอบนี้แจกแจงกลไกต่าง ๆ สำหรับการส่งพารามิเตอร์ไปยังตัวควบคุม FXML

สำหรับแอปพลิเคชันขนาดเล็กฉันขอแนะนำให้ส่งพารามิเตอร์โดยตรงจากผู้เรียกไปยังคอนโทรลเลอร์ - มันเป็นเรื่องง่ายตรงไปตรงมาและไม่จำเป็นต้องใช้เฟรมเวิร์กเพิ่มเติม

สำหรับแอพพลิเคชั่นที่มีขนาดใหญ่และซับซ้อนมากขึ้นมันจะคุ้มค่าที่จะตรวจสอบว่าคุณต้องการใช้การฉีดอ้างอิงหรือEvent Busหรือไม่กลไกการภายในแอปพลิเคชันของคุณหรือไม่

การส่งผ่านพารามิเตอร์โดยตรงจากผู้เรียกไปยังคอนโทรลเลอร์

ส่งผ่านข้อมูลที่กำหนดเองไปยังตัวควบคุม FXML โดยดึงตัวควบคุมจากอินสแตนซ์ของตัวโหลด FXML และเรียกวิธีการบนตัวควบคุมเพื่อเริ่มต้นมันด้วยค่าข้อมูลที่จำเป็น

บางอย่างเช่นรหัสต่อไปนี้:

public Stage showCustomerDialog(Customer customer) {
  FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
      "customerDialog.fxml"
    )
  );

  Stage stage = new Stage(StageStyle.DECORATED);
  stage.setScene(
    new Scene(
      (Pane) loader.load()
    )
  );

  CustomerDialogController controller = 
    loader.<CustomerDialogController>getController();
  controller.initData(customer);

  stage.show();

  return stage;
}

...

class CustomerDialogController {
  @FXML private Label customerName;
  void initialize() {}
  void initData(Customer customer) {
    customerName.setText(customer.getName());
  }
}

FXMLLoader new FXMLLoader(location)ใหม่จะถูกสร้างขึ้นตามที่แสดงในรหัสตัวอย่างเช่น ที่ตั้งเป็น URL และคุณสามารถสร้าง URL ดังกล่าวจากทรัพยากร FXML โดย:

new FXMLLoader(getClass().getResource("sample.fxml"));

ระวังอย่าใช้ฟังก์ชั่นโหลดแบบคงที่บน FXMLLoader ไม่เช่นนั้นคุณจะไม่สามารถรับตัวควบคุมจากอินสแตนซ์โหลดเดอร์ของคุณได้

อินสแตนซ์ FXMLLoader ไม่เคยรู้อะไรเกี่ยวกับวัตถุโดเมน คุณไม่ส่งวัตถุโดเมนเฉพาะแอปพลิเคชันไปยังตัวสร้าง FXMLLoader โดยตรงแทนคุณ:

  1. สร้าง FXMLLoader ตามมาร์กอัป fxml ณ ตำแหน่งที่ระบุ
  2. รับตัวควบคุมจากอินสแตนซ์ FXMLLoader
  3. เรียกใช้เมธอดบนตัวควบคุมที่ดึงมาเพื่อให้การอ้างอิงกับตัวควบคุมโดเมนวัตถุ

บล็อกนี้ (โดยนักเขียนอื่น) ให้เป็นทางเลือก แต่ที่คล้ายกันตัวอย่างเช่นตัวอย่างเช่น

การตั้งค่าคอนโทรลเลอร์บน FXMLLoader

CustomerDialogController dialogController = 
    new CustomerDialogController(param1, param2);

FXMLLoader loader = new FXMLLoader(
    getClass().getResource(
        "customerDialog.fxml"
    )
);
loader.setController(dialogController);

Pane mainPane = (Pane) loader.load();

คุณสามารถสร้างคอนโทรลเลอร์ใหม่ในรหัสผ่านพารามิเตอร์ใด ๆ ที่คุณต้องการจากผู้โทรเข้าสู่คอนสตรัคเตอร์คอนโทรลเลอร์ เมื่อคุณสร้างคอนโทรลเลอร์แล้วคุณสามารถตั้งค่าบนอินสแตนซ์ FXMLLoader ก่อนที่คุณจะเรียกใช้load() อินสแตนซ์เมธอด

ในการตั้งค่าคอนโทรลเลอร์บนโหลดเดอร์ (ใน JavaFX 2.x) คุณไม่สามารถกำหนด a fx:controllerแอตทริบิวต์ในไฟล์ fxml ของคุณได้

เนื่องจากข้อ จำกัด ในการ fx:controllerคำจำกัดความใน FXML ฉันชอบรับคอนโทรลเลอร์จาก FXMLLoader เป็นการส่วนตัวมากกว่าตั้งค่าคอนโทรลเลอร์ลงใน FXMLLoader

การมีตัวควบคุมดึงพารามิเตอร์จากวิธีการคงที่ภายนอก

วิธีนี้เป็นแบบสุดขั้วโดยคำตอบของ Sergey สำหรับJavafx 2.0 How-to Application.getParameters () ในไฟล์ Controller.javaในไฟล์

ใช้การฉีดพึ่งพา

FXMLLoader รองรับระบบฉีดพึ่งพาเช่น Guice, Spring หรือ Java EE CDI โดยอนุญาตให้คุณตั้งโรงงานควบคุมแบบกำหนดเองบน FXMLLoader สิ่งนี้จัดเตรียมการติดต่อกลับที่คุณสามารถใช้เพื่อสร้างอินสแตนซ์ของตัวควบคุมที่มีค่าที่ต้องพึ่งพาซึ่งถูกฉีดโดยระบบฉีดที่เกี่ยวข้อง

ตัวอย่างของแอปพลิเคชัน JavaFX และตัวควบคุมการพึ่งพาการฉีดกับสปริงมีให้ในคำตอบ:

วิธีการฉีดการพึ่งพาที่ดีและสะอาดนั้นเป็นแบบสุดขั้วโดยafterburner.fxด้วยตัวอย่างแอปพลิเคชัน air-hacksที่ใช้มัน afterburner.fx อาศัย JEE6 javax.injectเพื่อดำเนินการฉีดต่อเนื่อง

ใช้ Event Bus

Greg Brown ผู้สร้างข้อมูลจำเพาะ FXML ดั้งเดิมและผู้ดำเนินการมักแนะนำให้พิจารณาการใช้ Event Bus เช่น Guava EventBusเพื่อการสื่อสารระหว่าง FXML คอนโทรลเลอร์ที่สร้างอินสแตนซ์และแอปพลิเคชันอื่น ๆ

EventBus เป็น API การเผยแพร่ / สมัครสมาชิกที่เรียบง่าย แต่ทรงพลังพร้อมคำอธิบายประกอบที่ช่วยให้ POJO สื่อสารกับกันและกันได้ทุกที่ใน JVM โดยไม่ต้องอ้างถึงกันและกัน

ติดตามคำถาม & คำตอบ

ในวิธีแรกทำไมคุณถึงกลับสเตจ วิธีการอาจเป็นโมฆะเช่นกันเพราะคุณให้คำสั่งแสดง (); ก่อนกลับเวที; คุณวางแผนการใช้งานอย่างไรโดยส่งคืนสเตจ

มันเป็นวิธีการแก้ไขปัญหาการทำงาน สเตจจะถูกส่งคืนจากshowCustomerDialogฟังก์ชันเพื่อให้สามารถอ้างอิงการอ้างอิงกับคลาสภายนอกซึ่งอาจต้องการทำบางสิ่งเช่นซ่อนสเตจตามการคลิกปุ่มในหน้าต่างหลักในภายหลัง โซลูชันสำรองที่มุ่งเน้นวัตถุสามารถห่อหุ้มฟังก์ชันการทำงานและการอ้างอิงสเตจภายในวัตถุ CustomerDialog หรือให้ CustomerDialog ขยายสเตจได้ ตัวอย่างเต็มรูปแบบสำหรับอินเทอร์เฟซแบบเชิงวัตถุไปยังกล่องโต้ตอบแบบกำหนดเองที่ห่อหุ้ม FXML ข้อมูลคอนโทรลเลอร์และโมเดลอยู่นอกเหนือขอบเขตของคำตอบนี้ แต่อาจสร้างโพสต์บล็อกที่คุ้มค่าสำหรับใครก็ได้ที่ต้องการสร้าง


ข้อมูลเพิ่มเติมที่จัดทำโดยผู้ใช้ StackOverflow ชื่อ@dzim

ตัวอย่างการฉีดสปริงบูตพึ่งพา

คำถามที่ว่าจะทำอย่างไร "The Spring Boot Way" มีการสนทนาเกี่ยวกับ JavaFX 2 ซึ่งฉันได้รับการบอกกล่าวในลิงก์ถาวร วิธีการดังกล่าวยังคงใช้ได้และทดสอบในเดือนมีนาคม 2559 ใน Spring Boot v1.3.3 การปล่อย: https://stackoverflow.com/a/36310391/1281217


บางครั้งคุณอาจต้องการส่งผลลัพธ์กลับไปยังผู้โทรซึ่งในกรณีนี้คุณสามารถตรวจสอบคำตอบสำหรับคำถามที่เกี่ยวข้อง:


ตัวสร้าง FXMLLoader ใช้ URL เป็นพารามิเตอร์เท่านั้น .. วิธีที่ถูกต้องในการสร้างอินสแตนซ์ FXMLLoader คืออะไร
Alvaro

1
เว็บไซต์รถบัสเหตุการณ์พูดถึงอเมริกา "อัปเดต 3/2013: The EventBus ค้างแล้ว ... "
j จะ

1
DataFX Controller Frameworks ให้การสนับสนุนการฉีดสำหรับคอนโทรลเลอร์FXML
Hendrik Ebbers

2
เพิ่มหัวข้อคำถามเพิ่มเติมเพื่อตอบคำถามเพิ่มเติมของ @Anarkie
jewelsea

7
สำหรับ godshake มีอะไรง่าย ๆ สำหรับการทำงานเล็ก ๆ นี้ใน JavaFx? มันเป็นคุณสมบัติที่พบบ่อยมากในการส่งผ่านข้อมูลในตัวสร้างและ javafx ต้องการสิ่งที่ทุกอย่างร่วมกันเพียงแค่ส่งชื่อหรือค่าเดียว?
Zahan Safallwa

13

ฉันรู้ว่านี่เป็นโพสต์เก่ามากและมีคำตอบที่ดีอยู่แล้ว แต่ฉันต้องการสร้าง MCVE อย่างง่าย ๆ เพื่อสาธิตวิธีการดังกล่าวและอนุญาตให้ผู้เขียนโค้ดใหม่สามารถเห็นแนวคิดในการทำงาน

ในตัวอย่างนี้เราจะใช้ 5 ไฟล์:

  1. Main.java - ใช้เพื่อเริ่มแอปพลิเคชั่นและเรียกคอนโทรลเลอร์ตัวแรก
  2. Controller1.java - คอนโทรลเลอร์สำหรับโครงร่าง FXML แรก
  3. Controller2.java - คอนโทรลเลอร์สำหรับโครงร่าง FXML ที่สอง
  4. Layout1.fxml - เค้าโครง FXML สำหรับฉากแรก
  5. Layout2.fxml - เค้าโครง FXML สำหรับฉากที่สอง

ไฟล์ทั้งหมดมีการระบุไว้อย่างครบถ้วนที่ด้านล่างของโพสต์นี้

เป้าหมาย:เพื่อแสดงให้เห็นถึงการส่งผ่านค่าจากController1ไปController2และกลับกัน

การไหลของโปรแกรม:

  • ฉากแรกที่มีความTextFieldเป็นและButton Labelเมื่อมีการคลิกหน้าต่างที่สองมีการโหลดและแสดงรวมถึงข้อความที่ป้อนในButtonTextField
  • ภายในฉากที่สองยังมีความTextFieldเป็นและButton จะแสดงข้อความที่ป้อนในในฉากแรกLabelLabelTextField
  • เมื่อป้อนข้อความในฉากที่สองTextFieldและคลิกButtonที่ฉากแรกLabelจะถูกอัปเดตเพื่อแสดงข้อความที่ป้อน

นี่เป็นการสาธิตที่ง่ายมากและอาจยืนหยัดเพื่อการปรับปรุง แต่ควรทำให้แนวคิดชัดเจนยิ่งขึ้น

โค้ดเองก็ให้ความเห็นพร้อมรายละเอียดบางอย่างเกี่ยวกับสิ่งที่เกิดขึ้นและวิธีการ

รหัส

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>

1
เป็นไปได้ไหมที่จะตั้งค่าคอนโทรลเลอร์ในไฟล์ FXML? จงเอาสายออก: loader.setController(this)และเพิ่มคอนโทรลเลอร์ในไฟล์ FXML ขัดข้องแอปพลิเคชัน
Halfacht

1
ไม่ใช่ถ้าโหลด FXML จากภายในตัวควบคุมเอง หากคุณโหลด FXML จากคลาส Main คุณสามารถกำหนดคอนโทรลเลอร์ในไฟล์ FXML และรับการอ้างอิงโดยใช้loader.getController()
Zephyr

ในที่สุดฉันก็หาวิธีแก้ไขได้เป็นตัวอย่างที่ดี ฉันนำไปใช้กับโครงการของฉันและตอนนี้ฉันกำลังพยายามเปิดหน้าต่างทั้งสองพร้อมกันและทำให้เป็นครั้งแรก น่าเสียดายที่มีเพียงแห่งเดียวเท่านั้นที่เปิด ใครช่วยได้บ้าง
jabba

8

คลาส javafx.scene.Node มีคู่ของเมธอด setUserData (Object) และ Object getUserData ()

ซึ่งคุณสามารถใช้เพื่อเพิ่มข้อมูลของคุณไปยังโหนด

ดังนั้นคุณสามารถเรียก page.setUserData (ข้อมูล);

และผู้ควบคุมสามารถตรวจสอบได้หากมีการตั้งค่าข้อมูล นอกจากนี้คุณสามารถใช้ ObjectProperty สำหรับการถ่ายโอนข้อมูลย้อนกลับได้ถ้าจำเป็น

ดูเอกสารที่นี่: http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html ก่อนวลี "ในเวอร์ชันแรก handleButtonAction () จะถูกแท็กด้วย @FXML เพื่อให้มาร์กอัพที่กำหนดไว้ในเอกสารของคอนโทรลเลอร์สามารถเรียกใช้งานได้ในตัวอย่างที่สองฟิลด์คำอธิบายประกอบเพื่อให้ตัวโหลดเดอร์สามารถตั้งค่าได้เมธอด initialize () จะมีคำอธิบายประกอบในทำนองเดียวกัน "

ดังนั้นคุณต้องเชื่อมโยงคอนโทรลเลอร์กับโหนดและตั้งค่าข้อมูลผู้ใช้กับโหนด


Stage.getScene () -> Scene.getRoot () -> การค้นหาแบบเรียกซ้ำด้วย Parent.getChildrenUnmodifiable () นี่เป็นวิธีที่สกปรกมาก หากใครบางคนสามารถแนะนำ smth ได้ดีขึ้น - มันจะยอดเยี่ยม
Alexander Kirov

ดูเหมือนว่า Stage.getScene (). getRoot () เป็นวิธีที่ถูกต้อง! ขอบคุณ
Alvaro

7

นี่คือตัวอย่างสำหรับการส่งพารามิเตอร์ไปยังเอกสาร fxml ผ่าน namespace

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
    <BorderPane>
        <center>
            <Label text="$labelText"/>
        </center>
    </BorderPane>
</VBox>

กำหนดค่าExternal TextสำหรับตัวแปรเนมสเปซlabelText:

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

import java.io.IOException;

public class NamespaceParameterExampleApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("namespace-parameter-example.fxml"));

        fxmlLoader.getNamespace()
                  .put("labelText", "External Text");

        final Parent root = fxmlLoader.load();

        primaryStage.setTitle("Namespace Parameter Example");
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }
}

มันควรจะตั้งข้อสังเกตว่าปุ่มบางอย่างจะใช้ภายใน: เช่นFXMLLoader.CONTROLLER_KEYWORD, FXMLLoader.LOCATION_KEY, FXMLLoader.RESOURCES_KEYและสตริงใด ๆ ใช้เป็นค่าสำหรับfx:idแอตทริบิวต์
fabian

ขอบคุณสำหรับเรื่องนี้ฉากอื่น ๆ ของฉันเป็นเพียงภาชนะที่แสดงข้อความที่แสดงก่อนหน้านี้ในฉากหลักของฉัน ตอนนี้ฉันสามารถมีหนึ่ง fxml ที่ฉันสามารถนำมาใช้ในหลายสถานที่โดยเริ่มต้นเนื้อหาผ่านตัวแปรเนมสเปซ ฉันไม่จำเป็นต้องสร้างวิธีการใหม่หรือปรับเปลี่ยนคอนสตรัคเตอร์หรือผู้เริ่มต้น - เพียงแค่เพิ่มตัวแปรใน FXML ของฉันและเพิ่มหนึ่งบรรทัดในรหัส fxmloader ของฉันในตัวควบคุมหลัก
SystemsInCode

4

งานนี้ ..

จำไว้ว่าครั้งแรกที่คุณพิมพ์ค่าการส่งผ่านที่คุณจะได้รับเป็นโมฆะคุณสามารถใช้มันได้หลังจากที่โหลดหน้าต่างแล้วเช่นเดียวกับทุกสิ่งที่คุณต้องการโค้ดสำหรับส่วนประกอบอื่น ๆ

ตัวควบคุมแรก

try {
    Stage st = new Stage();
    FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/inty360/free/form/MainOnline.fxml"));

    Parent sceneMain = loader.load();

    MainOnlineController controller = loader.<MainOnlineController>getController();
    controller.initVariable(99L);

    Scene scene = new Scene(sceneMain);
    st.setScene(scene);
    st.setMaximized(true);
    st.setTitle("My App");
    st.show();
} catch (IOException ex) {
    Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
}

ตัวควบคุมอื่น

public void initVariable(Long id_usuario){
    this.id_usuario = id_usuario;
    label_usuario_nombre.setText(id_usuario.toString());
}

1
สิ่งนี้จะทำงานเมื่อคุณส่งพารามิเตอร์จากตัวควบคุมแรกไปที่สอง แต่วิธีการส่งผ่านพารามิเตอร์จากตัวควบคุมที่หนึ่งถึงที่หนึ่งฉันหมายถึงหลังจากโหลด First.fxml แล้ว
Menai Ala Eddine - Aladdin

@XlintXms ดูคำถามที่เกี่ยวข้องJavaFX FXML Parameter ที่ส่งผ่านจากคอนโทรลเลอร์ A ถึง B และย้อนกลับซึ่งตอบคำถามเพิ่มเติมของคุณ
jewelsea

2

คุณต้องสร้างคลาสบริบทหนึ่งคลาส

public class Context {
    private final static Context instance = new Context();
    public static Context getInstance() {
        return instance;
    }

    private Connection con;
    public void setConnection(Connection con)
    {
        this.con=con;
    }
    public Connection getConnection() {
        return con;
    }

    private TabRoughController tabRough;
    public void setTabRough(TabRoughController tabRough) {
        this.tabRough=tabRough;
    }

    public TabRoughController getTabRough() {
        return tabRough;
    }
}

คุณต้องตั้งค่าอินสแตนซ์ของคอนโทรลเลอร์ในการเริ่มต้นใช้งาน

Context.getInstance().setTabRough(this);

และคุณสามารถใช้มันได้จากแอปพลิเคชันทั้งหมดของคุณเพียงแค่ใช้

TabRoughController cont=Context.getInstance().getTabRough();

ตอนนี้คุณสามารถส่งพารามิเตอร์ไปยังตัวควบคุมใด ๆ จากแอปพลิเคชันทั้งหมด


เราใช้วิธีนี้และใช้งานได้ดี ฉันชอบที่ฉันสามารถเข้าถึงข้อมูลภายในตัวสร้างหรือในวิธีการเริ่มต้นและฉันไม่จำเป็นต้องตั้งค่าข้อมูลในตัวควบคุมหลังจากที่มันถูกสร้างขึ้น
Bob

1

ใช่คุณสามารถ.
คุณต้องเพิ่มในตัวควบคุมแรก:

YourController controller = loader.getController();     
controller.setclient(client);

จากนั้นในอันที่สองประกาศไคลเอ็นต์จากนั้นที่ด้านล่างของคอนโทรลเลอร์ของคุณ:

public void setclien(Client c) {
    this.client = c;
}

0

นี่คือตัวอย่างสำหรับการใช้ตัวควบคุมที่ถูกควบคุมโดย Guice

/**
 * Loads a FXML file and injects its controller from the given Guice {@code Provider}
 */
public abstract class GuiceFxmlLoader {

   public GuiceFxmlLoader(Stage stage, Provider<?> provider) {
      mStage = Objects.requireNonNull(stage);
      mProvider = Objects.requireNonNull(provider);
   }

   /**
    * @return the FXML file name
    */
   public abstract String getFileName();

   /**
    * Load FXML, set its controller with given {@code Provider}, and add it to {@code Stage}.
    */
   public void loadView() {
      try {
         FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(getFileName()));
         loader.setControllerFactory(p -> mProvider.get());
         Node view = loader.load();
         setViewInStage(view);
      }
      catch (IOException ex) {
         LOGGER.error("Failed to load FXML: " + getFileName(), ex);
      }
   }

   private void setViewInStage(Node view) {
      BorderPane pane = (BorderPane)mStage.getScene().getRoot();
      pane.setCenter(view);
   }

   private static final Logger LOGGER = Logger.getLogger(GuiceFxmlLoader.class);

   private final Stage mStage;
   private final Provider<?> mProvider;
}

นี่คือการดำเนินการที่เป็นรูปธรรมของโหลดเดอร์:

public class ConcreteViewLoader extends GuiceFxmlLoader {

   @Inject
   public ConcreteViewLoader(Stage stage, Provider<MyController> provider) {
      super(stage, provider);
   }

   @Override
   public String getFileName() {
      return "my_view.fxml";
   }
}

หมายเหตุตัวอย่างนี้โหลดมุมมองไปยังกึ่งกลางของ BoarderPane ซึ่งเป็นรากของฉากในเวที นี่ไม่เกี่ยวข้องกับตัวอย่าง (รายละเอียดการใช้งานของเคสการใช้งานเฉพาะของฉัน) แต่ตัดสินใจทิ้งไว้เพราะบางคนอาจพบว่ามีประโยชน์


-1

คุณสามารถตัดสินใจที่จะใช้รายการสาธารณะที่สังเกตได้เพื่อเก็บข้อมูลสาธารณะหรือเพียงแค่สร้างวิธี setter สาธารณะเพื่อเก็บข้อมูลและดึงข้อมูลจากตัวควบคุมที่เกี่ยวข้อง


-3

ทำไมต้องตอบคำถามอายุ 6 ปี
หนึ่งในแนวคิดพื้นฐานที่สุดที่ทำงานกับภาษาการเขียนโปรแกรมใด ๆ คือวิธีการนำทางจากหน้าต่างหนึ่งไปยังอีกแบบหนึ่ง ในขณะที่ทำการนำทางนี้นักพัฒนามักต้องการส่งข้อมูลจากหนึ่ง (หน้าต่างแบบฟอร์มหรือหน้า) และแสดงหรือใช้ข้อมูลที่ส่งผ่าน
ในขณะที่คำตอบส่วนใหญ่ให้ตัวอย่างที่ดีกับวิธีที่ดีที่เราคิดว่าเราจะทำให้สำเร็จ หรือสองหรือสาม
เราบอกว่าสามเพราะเราจะนำทางระหว่างสาม (หน้าต่างแบบฟอร์มหรือหน้า) และใช้แนวคิดของตัวแปรคงที่เพื่อส่งผ่านข้อมูลรอบ ๆ (หน้าต่างรูปแบบหรือหน้า)
เราจะรวมถึงการตัดสินใจบางรหัสในขณะที่ เรานำทาง

public class Start extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        // This is MAIN Class which runs first
        Parent root = FXMLLoader.load(getClass().getResource("start.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setResizable(false);// This sets the value for all stages
        stage.setTitle("Start Page"); 
        stage.show();
        stage.sizeToScene();
    }

    public static void main(String[] args) {
        launch(args);
    } 
}

เริ่มตัวควบคุม

public class startController implements Initializable {

@FXML Pane startPane,pageonePane;
@FXML Button btnPageOne;
@FXML TextField txtStartValue;
public Stage stage;
public static int intSETonStartController;
String strSETonStartController;

@FXML
private void toPageOne() throws IOException{

    strSETonStartController = txtStartValue.getText().trim();


        // yourString != null && yourString.trim().length() > 0
        // int L = testText.length();
        // if(L == 0){
        // System.out.println("LENGTH IS "+L);
        // return;
        // }
        /* if (testText.matches("[1-2]") && !testText.matches("^\\s*$")) 
           Second Match is regex for White Space NOT TESTED !
        */

        String testText = txtStartValue.getText().trim();
        // NOTICE IF YOU REMOVE THE * CHARACTER FROM "[1-2]*"
        // NO NEED TO CHECK LENGTH it also permited 12 or 11 as valid entry 
        // =================================================================
        if (testText.matches("[1-2]")) {
            intSETonStartController = Integer.parseInt(strSETonStartController);
        }else{
            txtStartValue.setText("Enter 1 OR 2");
            return;
        }

        System.out.println("You Entered = "+intSETonStartController);
        stage = (Stage)startPane.getScene().getWindow();// pane you are ON
        pageonePane = FXMLLoader.load(getClass().getResource("pageone.fxml"));// pane you are GOING TO
        Scene scene = new Scene(pageonePane);// pane you are GOING TO
        stage.setScene(scene);
        stage.setTitle("Page One"); 
        stage.show();
        stage.sizeToScene();
        stage.centerOnScreen();  
}

private void doGET(){
    // Why this testing ?
    // strSENTbackFROMPageoneController is null because it is set on Pageone
    // =====================================================================
    txtStartValue.setText(strSENTbackFROMPageoneController);
    if(intSETonStartController == 1){
      txtStartValue.setText(str);  
    }
    System.out.println("== doGET WAS RUN ==");
    if(txtStartValue.getText() == null){
       txtStartValue.setText("");   
    }
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // This Method runs every time startController is LOADED
     doGET();
}    
}

ผู้ควบคุมหน้าหนึ่ง

public class PageoneController implements Initializable {

@FXML Pane startPane,pageonePane,pagetwoPane;
@FXML Button btnOne,btnTwo;
@FXML TextField txtPageOneValue;
public static String strSENTbackFROMPageoneController;
public Stage stage;

    @FXML
private void onBTNONE() throws IOException{

        stage = (Stage)pageonePane.getScene().getWindow();// pane you are ON
        pagetwoPane = FXMLLoader.load(getClass().getResource("pagetwo.fxml"));// pane you are GOING TO
        Scene scene = new Scene(pagetwoPane);// pane you are GOING TO
        stage.setScene(scene);
        stage.setTitle("Page Two"); 
        stage.show();
        stage.sizeToScene();
        stage.centerOnScreen();
}

@FXML
private void onBTNTWO() throws IOException{
    if(intSETonStartController == 2){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.setTitle("Alert");
        alert.setHeaderText("YES to change Text Sent Back");
        alert.setResizable(false);
        alert.setContentText("Select YES to send 'Alert YES Pressed' Text Back\n"
                + "\nSelect CANCEL send no Text Back\r");// NOTE this is a Carriage return\r
        ButtonType buttonTypeYes = new ButtonType("YES");
        ButtonType buttonTypeCancel = new ButtonType("CANCEL", ButtonData.CANCEL_CLOSE);

        alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeCancel);

        Optional<ButtonType> result = alert.showAndWait();
        if (result.get() == buttonTypeYes){
            txtPageOneValue.setText("Alert YES Pressed");
        } else {
            System.out.println("canceled");
            txtPageOneValue.setText("");
            onBack();// Optional
        }
    }
}

@FXML
private void onBack() throws IOException{

    strSENTbackFROMPageoneController = txtPageOneValue.getText();
    System.out.println("Text Returned = "+strSENTbackFROMPageoneController);
    stage = (Stage)pageonePane.getScene().getWindow();
    startPane = FXMLLoader.load(getClass().getResource("start.fxml")); 
    Scene scene = new Scene(startPane);
    stage.setScene(scene);
    stage.setTitle("Start Page"); 
    stage.show();
    stage.sizeToScene();
    stage.centerOnScreen(); 
}

private void doTEST(){
    String fromSTART = String.valueOf(intSETonStartController);
    txtPageOneValue.setText("SENT  "+fromSTART);
    if(intSETonStartController == 1){
       btnOne.setVisible(true);
       btnTwo.setVisible(false);
       System.out.println("INTEGER Value Entered = "+intSETonStartController);  
    }else{
       btnOne.setVisible(false);
       btnTwo.setVisible(true);
       System.out.println("INTEGER Value Entered = "+intSETonStartController); 
    }  
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    doTEST();
}    

}

หน้าสองตัวควบคุม

public class PagetwoController implements Initializable {

@FXML Pane startPane,pagetwoPane;
public Stage stage;
public static String str;

@FXML
private void toStart() throws IOException{

    str = "You ON Page Two";
    stage = (Stage)pagetwoPane.getScene().getWindow();// pane you are ON
    startPane = FXMLLoader.load(getClass().getResource("start.fxml"));// pane you are GOING TO
    Scene scene = new Scene(startPane);// pane you are GOING TO
    stage.setScene(scene);
    stage.setTitle("Start Page"); 
    stage.show();
    stage.sizeToScene();
    stage.centerOnScreen();  
}

@Override
public void initialize(URL url, ResourceBundle rb) {

}    

}

ด้านล่างคือไฟล์ FXML ทั้งหมด

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="pagetwoPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.PagetwoController">
   <children>
      <Button layoutX="227.0" layoutY="62.0" mnemonicParsing="false" onAction="#toStart" text="To Start Page">
         <font>
            <Font name="System Bold" size="18.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="startPane" prefHeight="200.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.startController">
   <children>
      <Label focusTraversable="false" layoutX="115.0" layoutY="47.0" text="This is the Start Pane">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Button fx:id="btnPageOne" focusTraversable="false" layoutX="137.0" layoutY="100.0" mnemonicParsing="false" onAction="#toPageOne" text="To Page One">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Label focusTraversable="false" layoutX="26.0" layoutY="150.0" text="Enter 1 OR 2">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <TextField fx:id="txtStartValue" layoutX="137.0" layoutY="148.0" prefHeight="28.0" prefWidth="150.0" />
   </children>
</AnchorPane>

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane id="AnchorPane" fx:id="pageonePane" prefHeight="200.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="atwopage.PageoneController">
   <children>
      <Label focusTraversable="false" layoutX="111.0" layoutY="35.0" text="This is Page One Pane">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <Button focusTraversable="false" layoutX="167.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBack" text="BACK">
         <font>
            <Font size="18.0" />
         </font></Button>
      <Button fx:id="btnOne" focusTraversable="false" layoutX="19.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBTNONE" text="Button One" visible="false">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Button fx:id="btnTwo" focusTraversable="false" layoutX="267.0" layoutY="97.0" mnemonicParsing="false" onAction="#onBTNTWO" text="Button Two">
         <font>
            <Font size="18.0" />
         </font>
      </Button>
      <Label focusTraversable="false" layoutX="19.0" layoutY="152.0" text="Send Anything BACK">
         <font>
            <Font size="18.0" />
         </font>
      </Label>
      <TextField fx:id="txtPageOneValue" layoutX="195.0" layoutY="150.0" prefHeight="28.0" prefWidth="150.0" />
   </children>
</AnchorPane>


3
ฉันขอโทษ แต่การโพสต์โค้ดร้อยบรรทัดโดยไม่มีคำอธิบายว่ามันทำอะไรหรือทำไมคุณทำอย่างที่คุณทำมันไม่ใช่คำตอบที่ดีมาก นอกจากนี้รหัสที่คุณโพสต์มีการจัดระเบียบไม่ดีและยากที่จะติดตาม
Zephyr

ไม่จำเป็นต้องหยาบคายกับผู้ถาม เราอยู่ที่นี่เพื่อเรียนรู้
Zeyad
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.