JavaFX не инициализирует представления

Я столкнулся с проблемой, что JavaFX не инициализирует какое-либо представление или контейнер по fx:id. И он выдает исключение NullPointerException со ссылкой на объект, который я уже инициализировал.

Например, я попытался инициализировать с помощью метода scene.lookup("#updatelist") и установить прослушиватель на кнопку updateList и получил это исключение:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at javafx.graphics@21/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
    at javafx.graphics@21/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1135)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics@21/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:893)
    at javafx.graphics@21/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.control.Button.setOnMouseClicked(javafx.event.EventHandler)" because "this.updateList" is null
    at com.lifedrained.injector.injector@1/com.lifedrained.injector.injector.HelloApplication.start(HelloApplication.java:49)
    at javafx.graphics@21/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:839)
    at javafx.graphics@21/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:483)
    at javafx.graphics@21/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:456)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at javafx.graphics@21/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:455)
    at javafx.graphics@21/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at javafx.graphics@21/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@21/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:185)
    ... 1 more
Exception running application com.lifedrained.injector.injector.HelloApplication

Вот мой основной класс с инициализацией:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.EventHandler;
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.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import javafx.util.Callback;

import java.io.IOException;
import java.util.Objects;

public class HelloApplication extends Application {
     Button updateList, inject, chooseDll, copyEx;
    private ListView<ProcessData> listView;
    private Label dllPath, exLog;
     Scene scene;
    private ListAdapter adapter;
    private ProcessGainer processGainer;
    private ProcessData selectedProcess;


    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("view.fxml"));

         scene = new Scene(fxmlLoader.load(), 600, 400);
        listView  =(ListView<ProcessData>) scene.lookup("#processlist");
        updateList = (Button) scene.lookup("#updatelist");
        inject = (Button) scene.lookup("#injectbtn");
        chooseDll = (Button) scene.lookup("#choose_dll_btn");
        copyEx = (Button) scene.lookup("#copy_btn");
        dllPath = (Label) scene.lookup("#path_lbl");

        stage.setTitle("Injector by Drained");
        stage.setScene(scene);
        stage.getIcons().add(new Image(Objects.requireNonNull(HelloApplication.class.getResourceAsStream("assets/syringe_icon.svg.png"))));
        stage.setResizable(false);
        stage.show();
        updateList.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                listView.setCellFactory(new Callback<ListView<ProcessData>, ListCell<ProcessData>>() {
                    @Override
                    public ListCell<ProcessData> call(ListView<ProcessData> param) {
                        param.setItems(FXCollections.observableArrayList(processGainer.listProcesses()));
                        return new ListAdapter(new ClickCallback() {
                            @Override
                            public void onSelect(ProcessData data) {
                                selectedProcess = data;
                                dllPath.setText("dll path: "+selectedProcess.getPath());
                            }
                        });
                    }
                });
            }
        });
    }

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


}

в файле fxml у меня тот же fx:id, что и в методе scene.lookup(). Вот мой файл fxml, подтверждающий, что я правильно ввожу fx:id:

