Так что это было довольно запутанно для меня. Я решил перенаправить консоль приложений в TextArea в своем интерфейсе.
Bud, когда я сделал это с Fixed TextArea (Fixed ID) в SceneBuilder, а затем аннотировал
@FXML
private TextArea consoleTextArea;
Ничего не происходит. Нет изменений в контенте. И да, я инициализировал его в конструкторе. И далее в инициализации. Это не рабочий код:
public class ConsoleController implements Initializable {
Thread t;
@FXML
private Label totalM;
@FXML
private Label freeM;
@FXML
private Label maxM;
@FXML
private TextArea consoleTextArea;
private Console console;
private PrintStream ps;
public ConsoleController() {
System.out.println("Called constructor");
totalM = new Label();
freeM = new Label();
maxM = new Label();
consoleTextArea = new TextArea();
console = new Console(consoleTextArea);
ps = new PrintStream(console, true);
}
@Override
public void initialize(URL location, ResourceBundle resources) {
redirectOutput(ps);
t = new Thread(() -> {
while (true) {
try {
Platform.runLater(() -> {
updateMemInfo();
});
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.setName("MemUpdateInfoThread");
t.setDaemon(true);
t.start();
}
private void updateMemInfo() {
totalM.setText("Total Memory (in bytes): " + Runtime.getRuntime().totalMemory());
freeM.setText("Free Memory (in bytes): " + Runtime.getRuntime().freeMemory());
maxM.setText("Max Memory (in bytes): " + Runtime.getRuntime().maxMemory());
}
private void redirectOutput(PrintStream prs) {
System.setOut(prs);
System.setErr(prs);
}
private void updateConsole(String text) {
for (int c : text.toCharArray()) {
try {
console.write(c);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private class Console extends OutputStream {
private TextArea txtArea;
public Console(TextArea txtArea) {
this.txtArea = txtArea;
}
@Override
public void write(int b) throws IOException {
txtArea.appendText(String.valueOf((char) b));
}
}
}
Bud после некоторого edditing.I решил не использовать fxml id.And только поместить id на родительский элемент AnchorPane и добавил textArea в java-код.
@FXML
private AnchorPane anchp;
private TextArea consoleTextArea;
//then added to anchor
anchp.getChildren().add(consoleTextArea);
рабочий код:
public class ConsoleController implements Initializable {
Thread t;
@FXML
private Label totalM;
@FXML
private Label freeM;
@FXML
private Label maxM;
@FXML
private AnchorPane anchp;
private TextArea consoleTextArea;
private Console console;
private PrintStream ps;
public ConsoleController() {
System.out.println("Called constructor");
totalM = new Label();
freeM = new Label();
maxM = new Label();
anchp=new AnchorPane();
consoleTextArea = new TextArea();
console = new Console(consoleTextArea);
ps = new PrintStream(console, true);
}
@Override
public void initialize(URL location, ResourceBundle resources) {
anchp.getChildren().add(consoleTextArea);
AnchorPane.setTopAnchor(consoleTextArea, 0d);
AnchorPane.setLeftAnchor(consoleTextArea, 0d);
AnchorPane.setRightAnchor(consoleTextArea, 0d);
AnchorPane.setBottomAnchor(consoleTextArea, 0d);
redirectOutput(ps);
t = new Thread(() -> {
while (true) {
try {
Platform.runLater(() -> {
updateMemInfo();
});
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.setName("MemUpdateInfoThread");
t.setDaemon(true);
t.start();
}
private void updateMemInfo() {
totalM.setText("Total Memory (in bytes): " + Runtime.getRuntime().totalMemory());
freeM.setText("Free Memory (in bytes): " + Runtime.getRuntime().freeMemory());
maxM.setText("Max Memory (in bytes): " + Runtime.getRuntime().maxMemory());
}
private void redirectOutput(PrintStream prs) {
System.setOut(prs);
System.setErr(prs);
}
private void updateConsole(String text) {
for (int c : text.toCharArray()) {
try {
console.write(c);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private class Console extends OutputStream {
private TextArea txtArea;
public Console(TextArea txtArea) {
this.txtArea = txtArea;
}
@Override
public void write(int b) throws IOException {
txtArea.appendText(String.valueOf((char) b));
}
}
}
Почему я не смог сделать это с помощью fixedID на компоненте, который я использовал? Может ли кто-нибудь объяснить, что я сделал неправильно?
И да, я инициализировал его в конструкторе.
Это точно проблема. Никогда не инициализируйте поля, которые вводятся в контроллер с помощью аннотации @FXML
.
Если вы аннотируете поле с @FXML
, FXMLLoader
собирается инициализировать это поле экземпляром, объявленным в файле FXML, сопоставляя имя поля (“consoleTextArea”) с атрибутом fx:id
. Очевидно, все это происходит после завершения конструктора, но до initialize()
метода initialize()
. Следовательно, consoleTextArea
вы передали в свой Console
конструктор, представляет собой другой экземпляр для того, к которому вы в конечном итоге к тому времени, когда initalize()
метод initalize()
(и когда обработчики событий вызывается позже).
Чтобы исправить это, полностью избавиться от конструктора и инициализировать другие части, которые вам нужны (т.е. вещи, которые не определены в вашем FXML) в методе initialize()
.
Что-то вроде:
public class ConsoleController implements Initializable {
Thread t;
@FXML
private Label totalM;
@FXML
private Label freeM;
@FXML
private Label maxM;
@FXML
private TextArea consoleTextArea;
private Console console;
private PrintStream ps;
@Override
public void initialize(URL location, ResourceBundle resources) {
console = new Console(consoleTextArea);
ps = new PrintStream(console, true);
redirectOutput(ps);
t = new Thread(() -> {
while (true) {
try {
Platform.runLater(() -> {
updateMemInfo();
});
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.setName("MemUpdateInfoThread");
t.setDaemon(true);
t.start();
}
// other methods as before...
}