【发布时间】:2015-10-02 19:16:48
【问题描述】:
我有一个内置在 SceneBuilder 中的 FXML 类,它通过 FXMLLoader 加载,并带有一个与之关联的控制器。我在小部件的父面板上放置了一个带有伪类的样式类。 ToggleButton 的样式很好,但两个标签不是。
作为测试,我尝试直接为其中一个标签指定 CSS 类。这种行为很有趣;它在初始更改时得到响应,但在伪类更改时没有更新文本颜色。
以下是从生产代码中删除的 SCCEE。所有文件都直接在类路径中。
CustomButton.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="70.0" prefWidth="70.0" styleClass="custom-button" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CustomButtonController">
<children>
<ToggleButton fx:id="selectionToggle" mnemonicParsing="false" prefHeight="70.0" prefWidth="70.0" />
<Label fx:id="dateLabel" alignment="TOP_LEFT" contentDisplay="TOP" layoutX="5.0" layoutY="5.0" mouseTransparent="true" prefHeight="25.0" prefWidth="60.0" styleClass="custom-button" text="Date" wrapText="true" />
<Label fx:id="eventLabel" alignment="BOTTOM_LEFT" contentDisplay="BOTTOM" layoutX="5.0" layoutY="40.0" mouseTransparent="true" prefHeight="25.0" prefWidth="60.0" text="Event" wrapText="true" />
</children>
</Pane>
CustomButtonController.java:
/**
* Sample Skeleton for 'CustomButton.fxml' Controller Class
*/
import java.net.URL;
import java.util.ResourceBundle;
import javafx.css.PseudoClass;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
public class CustomButtonController {
@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
@FXML // fx:id="dateLabel"
private Label dateLabel; // Value injected by FXMLLoader
@FXML // fx:id="selectionToggle"
private ToggleButton selectionToggle; // Value injected by FXMLLoader
@FXML // fx:id="eventLabel"
private Label eventLabel; // Value injected by FXMLLoader
private Parent m_parent;
@FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert dateLabel != null : "fx:id=\"dateLabel\" was not injected: check your FXML file 'CustomButton.fxml'.";
assert selectionToggle != null : "fx:id=\"selectionToggle\" was not injected: check your FXML file 'CustomButton.fxml'.";
assert eventLabel != null : "fx:id=\"eventLabel\" was not injected: check your FXML file 'CustomButton.fxml'.";
}
public void setParent(Parent parent) {
m_parent = parent;
selectionToggle.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
m_parent.pseudoClassStateChanged(PseudoClass.getPseudoClass("state2"), selectionToggle.isSelected());
m_parent.applyCss();
}
});
}
}
Main.java:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main implements Runnable {
@Override
public void run() {
JFrame frame = new JFrame("Test Frame");
frame.setSize(800, 600);
JPanel panel = new JPanel();
frame.setContentPane(panel);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/CustomButton.fxml"));
Parent parent = loader.load();
String cssString = getClass().getResource("/CustomButton.css").toExternalForm();
parent.getStylesheets().add(cssString);
CustomButtonController cont = loader.<CustomButtonController>getController();
cont.setParent(parent);
JFXPanel jfxPanel = new JFXPanel();
jfxPanel.setScene(new Scene(parent));
panel.add(jfxPanel);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JFXPanel();
Platform.runLater(new Main());
}
});
}
}
CustomButton.css:
.custom-button:state2 {
-fx-base: cyan;
-fx-text-fill: red;
-fx-fill: yellow;
-fx-stroke: purple;
}
.custom-button {
-fx-base: green;
-fx-text-fill: blue;
}
结果(顶部未选中,底部已选中):
请注意,ToggleButton 服从 -fx-base,日期标签(直接添加了 CSS 类)服从 -fx-text-fill 的初始颜色,但不会在选择时更新),以及事件标签不会从 CSS 中获取任何内容。
【问题讨论】:
-
有必要使用伪类吗?一个 Node 可以拥有任意数量的样式类,那么为什么不在 CSS 中使用像
.custom-button.state2(句点而不是冒号)这样的选择器呢? -
@VGR 一般来说,伪类对于这种功能来说要方便得多,因为您不必担心多次检查是否没有添加同一个类。
-
@James_D 我不确定我是否跟随。无论是使用样式类还是伪类,在将控件返回到正常状态时,代码仍然必须将其删除。在最坏的情况下,多次添加样式类没有任何后果(除了小的内存泄漏)。我错过了什么?
-
@VGR 如果您可能无意中多次添加它,则删除它的代码必须确保它删除了 所有 个副本。如果您不控制管理此方法的方法的调用(例如单元实现中的
updateItem(...)),则必须涵盖所有可能性。所以你要么需要昂贵的(和丑陋的)if (! getStyleClass().contains("my-style")) getStyleClass().add("my-style");来添加,要么需要昂贵的(和丑陋的)while (getStyleClass().contains("my-class")) getStyleClass().remove("my-style"); -
@James_D 或者,
getStyleClass().removeAll(Collections.singleton("my-style")),这对我来说似乎并没有那么糟糕。我想这是主观的;对我来说,使用 PseudoClass 似乎更尴尬。
标签: java css javafx fxml fxmlloader