是否可以将整个样式表包装在字符串中并将其应用于某个节点?
用例是为PseudoClass添加特定(非变化)行为。
我知道我可以使用pane.getStylesheets().add(getClass().getResource("mycss.css").toExternalForm());
,但我想知道是否有某种方法可以直接将其嵌入源代码中;一些事情:
pane.getStylesheets().add(
".button:ok { -fx-background-color: green; }\n"+
".button:ko { -fx-background-color: red; }");
答案 0 :(得分:9)
我通过定义新的URL连接找到了一种方法:
private String css;
public void initialize() {
...
// to be done only once.
URL.setURLStreamHandlerFactory(new StringURLStreamHandlerFactory());
...
}
private void updateCss(Node node) {
// can be done multiple times.
css = createCSS();
node.getStylesheets().setAll("internal:"+System.nanoTime()+"stylesheet.css");
}
private class StringURLConnection extends URLConnection {
public StringURLConnection(URL url){
super(url);
}
@Override public void connect() throws IOException {}
@Override public InputStream getInputStream() throws IOException {
return new StringBufferInputStream(css);
}
}
private class StringURLStreamHandlerFactory implements URLStreamHandlerFactory {
URLStreamHandler streamHandler = new URLStreamHandler(){
@Override protected URLConnection openConnection(URL url) throws IOException {
if (url.toString().toLowerCase().endsWith(".css")) {
return new StringURLConnection(url);
}
throw new FileNotFoundException();
}
};
@Override public URLStreamHandler createURLStreamHandler(String protocol) {
if ("internal".equals(protocol)) {
return streamHandler;
}
return null;
}
}
显然协议“internal”可以是任何(非碰撞)格式良好的字符串,并且(在这个简单的示例中)文件路径被强制忽略。
我用它来设置全局.css,所以我不需要记住多个字符串。 似乎Stream只打开了一次,但我不知道在所有情况下是否都适用。
根据需要随意使代码复杂化;)
此方法可归功于Jasper Potts(see this example)
答案 1 :(得分:3)
这是我的基于ZioBytre答案的CSS更新程序类(+1效果非常好)。
这是一个自包含的类,可以轻松复制到项目中并按原样使用。
它依赖于公共IO IOUtils
类来基于Stream
返回String
。但如果需要,这可以很容易地被内联或替换为另一个库。
我在一个项目中使用此类,其中CSS可在应用程序内部,服务器端动态编辑,并推送到JavaFX客户端。它可用于CSS字符串不是来自文件或URL而是来自其他来源(服务器应用程序,数据库,用户输入......)的任何场景。
它有一个绑定字符串属性的方法,以便CSS更改一旦发生就会自动应用。
/**
* Class that handles the update of the CSS on the scene or any parent.
*
* Since in JavaFX, stylesheets can only be loaded from files or URLs, it implements a handler to create a magic "internal:stylesheet.css" url for our css string
* see : https://github.com/fxexperience/code/blob/master/FXExperienceTools/src/com/fxexperience/tools/caspianstyler/CaspianStylerMainFrame.java
* and : http://stackoverflow.com/questions/24704515/in-javafx-8-can-i-provide-a-stylesheet-from-a-string
*/
public class FXCSSUpdater {
// URL Handler to create magic "internal:stylesheet.css" url for our css string
{
URL.setURLStreamHandlerFactory(new StringURLStreamHandlerFactory());
}
private String css;
private Scene scene;
public FXCSSUpdater(Scene scene) {
this.scene = scene;
}
public void bindCss(StringProperty cssProperty){
cssProperty.addListener(e -> {
this.css = cssProperty.get();
Platform.runLater(()->{
scene.getStylesheets().clear();
scene.getStylesheets().add("internal:stylesheet.css");
});
});
}
public void applyCssToParent(Parent parent){
parent.getStylesheets().clear();
scene.getStylesheets().add("internal:stylesheet.css");
}
/**
* URLConnection implementation that returns the css string property, as a stream, in the getInputStream method.
*/
private class StringURLConnection extends URLConnection {
public StringURLConnection(URL url){
super(url);
}
@Override
public void connect() throws IOException {}
@Override public InputStream getInputStream() throws IOException {
return IOUtils.toInputStream(css);
}
}
/**
* URL Handler to create magic "internal:stylesheet.css" url for our css string
*/
private class StringURLStreamHandlerFactory implements URLStreamHandlerFactory {
URLStreamHandler streamHandler = new URLStreamHandler(){
@Override
protected URLConnection openConnection(URL url) throws IOException {
if (url.toString().toLowerCase().endsWith(".css")) {
return new StringURLConnection(url);
}
throw new FileNotFoundException();
}
};
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
if ("internal".equals(protocol)) {
return streamHandler;
}
return null;
}
}
}
用法:
StringProperty cssProp = new SimpleStringProperty(".root {-fx-background-color : red}");
FXCSSUpdater updater = new FXCSSUpdater(scene);
updater.bindCss(cssProp);
//new style will be applied to the scene automatically
cssProp.set(".root {-fx-background-color : green}");
//manually apply css to another node
cssUpdater.applyCssToParent(((Parent)popover.getSkin().getNode()));
答案 2 :(得分:2)
我查看了文档,但我没有看到内置的方法。 getStylesheets
是Parent
中唯一与样式表相关的方法,它只接受“链接到样式表的字符串URL”,而不是样式表本身。它返回一个通用的ObservableList
,因此它的返回值没有针对不同类型的特殊方法;只有通用add
。这与getResource
返回URL
一致,而toExternalForm()
只返回该网址对象的字符串版本。
但是,您可以尝试一件事:data URI。不是将生成的URI传递给样式表文件,而是传入其内容为样式表的数据URI。我不知道API是否会接受这种URI,因为getStylesheets
文档中链接的CSS Reference Guide说明
样式表网址可以是绝对网址或相对网址。
首先尝试一个非常简单的数据URI,看它是否有效。您可以使用this online tool生成一个。如果Java确实接受了数据URI,那么您只需要将包含CSS的String包含一些将String转换为数据URI的方法调用,如下所示:
pane.getStylesheets().add(new DataURI(
".button:ok { -fx-background-color: green; }\n"+
".button:ko { -fx-background-color: red; }").toString());
班级DataURI
是假设的。如果JavaFX接受手动生成的数据URI,那么您必须找到一个自己提供DataURI
类的库;我确定某个地方存在。
还有一种方法可以将某个Node
的内联CSS指定为String
,这几乎就是您要查找的内容。它在CSS Reference Guide中提到:
CSS样式可以来自样式表或内联样式。样式表是从Scene对象的
stylesheets
变量中指定的URL加载的。如果场景图包含Control,则会加载默认的用户代理样式表。内联样式通过NodesetStyle
API指定。内联样式类似于HTML元素的style="…"
属性。
然而,听起来它不支持CSS中的选择器,只支持规则 - 所以不是说.red { color: red; }
,而是只能写color: red;
,它适用于所有孩子那个Node
。这听起来不像你想要的。所以数据URI是你唯一的希望。
我用过这种风格:
.root{
-fx-font-family: "Muli";
-fx-font-weight: lighter;
-fx-font-size: 35pt;
-fx-padding: 0;
-fx-spacing: 0;
}
使用提供的工具生成以下数据URI:
data:text/css;charset=utf-8,.root%7B%0D%0A%20%20%20%20-fx-font-family%3A%20%22Muli%22%3B%0D%0A%20%20%20%20-fx-font-weight%3A%20lighter%3B%0D%0A%20%20%20%20-fx-font-size%3A%2035pt%3B%0D%0A%20%20%20%20-fx-padding%3A%200%3B%0D%0A%20%20%20%20-fx-spacing%3A%200%3B%0D%0A%7D
将它应用到我的场景中:
scene.getStylesheets().add("data:text/css;charset=utf-8,.root%7B%0D%0A%20%20%20%20-fx-font-family%3A%20%22Muli%22%3B%0D%0A%20%20%20%20-fx-font-weight%3A%20lighter%3B%0D%0A%20%20%20%20-fx-font-size%3A%2035pt%3B%0D%0A%20%20%20%20-fx-padding%3A%200%3B%0D%0A%20%20%20%20-fx-spacing%3A%200%3B%0D%0A%7D");
结果(原谅我的法语,AVERTISSEMENT =警告):
janv. 07, 2015 12:02:03 PM com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged
AVERTISSEMENT: Resource "data:text/css;charset=utf-8,%23header%7B%0D%0A%20%20%20%20-fx-background-color%3A%23002D27%3B%0D%0A%20%20%20%20-fx-font-size%3A%2035pt%3B%0D%0A%20%20%20%20-fx-text-fill%3A%20%23fff%3B%0D%0A%7D" not found.
很遗憾,JavaFX似乎并不了解数据URI。
答案 3 :(得分:1)
对于正在编写不想用尽全局静态url流工厂的唯一覆盖框架级代码的人来说,你可以改为绑定到内部服务加载器" URL类本身的框架。
为此,您必须创建一个名为getWaveform()
的类,并更新系统属性Handler extends URLStreamHandler
以指向该类的包,减去最终的包后缀。因此,java.protocol.handler.pkgs
会将该属性设置为com.fu,然后所有com.fu.css
请求都将路由到此处理程序。
我将粘贴我正在使用的课程;原谅奇怪的收藏品和供应商界面;你可以猜出它们做了什么,并用标准实用程序替换它们而没有太多麻烦。
css:my/path
现在,任何代码都可以调用Handler.registerStylesheet(" my / path",() - >" * {-fx-css:blah}");和您可以通过" css:my / path"在任何地方使用此样式表。
请注意,我只查看网址的路径部分;我打算利用查询参数来进一步增加动态(通过使用接受参数映射的css工厂),但这超出了这个问题的范围。