Я столкнулся с проблемой, что 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 "update list" 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 = " " 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 = " " 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. Определите отдельный класс контроллера обычным способом.
Но как я могу получить доступ к элементам в другом проекте, как я уже говорил? Почему он полностью инициализирует каждое представление scene.lookup("#fxid")
в другом моем проекте JavaFX? Этот другой проект имеет те же зависимости и ту же версию JavaFX с JDK? Я что-то не понимаю? @Джеймс_Д
@Drained, вы не можете сообщить о не предоставленном коде (ваш другой проект), код в этом проекте будет отличаться от кода, который вы предоставили неизвестным образом.
Использование динамического поиска вместо введения ссылки в FXML обычно является плохой идеей. IDE и FXMLLoader могут проверять, что внедренные FXML ссылки в порядке и типобезопасны, но для функции поиска узла такой проверки не существует. Во время выполнения поиска сцена не была визуализирована, а CSS и проходы макета не были выполнены, что означает, что граф сцены не был полностью инициализирован (я не знаю, является ли это причиной вашей неудачи).
Поиск CSS по идентификатору выбирает идентификаторы CSS, а не идентификаторы FXML. Вы используете оба в своем FXML (id = ".."
и fx:id = ".."
). Хотя загрузчик FXML может инициализировать идентификаторы CSS элементов с fx:id
одним и тем же значением, я в этом не уверен.
Хорошо. Способ, которым я сейчас пользуюсь, плох. Спасибо @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) {
}
});
}
}