<VBox prefHeight = "400.0" prefWidth = "600.0" xmlns = "http://javafx.com/javafx/21" xmlns:fx = "http://javafx.com/fxml/1">
    <children>
        <MenuBar fx:id = "menu_bar" visible = "false" VBox.vgrow = "NEVER">
            <menus>
                <Menu mnemonicParsing = "false" text = "File">
                    <items>
                        <MenuItem mnemonicParsing = "false" text = "New" />
                        <MenuItem mnemonicParsing = "false" text = "Open…" />
                        <Menu mnemonicParsing = "false" text = "Open Recent" />
                        <SeparatorMenuItem mnemonicParsing = "false" />
                        <MenuItem mnemonicParsing = "false" text = "Close" />
                        <MenuItem mnemonicParsing = "false" text = "Save" />
                        <MenuItem mnemonicParsing = "false" text = "Save As…" />
                        <MenuItem mnemonicParsing = "false" text = "Revert" />
                        <SeparatorMenuItem mnemonicParsing = "false" />
                        <MenuItem mnemonicParsing = "false" text = "Preferences…" />
                        <SeparatorMenuItem mnemonicParsing = "false" />
                        <MenuItem mnemonicParsing = "false" text = "Quit" />
                    </items>
                </Menu>
                <Menu mnemonicParsing = "false" text = "Edit">
                    <items>
                        <MenuItem mnemonicParsing = "false" text = "Undo" />
                        <MenuItem mnemonicParsing = "false" text = "Redo" />
                        <SeparatorMenuItem mnemonicParsing = "false" />
                        <MenuItem mnemonicParsing = "false" text = "Cut" />
                        <MenuItem mnemonicParsing = "false" text = "Copy" />
                        <MenuItem mnemonicParsing = "false" text = "Paste" />
                        <MenuItem mnemonicParsing = "false" text = "Delete" />
                        <SeparatorMenuItem mnemonicParsing = "false" />
                        <MenuItem mnemonicParsing = "false" text = "Select All" />
                        <MenuItem mnemonicParsing = "false" text = "Unselect All" />
                    </items>
                </Menu>
                <Menu mnemonicParsing = "false" text = "Help">
                    <items>
                        <MenuItem mnemonicParsing = "false" text = "About MyHelloApp" />
                    </items>
                </Menu>
            </menus>
        </MenuBar>
        <SplitPane dividerPositions = "0.33121657754010686, 0.6871657754010694" focusTraversable = "true" VBox.vgrow = "ALWAYS">
            <items>
                <ScrollPane prefHeight = "350.0" prefWidth = "198.0">
                    <content>
                        <AnchorPane id = "Content" minHeight = "-1.0" minWidth = "-Infinity" prefHeight = "350.0" prefWidth = "194.0" style = "-fx-background-color: E9E9E9;">
                            <children>
                                <ListView fx:id = "processlist" layoutX = "15.0" layoutY = "28.0" prefHeight = "200.0" prefWidth = "150.0" />
                                <Button fx:id = "updatelist" layoutX = "54.0" layoutY = "228.0" maxWidth = "1.7976931348623157E308" minHeight = "-Infinity" mnemonicParsing = "false" text = "Update list" />
                                <Label contentDisplay = "CENTER" layoutX = "11.0" layoutY = "264.0" prefHeight = "70.0" prefWidth = "160.0" style = "-fx-background-color: white; -fx-alignment: center;" text = "Before reinjection: Make sure you've updated procces list by pressing &quot;update list&quot; button !" textAlignment = "CENTER" textOverrun = "WORD_ELLIPSIS" wrapText = "true" />
                                <Label contentDisplay = "CENTER" layoutX = "16.0" layoutY = "4.0" prefHeight = "20.0" prefWidth = "150.0" style = "-fx-background-color: white; -fx-alignment: center;" text = "Process list" textAlignment = "CENTER" textOverrun = "WORD_ELLIPSIS" wrapText = "true" />
                            </children>
                        </AnchorPane>
                    </content>
                </ScrollPane>
                <AnchorPane prefHeight = "550.0" prefWidth = "20.0" style = "-fx-background-color: E9E9E9;">
                    <children>
                        <Button fx:id = "choose_dll_btn" layoutX = "47.0" layoutY = "160.0" mnemonicParsing = "false" prefHeight = "30.0" prefWidth = "120.0" text = "Choose dll path" AnchorPane.topAnchor = "240.0" />
                        <Button fx:id = "injectbtn" layoutX = "69.0" layoutY = "254.0" mnemonicParsing = "false" prefHeight = "26.0" prefWidth = "70.0" text = "Inject" AnchorPane.topAnchor = "205.0" />
                        <ScrollPane layoutX = "7.0" prefHeight = "200.0" prefWidth = "200.0">
                            <content>
                                <Label fx:id = "path_lbl" alignment = "TOP_LEFT" maxHeight = "1.7976931348623157E308" maxWidth = "1.7976931348623157E308" style = "&#10;" text = "dll path: " textOverrun = "CLIP" wrapText = "true">
                                    <textFill>
                                        <Color red = "0.624" green = "0.624" blue = "0.624" fx:id = "x2" />
                                    </textFill>
                                </Label>
                            </content>
                        </ScrollPane>
                    </children>
                </AnchorPane>
                <AnchorPane style = "-fx-background-color: E9E9E9;">
                    <children>
                        <ScrollPane fx:id = "exception_pane" layoutX = "14.0" layoutY = "14.0" visible = "false">
                            <content>
                                <Label fx:id = "exception_lbl" alignment = "TOP_LEFT" disable = "true" prefHeight = "271.0" prefWidth = "158.0" style = "&#10;" textAlignment = "CENTER" visible = "false" wrapText = "false">
                                    <font>
                                        <Font size = "11.0" fx:id = "x7" />
                                    </font>
                                </Label>
                            </content>
                        </ScrollPane>
                        <Button fx:id = "copy_btn" disable = "true" layoutX = "43.0" layoutY = "301.0" mnemonicParsing = "false" text = "copy exception" visible = "false" />
                    </children>
                </AnchorPane>
            </items>
        </SplitPane>
        <HBox id = "HBox" alignment = "CENTER_LEFT" spacing = "5.0" VBox.vgrow = "NEVER">
            <children>
                <Label maxHeight = "1.7976931348623157E308" maxWidth = "-1.0" text = "Special created for Techno - " HBox.hgrow = "ALWAYS">
                    <font>
                        <Font size = "11.0" fx:id = "x3" />
                    </font>
                    <textFill>
                        <Color red = "0.625" green = "0.625" blue = "0.625" fx:id = "x4" />
                    </textFill>
                </Label>
                <Pane prefHeight = "-1.0" prefWidth = "-1.0" HBox.hgrow = "ALWAYS">
                    <children>
                        <Hyperlink layoutY = "-4.0" text = "https://www.youtube.com/@TechnoKVofficial/videos" />
                    </children>
                </Pane>
            </children>
            <padding>
                <Insets bottom = "3.0" left = "3.0" right = "3.0" top = "3.0" />
            </padding>
        </HBox>
    </children>
