JavaFX Custom Control CSS PseudoClass 状态改变描边颜色
JavaFX Custom Control CSS PseudoClass state change stroke color
我创建了一个带有两个 CSS 样式属性和一个 CSS 状态伪类的 JavaFX 自定义控件。
我不确定我遗漏了什么,但更改状态不会更新描边颜色。
Reference.java
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.ObjectProperty;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;
import javafx.css.Styleable;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.css.StyleablePropertyFactory;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.paint.Color;
public class Reference extends Control {
private static final StyleablePropertyFactory<Reference> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData());
private static final CssMetaData<Reference, Color> STROKE_COLOR = FACTORY.createColorCssMetaData("-stroke-color", s -> s.strokeColor, Color.BLACK, false);
private final StyleableProperty<Color> strokeColor;
private static final CssMetaData<Reference, Number> STROKE_WIDTH = FACTORY.createSizeCssMetaData("-stroke-width", s -> s.strokeWidth, Double.valueOf(5.0), false);
private final StyleableProperty<Number> strokeWidth;
private static final PseudoClass ACTIVE_PSEUDO_CLASS = PseudoClass.getPseudoClass("active");
private BooleanProperty active;
private boolean activeValue = false;
public Reference() {
getStyleClass().add("reference");
strokeColor = new StyleableObjectProperty<Color>(STROKE_COLOR.getInitialValue(Reference.this)) {
@Override protected void invalidated() {}
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "strokeColor"; }
@Override public CssMetaData<? extends Styleable, Color> getCssMetaData() { return STROKE_COLOR; }
};
// strokeColor = new SimpleStyleableObjectProperty<>(STROKE_COLOR, this, "strokeColor", STROKE_COLOR.getInitialValue(Reference.this));
strokeWidth = new StyleableObjectProperty<Number>(STROKE_WIDTH.getInitialValue(Reference.this)) {
@Override protected void invalidated() {}
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "strokeWidth"; }
@Override public CssMetaData<? extends Styleable, Number> getCssMetaData() { return STROKE_WIDTH; }
};
}
public final boolean isActive() {
return null == active ? activeValue : active.get();
}
public final void setActive(final boolean active) {
activeProperty().set(active);
}
public final BooleanProperty activeProperty() {
if (null == active) {
active = new BooleanPropertyBase(activeValue) {
@Override protected void invalidated() { pseudoClassStateChanged(ACTIVE_PSEUDO_CLASS, get()); }
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "active"; }
};
}
return active;
}
public Color getStrokeColor() { return strokeColor.getValue(); }
public void setStrokeColor(final Color color) { strokeColor.setValue(color); }
public ObjectProperty<Color> strokeColorProperty() { return (ObjectProperty<Color>) strokeColor; }
public Double getStrokeWidth() { return strokeWidth.getValue().doubleValue(); }
public void setStrokeColor(final Double number) { strokeWidth.setValue(number); }
public ObjectProperty<Number> strokeWidthProperty() { return (ObjectProperty<Number>) strokeWidth; }
/* (non-Javadoc)
* @see javafx.scene.control.Control#createDefaultSkin()
*/
@Override
protected Skin<?> createDefaultSkin() {
return new ReferenceSkin(this);
}
/* (non-Javadoc)
* @see javafx.scene.layout.Region#getUserAgentStylesheet()
*/
@Override
public String getUserAgentStylesheet() {
return getClass().getResource(getClass().getSimpleName().toLowerCase() + ".css").toExternalForm();
}
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
/* (non-Javadoc)
* @see javafx.scene.control.Control#getControlCssMetaData()
*/
@Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return getClassCssMetaData();
}
}
ReferenceSkin.java
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.SkinBase;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
public class ReferenceSkin extends SkinBase<Reference> {
private ObjectProperty<Paint> strokeColorProperty;
private ObjectProperty<Number> strokeWidthProperty;
public ReferenceSkin(Reference control) {
super(control);
strokeColorProperty = new SimpleObjectProperty<>(control.getStrokeColor());
strokeWidthProperty = new SimpleObjectProperty<>(control.getStrokeWidth());
initGraphics();
registerListeners();
}
/**
* Initialize graphics.
*/
private void initGraphics() {
final double startX = 0.0;
final double startY = 0.0;
final double endX = 200.;
final double endY = 0.0;
final Line line = new Line(startX, startY, endX, endY);
line.strokeProperty().bind(strokeColorProperty);
line.strokeWidthProperty().bind(strokeWidthProperty);
getChildren().add(line);
}
private void registerListeners() {
getSkinnable().activeProperty().addListener(observable -> handleControlPropertyChanged("ACTIVE"));
}
protected void handleControlPropertyChanged(final String PROPERTY) {
if ("ACTIVE".equals(PROPERTY)) {
if (getSkinnable().isActive()) {
System.out.println("isActive");
} else {
System.out.println("isInactive");
}
}
}
/* (non-Javadoc)
* @see javafx.scene.control.SkinBase#layoutChildren(double, double, double, double)
*/
@Override
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
for (Node child : getChildren()) {
if (child.isManaged()) {
layoutInArea(child, contentX, contentY, contentWidth, contentHeight, 10, HPos.RIGHT, VPos.TOP);
}
}
}
}
ReferenceApplication.java
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class ReferenceApplication extends Application {
public static void main(String[] args) {
ReferenceApplication.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
AnchorPane pane = new AnchorPane();
pane.setPadding(new Insets(10, 10, 10, 10));
Reference reference1 = new Reference();
reference1.setPadding(Insets.EMPTY);
reference1.setLayoutX(100);
reference1.setLayoutY(50);
pane.getChildren().add(reference1);
Reference reference2 = new Reference();
reference2.setPadding(Insets.EMPTY);
reference2.setLayoutX(100);
reference2.setLayoutY(80);
pane.getChildren().add(reference2);
Scene scene = new Scene(pane, 400, 200);
scene.setFill(null);
stage.setScene(scene);
stage.setTitle("ReferenceApplication");
stage.show();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
reference2.setActive(!reference2.isActive());
}
};
new Timer().scheduleAtFixedRate(timerTask, 5000, 500);
}
}
reference.css
.reference {
-stroke-color: BLACK;
-stroke-width: 25.0;
}
.reference:active {
-stroke-color: GREEN;
}
如果我删除 strokeColor 和 strokeWidth 属性并改用以下 CSS 改变状态确实会改变笔触颜色。
.reference > Line {
-fx-stroke: BLACK;
-fx-stroke-width: 25.0;
}
.reference:active > Line {
-fx-stroke: GREEN;
}
但是我不希望那些使用自定义控件的人必须知道内部表示是一条线。也许我想在未来改变它。因此,我创建了自己的 CSS 属性。
您只能在创建皮肤时应用 color/stoke 宽度。
您需要 listen/bind 到可设置样式的属性:
strokeColorProperty = control.strokeColorProperty();
strokeWidthProperty = control.strokeWidthProperty();
我创建了一个带有两个 CSS 样式属性和一个 CSS 状态伪类的 JavaFX 自定义控件。
我不确定我遗漏了什么,但更改状态不会更新描边颜色。
Reference.java
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.ObjectProperty;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;
import javafx.css.Styleable;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.css.StyleablePropertyFactory;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.scene.paint.Color;
public class Reference extends Control {
private static final StyleablePropertyFactory<Reference> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData());
private static final CssMetaData<Reference, Color> STROKE_COLOR = FACTORY.createColorCssMetaData("-stroke-color", s -> s.strokeColor, Color.BLACK, false);
private final StyleableProperty<Color> strokeColor;
private static final CssMetaData<Reference, Number> STROKE_WIDTH = FACTORY.createSizeCssMetaData("-stroke-width", s -> s.strokeWidth, Double.valueOf(5.0), false);
private final StyleableProperty<Number> strokeWidth;
private static final PseudoClass ACTIVE_PSEUDO_CLASS = PseudoClass.getPseudoClass("active");
private BooleanProperty active;
private boolean activeValue = false;
public Reference() {
getStyleClass().add("reference");
strokeColor = new StyleableObjectProperty<Color>(STROKE_COLOR.getInitialValue(Reference.this)) {
@Override protected void invalidated() {}
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "strokeColor"; }
@Override public CssMetaData<? extends Styleable, Color> getCssMetaData() { return STROKE_COLOR; }
};
// strokeColor = new SimpleStyleableObjectProperty<>(STROKE_COLOR, this, "strokeColor", STROKE_COLOR.getInitialValue(Reference.this));
strokeWidth = new StyleableObjectProperty<Number>(STROKE_WIDTH.getInitialValue(Reference.this)) {
@Override protected void invalidated() {}
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "strokeWidth"; }
@Override public CssMetaData<? extends Styleable, Number> getCssMetaData() { return STROKE_WIDTH; }
};
}
public final boolean isActive() {
return null == active ? activeValue : active.get();
}
public final void setActive(final boolean active) {
activeProperty().set(active);
}
public final BooleanProperty activeProperty() {
if (null == active) {
active = new BooleanPropertyBase(activeValue) {
@Override protected void invalidated() { pseudoClassStateChanged(ACTIVE_PSEUDO_CLASS, get()); }
@Override public Object getBean() { return Reference.this; }
@Override public String getName() { return "active"; }
};
}
return active;
}
public Color getStrokeColor() { return strokeColor.getValue(); }
public void setStrokeColor(final Color color) { strokeColor.setValue(color); }
public ObjectProperty<Color> strokeColorProperty() { return (ObjectProperty<Color>) strokeColor; }
public Double getStrokeWidth() { return strokeWidth.getValue().doubleValue(); }
public void setStrokeColor(final Double number) { strokeWidth.setValue(number); }
public ObjectProperty<Number> strokeWidthProperty() { return (ObjectProperty<Number>) strokeWidth; }
/* (non-Javadoc)
* @see javafx.scene.control.Control#createDefaultSkin()
*/
@Override
protected Skin<?> createDefaultSkin() {
return new ReferenceSkin(this);
}
/* (non-Javadoc)
* @see javafx.scene.layout.Region#getUserAgentStylesheet()
*/
@Override
public String getUserAgentStylesheet() {
return getClass().getResource(getClass().getSimpleName().toLowerCase() + ".css").toExternalForm();
}
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return FACTORY.getCssMetaData();
}
/* (non-Javadoc)
* @see javafx.scene.control.Control#getControlCssMetaData()
*/
@Override
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
return getClassCssMetaData();
}
}
ReferenceSkin.java
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.SkinBase;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
public class ReferenceSkin extends SkinBase<Reference> {
private ObjectProperty<Paint> strokeColorProperty;
private ObjectProperty<Number> strokeWidthProperty;
public ReferenceSkin(Reference control) {
super(control);
strokeColorProperty = new SimpleObjectProperty<>(control.getStrokeColor());
strokeWidthProperty = new SimpleObjectProperty<>(control.getStrokeWidth());
initGraphics();
registerListeners();
}
/**
* Initialize graphics.
*/
private void initGraphics() {
final double startX = 0.0;
final double startY = 0.0;
final double endX = 200.;
final double endY = 0.0;
final Line line = new Line(startX, startY, endX, endY);
line.strokeProperty().bind(strokeColorProperty);
line.strokeWidthProperty().bind(strokeWidthProperty);
getChildren().add(line);
}
private void registerListeners() {
getSkinnable().activeProperty().addListener(observable -> handleControlPropertyChanged("ACTIVE"));
}
protected void handleControlPropertyChanged(final String PROPERTY) {
if ("ACTIVE".equals(PROPERTY)) {
if (getSkinnable().isActive()) {
System.out.println("isActive");
} else {
System.out.println("isInactive");
}
}
}
/* (non-Javadoc)
* @see javafx.scene.control.SkinBase#layoutChildren(double, double, double, double)
*/
@Override
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
for (Node child : getChildren()) {
if (child.isManaged()) {
layoutInArea(child, contentX, contentY, contentWidth, contentHeight, 10, HPos.RIGHT, VPos.TOP);
}
}
}
}
ReferenceApplication.java
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class ReferenceApplication extends Application {
public static void main(String[] args) {
ReferenceApplication.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
AnchorPane pane = new AnchorPane();
pane.setPadding(new Insets(10, 10, 10, 10));
Reference reference1 = new Reference();
reference1.setPadding(Insets.EMPTY);
reference1.setLayoutX(100);
reference1.setLayoutY(50);
pane.getChildren().add(reference1);
Reference reference2 = new Reference();
reference2.setPadding(Insets.EMPTY);
reference2.setLayoutX(100);
reference2.setLayoutY(80);
pane.getChildren().add(reference2);
Scene scene = new Scene(pane, 400, 200);
scene.setFill(null);
stage.setScene(scene);
stage.setTitle("ReferenceApplication");
stage.show();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
reference2.setActive(!reference2.isActive());
}
};
new Timer().scheduleAtFixedRate(timerTask, 5000, 500);
}
}
reference.css
.reference {
-stroke-color: BLACK;
-stroke-width: 25.0;
}
.reference:active {
-stroke-color: GREEN;
}
如果我删除 strokeColor 和 strokeWidth 属性并改用以下 CSS 改变状态确实会改变笔触颜色。
.reference > Line {
-fx-stroke: BLACK;
-fx-stroke-width: 25.0;
}
.reference:active > Line {
-fx-stroke: GREEN;
}
但是我不希望那些使用自定义控件的人必须知道内部表示是一条线。也许我想在未来改变它。因此,我创建了自己的 CSS 属性。
您只能在创建皮肤时应用 color/stoke 宽度。
您需要 listen/bind 到可设置样式的属性:
strokeColorProperty = control.strokeColorProperty();
strokeWidthProperty = control.strokeWidthProperty();