如何将System.in重定向至JavaFX TextField?
JavaFX控制台GUI输入重定向问题:ConsoleTask移至Controller后无法运行
我是Java/JavaFX新手,正在开发控制台GUI应用,遇到无法将System.in重定向到JavaFX TextField的问题,尝试过两种方案均未成功。后续找到基于FXML的可行方案,代码参考自Slaws的回答,但目前出现新问题:ConsoleTask放在Controller类中无法运行,此前在Main类中可正常运行,期望实现用GUI的TextField作为Java控制台程序的输入。
已实现的基础FXML方案
该代码适配FXML文件,其余逻辑参考自Slaws的回答。
Main.java
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; public class Main extends Application { final static ExecutorService threadPool = Executors.newFixedThreadPool(3, r -> { var t = new Thread(r); t.setDaemon(true); return t; }); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("mainScreen.fxml")); String css = this.getClass().getResource("style.css").toExternalForm(); Scene scene = new Scene(root); scene.getStylesheets().add(css); stage.setMinHeight(800); stage.setMinWidth(1200); stage.setScene(scene); stage.show(); } @Override public void stop() { threadPool.shutdownNow(); } }
Controller.java
import java.io.IOException; import javafx.fxml.FXML; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.scene.text.Font; import javafx.application.Platform; public class Controller { @FXML private TextArea console; @FXML private TextField inputPane; public void initialize() throws IOException{ wireInputAndOutput(inputPane, console); startConsoleTask(); } static void wireInputAndOutput(TextField input, TextArea output) throws IOException { var inputConsumer = StreamWriter.redirectStandardIn(Main.threadPool); StreamReader.redirectStandardOut(new BufferedTextAreaAppender(output), Main.threadPool); input.setOnAction(e -> { e.consume(); var text = input.textProperty().getValueSafe() + "\n"; output.appendText(text); inputConsumer.accept(text); input.clear(); }); } private void startConsoleTask() { Main.threadPool.execute(new ConsoleTask(Platform::exit)); } }
编辑补充:当前核心问题
将ConsoleTask移到Controller.java后无法运行,此前直接复制Slaws的Main.java代码时可正常运行,但为适配FXML改造后功能失效。
test.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.TextArea?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.VBox?> <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller"> <children> <TextArea fx:id="console" editable="false" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS"> <VBox.margin> <Insets bottom="20.0" /> </VBox.margin> </TextArea> <TextField fx:id="inputPane" prefHeight="40.0" VBox.vgrow="ALWAYS" /> </children> <padding> <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" /> </padding> </VBox>
ConsoleTask.java
import java.util.NoSuchElementException; import java.util.Scanner; class ConsoleTask implements Runnable { private final static Scanner scanner = new Scanner(System.in); private final Runnable onExit; ConsoleTask(Runnable onExit) { this.onExit = onExit; } @Override public void run() { System.out.println("WELCOME TO CONSOLE APP!"); boolean running = true; while (running && !Thread.interrupted()) { printOptions(); int choice = readInt("Choose option: ", 1, 20); switch (choice) { case 1 -> doCheckIfLeapYear(); case 2 -> doCheckIfPrime(); case 3 -> doPrintStringBackwards(); case 20 -> running = false; default -> System.out.println("Unknown option!"); } System.out.println("\nPlease wait..."); wait(6000); } onExit.run(); } private void printOptions() { System.out.println(); System.out.println("Options"); System.out.println("-------"); System.out.println(" 1) Test Leap Year"); System.out.println(" 2) Test Prime"); System.out.println(" 3) Print String backwards"); System.out.println(" 20) Exit"); System.out.println(); } private int readInt(String prompt, int min, int max) { while (true) { System.out.print(prompt); try { int i = Integer.parseInt(scanner.nextLine()); if (i >= min && i <= max) { return i; } } catch (NumberFormatException | NoSuchElementException ignored) { } System.out.printf("Please enter an integer between [%,d, %,d]%n", min, max); } } private void doCheckIfLeapYear() { System.out.println(); int year = readInt("Enter year: ", 0, 1_000_000); if (year % 4 == 0 || (year % 100 == 0 && year % 400 != 0)) { System.out.printf("The year %d is a leap year.%n", year); } else { System.out.printf("The year %d is NOT a leap year.%n", year); } } private void doCheckIfPrime() { System.out.println(); int limit = readInt("Enter an Integer: ", 1, Integer.MAX_VALUE); while (limit <= 1){ System.out.print("Wrong number! the number should be higher or equal to 2: "); limit = scanner.nextInt(); } if(isPrime(limit)){ System.out.println(limit+" is a prime number."); }else{ System.out.println(limit+" is not a prime number."); } } private static boolean isPrime(int n){ if(n <= 1){ return false; } for(int i = 2; i <= Math.sqrt(n); i++){ if(n % i == 0){ return false; } } return true; } private static void doPrintStringBackwards(){ System.out.println(); System.out.print("\nEnter a word or a sentence: "); String answer = scanner.nextLine(); //StackOverflow version: (simple) System.out.println("\n"+new StringBuilder(answer).reverse().toString()); //Lecture version: /*for (int i = answer.length()-1;i>=0;i--){ System.out.print(answer.charAt(i)); }*/ } private static void wait(int ms){ try { Thread.sleep(ms); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } } }
内容的提问来源于stack exchange,提问作者UnseenSnick
相关产品推荐
相关产品推荐