</VBox>

Я пытался использовать аннотацию @FXML для представлений в контроллере, но кнопка и другие представления по-прежнему имеют нулевое значение. Кстати, у меня есть другой проект JavaFX с тем же способом инициализации представления, что и в основном классе, но он работает отлично. Моя версия JavaFX — 21. И JDK, который я использовал, — 21. Я использую систему сборки Gradle с отличным DSL. Вот мой файл build.gradle:

plugins {
    id 'java'
    id 'application'
    id 'org.javamodularity.moduleplugin' version '1.8.12'
    id 'org.openjfx.javafxplugin' version '0.0.13'
    id 'org.beryx.jlink' version '2.25.0'
}

group 'com.lifedrained.injector.injector'
version '1'

repositories {
    mavenCentral()
}

ext {
    junitVersion = '5.10.0'
}

sourceCompatibility = '21'
targetCompatibility = '21'

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

application {
    mainModule = 'com.lifedrained.injector.injector'
    mainClass = 'com.lifedrained.injector.injector.HelloApplication'
}

javafx {
    version = '21'
    modules = ['javafx.controls', 'javafx.fxml', 'javafx.web', 'javafx.swing']
}

dependencies {
    implementation('com.dlsc.formsfx:formsfx-core:11.6.0') {
        exclude(group: 'org.openjfx')
    }
    implementation('net.synedra:validatorfx:0.4.0') {
        exclude(group: 'org.openjfx')
    }
    // https://mvnrepository.com/artifact/net.java.dev.jna/jna
    implementation 'net.java.dev.jna:jna:5.14.0'
    implementation 'net.java.dev.jna:jna-platform:5.14.0'
    implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
    implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
    implementation('org.kordamp.ikonli:ikonli-javafx:12.3.1')
    implementation('org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0')
    implementation('eu.hansolo:tilesfx:11.48') {
        exclude(group: 'org.openjfx')
    }

    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}

test {
    useJUnitPlatform()
}

jlink {
    imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'app'
    }
}

jlinkZip {
    group = 'distribution'
}

Это просто не способ доступа к элементам, определенным в файле FXML. Определите отдельный класс контроллера обычным способом.

James_D 10.07.2024 13:49

Но как я могу получить доступ к элементам в другом проекте, как я уже говорил? Почему он полностью инициализирует каждое представление scene.lookup("#fxid") в другом моем проекте JavaFX? Этот другой проект имеет те же зависимости и ту же версию JavaFX с JDK? Я что-то не понимаю? @Джеймс_Д

Drained 10.07.2024 18:50

@Drained, вы не можете сообщить о не предоставленном коде (ваш другой проект), код в этом проекте будет отличаться от кода, который вы предоставили неизвестным образом.

jewelsea 11.07.2024 00:11

Использование динамического поиска вместо введения ссылки в FXML обычно является плохой идеей. IDE и FXMLLoader могут проверять, что внедренные FXML ссылки в порядке и типобезопасны, но для функции поиска узла такой проверки не существует. Во время выполнения поиска сцена не была визуализирована, а CSS и проходы макета не были выполнены, что означает, что граф сцены не был полностью инициализирован (я не знаю, является ли это причиной вашей неудачи).

jewelsea 11.07.2024 00:13

Поиск CSS по идентификатору выбирает идентификаторы CSS, а не идентификаторы FXML. Вы используете оба в своем FXML (id = ".." и fx:id = ".."). Хотя загрузчик FXML может инициализировать идентификаторы CSS элементов с fx:id одним и тем же значением, я в этом не уверен.

jewelsea 11.07.2024 00:21
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
6
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Хорошо. Способ, которым я сейчас пользуюсь, плох. Спасибо @James_D и @Jewelsea, эти люди подсказали мне, как получить доступ к просмотрам.

Это просто не способ доступа к элементам, определенным в файле FXML. Определите отдельный класс контроллера обычным способом. -Джеймс_Д

Использование динамического поиска вместо введения ссылки в FXML обычно является плохой идеей. IDE и FXMLLoader могут проверять, что внедренные FXML ссылки в порядке и типобезопасны, но для функции поиска узла такой проверки не существует. Во время выполнения поиска сцена не была визуализирована, а проходы CSS и макета не были выполнены, что означает, что граф сцены не был полностью инициализирован - Jewelsea.

Мне следует использовать класс контроллера с аннотацией @FXML для доступа к представлениям вместо использования scene.lookup(), как я указал в вопросе.

 public class Controller {
    @FXML
    private VBox vBox;
    @FXML
    private Pane pane;
    @FXML
    private Button updatelist;
    @FXML
    private void initialize() {

         //any logic here with views...
        //example: setting listener
        updatelist.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {

            }
        });
    }
}

Другие вопросы по теме