如何在JavaFX中构建可自适应缩放的元素周期表?
JavaFX元素周期表自适应缩放实现方案
1. 先搞定元素数据加载
把txt里的元素信息封装成实体类,用IO流读取后存到列表:
// 元素实体类 public class Element { private String name; private String symbol; private double mass; private int period; // 周期 private int group; // 族 public Element(String name, String symbol, double mass, int period, int group) { this.name = name; this.symbol = symbol; this.mass = mass; this.period = period; this.group = group; } // 生成getter方法 } // 读取txt数据的方法 private List<Element> loadElements() throws IOException { List<Element> elements = new ArrayList<>(); // 假设txt每行格式:名称,符号,质量,周期,族 try (BufferedReader br = new BufferedReader(new FileReader("elements.txt"))) { String line; while ((line = br.readLine()) != null) { String[] parts = line.split(","); Element elem = new Element( parts[0], parts[1], Double.parseDouble(parts[2]), Integer.parseInt(parts[3]), Integer.parseInt(parts[4]) ); elements.add(elem); } } return elements; }
2. 主布局用GridPane实现自适应网格
周期表是固定行列结构,GridPane最适合,通过行列约束让单元格随窗口缩放:
private GridPane buildPeriodicTableGrid() { GridPane tableGrid = new GridPane(); tableGrid.setPadding(new Insets(10)); tableGrid.setHgap(5); tableGrid.setVgap(5); // 设置18列,每列自动占满宽度 for (int i = 0; i < 18; i++) { ColumnConstraints colConst = new ColumnConstraints(); colConst.setHgrow(Priority.ALWAYS); // 列随窗口宽度拉伸 colConst.setFillWidth(true); tableGrid.getColumnConstraints().add(colConst); } // 设置7行,每行自动占满高度 for (int i = 0; i < 7; i++) { RowConstraints rowConst = new RowConstraints(); rowConst.setVgrow(Priority.ALWAYS); // 行随窗口高度拉伸 rowConst.setFillHeight(true); tableGrid.getRowConstraints().add(rowConst); } return tableGrid; }
3. 让元素卡片支持响应式缩放
加载FXML卡片后,确保内部控件能随卡片尺寸变化:
- 在FXML里给控件设置
maxWidth="Infinity"、maxHeight="Infinity",并绑定字体大小到容器尺寸:
<!-- elementCard.fxml 示例 --> <VBox fx:id="cardRoot" alignment="CENTER" spacing="5" style="-fx-border-color: black; -fx-padding: 10;"> <Label fx:id="symbolLabel" text="H" style="-fx-font-weight: bold;" maxWidth="Infinity" alignment="CENTER"/> <Label fx:id="nameLabel" text="氢" maxWidth="Infinity" alignment="CENTER"/> <Label fx:id="massLabel" text="1.008" maxWidth="Infinity" alignment="CENTER"/> </VBox>
- 在卡片控制器里绑定字体大小:
public class ElementCardController { @FXML private VBox cardRoot; @FXML private Label symbolLabel; @FXML private Label nameLabel; @FXML private Label massLabel; public void setElementData(Element elem) { symbolLabel.setText(elem.getSymbol()); nameLabel.setText(elem.getName()); massLabel.setText(String.valueOf(elem.getMass())); // 绑定符号字体大小到卡片宽度的1/4 symbolLabel.fontProperty().bind( new SimpleObjectProperty<>(Font.font("Arial", FontWeight.BOLD, cardRoot.widthProperty().divide(4))) ); // 其他标签字体大小绑定到卡片宽度的1/8 nameLabel.fontProperty().bind( new SimpleObjectProperty<>(Font.font("Arial", cardRoot.widthProperty().divide(8))) ); massLabel.fontProperty().bind( new SimpleObjectProperty<>(Font.font("Arial", cardRoot.widthProperty().divide(8))) ); } }
4. 整合主应用并添加窗口缩放支持
把所有部分拼起来,加载元素、生成网格、添加卡片:
public class PeriodicTableApp extends Application { @Override public void start(Stage primaryStage) throws IOException { List<Element> elements = loadElements(); GridPane tableGrid = buildPeriodicTableGrid(); // 给网格添加元素卡片 for (Element elem : elements) { FXMLLoader loader = new FXMLLoader(getClass().getResource("elementCard.fxml")); VBox card = loader.load(); ElementCardController controller = loader.getController(); controller.setElementData(elem); // 点击卡片打开新窗口 card.setOnMouseClicked(e -> { Stage detailStage = new Stage(); detailStage.setTitle(elem.getName() + " - 详情"); // 可以复制卡片内容到新窗口,或者加载专门的详情FXML VBox detailCard = (VBox) card.clone(); detailStage.setScene(new Scene(detailCard, 350, 450)); detailStage.show(); }); // 把卡片放到对应周期和族的位置(注意GridPane索引从0开始) tableGrid.add(card, elem.getGroup() - 1, elem.getPeriod() - 1); } Scene scene = new Scene(tableGrid, 1200, 800); primaryStage.setTitle("元素周期表"); primaryStage.setScene(scene); primaryStage.show(); } // 这里放上前面的loadElements()和buildPeriodicTableGrid()方法 public static void main(String[] args) { launch(args); } }
关键注意点
- 确保txt文件路径正确,若放在资源目录下,改用
getClass().getResourceAsStream("elements.txt")读取。 - 如果GridPane的自适应效果不够理想,可以监听场景的尺寸变化,手动调整卡片大小:
scene.widthProperty().addListener((obs, oldWidth, newWidth) -> { double cardWidth = (newWidth.doubleValue() - 20 - 17*5) / 18; // 减去padding和gap tableGrid.getChildren().forEach(node -> node.setPrefWidth(cardWidth)); });
内容的提问来源于stack exchange,提问作者Enrico Sombini
相关产品推荐
相关产品推荐

