在JavaFX 8中,我可以从String中提供样式表吗?

Zio*_*yte 8 css resources stylesheet embedded-resource javafx-8

是否可以将整个样式表包装在字符串中并将其应用于某个节点?用例是为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; }");
Run Code Online (Sandbox Code Playgroud)

Zio*_*yte 10

我通过定义新的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,协议"内部"可以是任何(非冲突)格式良好的字符串,并且(在这个简单的示例中)文件路径被强制忽略.

我用它来设置全局.css,所以我不需要记住多个字符串.似乎Stream只打开了一次,但我不知道在所有情况下是否都适用.

随意根据需要使代码复杂化;)

这种方法可以归功于Jasper Potts(参见本例)

  • 我想选择自己答案的原始海报可能不会将其更改为我在下面留下的解决方案,但是对于正在编写库或框架的任何人,您真的,真的,真的不应该覆盖默认的 url 流处理程序;相反,您应该使用 URL 类本身使用的服务提供者框架。阅读 URL 的文档,您将亲眼看到您不必(也不应该)覆盖默认的流处理程序。 (2认同)

Pie*_*nry 5

这是我基于ZioBytre答案的CSS更新器类(+1效果很好)。

这是一个自包含类,可以轻松将其复制到项目并按原样使用。

它依赖于commons IO IOUtils类以返回Stream基于的a 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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()));
Run Code Online (Sandbox Code Playgroud)