2014-07-11 7 views
6

क्या एक स्ट्रिंग में एक संपूर्ण स्टाइलशीट लपेटना संभव है और इसे किसी निश्चित नोड पर लागू करना संभव है? उपयोग केस छद्म क्लास के लिए विशिष्ट (गैर परिवर्तनीय) व्यवहार जोड़ना होगा। मुझे पता है कि मैं pane.getStylesheets().add(getClass().getResource("mycss.css").toExternalForm()); का उपयोग कर सकता हूं, लेकिन मैं जानना चाहता हूं कि स्रोत में इसे सख्ती से बांधने का कोई तरीका है या नहीं; लाइनों के साथ कुछ:जावाएफएक्स 8 में क्या मैं स्ट्रिंग से स्टाइलशीट प्रदान कर सकता हूं?

pane.getStylesheets().add(
    ".button:ok { -fx-background-color: green; }\n"+ 
    ".button:ko { -fx-background-color: red; }"); 

उत्तर

8

मैं एक नया 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; 
    } 
} 

जाहिर प्रोटोकॉल "आंतरिक" किसी भी (गैर टकरा) हो सकता है अच्छी तरह से गठित स्ट्रिंग और (इस सरल उदाहरण में) filepath है मजबूती से अनदेखा किया।

मैं वैश्विक .css सेट करने के लिए इसका उपयोग करता हूं, इसलिए मुझे एकाधिक तारों को याद रखने की आवश्यकता नहीं है। ऐसा लगता है कि स्ट्रीम सिर्फ एक बार खोला गया है, लेकिन मुझे नहीं पता कि यह सभी मामलों में सच है या नहीं।

कोड को मुश्किल करने के लिए स्वतंत्र रूप में की जरूरत महसूस;)

क्रेडिट के लिए इस विधि जैस्पर पॉट्स को जाता है (see this example)

+0

यदि आप फ्रेमवर्क स्तर कोड लिख रहे हैं और यूआरएल स्ट्रीम हैंडलर फैक्ट्री के एकमात्र संभावित ओवरराइड का उपयोग नहीं करना चाहते हैं, तो हल्का वजन विधि के लिए मेरा उत्तर नीचे देखें जो स्थिर कारखाने को ओवरराइड नहीं करेगा। यदि आपको स्ट्रीम फैक्ट्री सेट करने का प्रयास करने का अपवाद मिलता है, तो आपके पास पहले से ही कुछ अन्य कोड हो चुके हैं, और आपको यूआरएल क्लास के स्रोत में मिले आंतरिक "सर्विस लोडर" सेमेन्टिक्स का उपयोग करना होगा। यह आपको एक स्ट्रीम फैक्ट्री बनाने के बिना एक हैंडलर पंजीकृत करने देता है। – Ajax

+0

मुझे लगता है कि मूल पोस्टर जिसने अपना उत्तर चुना है, शायद इसे नीचे दिए गए समाधान में नहीं बदलेगा, लेकिन किसी भी व्यक्ति के लिए जो लाइब्रेरी या फ्रेमवर्क लिख रहा है, वास्तव में, वास्तव में, वास्तव में डिफ़ॉल्ट यूआरएल स्ट्रीम हैंडलर को ओवरराइड नहीं करना चाहिए ; इसके बजाय, आपको यूआरएल क्लास द्वारा उपयोग किए जाने वाले सेवा प्रदाता ढांचे का उपयोग करना चाहिए। यूआरएल के दस्तावेज़ीकरण को पढ़ें और आप अपने लिए देखेंगे कि आपको डिफ़ॉल्ट स्ट्रीम हैंडलर को ओवरराइड करने की आवश्यकता नहीं है (और कभी नहीं होना चाहिए)। – Ajax

2

मैंने प्रलेखन को देखा और मुझे ऐसा करने का एक अंतर्निहित तरीका नहीं दिख रहा है। getStylesheetsParent में एकमात्र स्टाइलशीट-संबंधित विधि है, और यह केवल "स्टाइलशीट से लिंक करने वाले स्ट्रिंग यूआरएल" स्वीकार करता है, स्टाइलशीट स्वयं नहीं। यह एक सामान्य ObservableList देता है, इसलिए इसके वापसी मूल्य में विभिन्न प्रकार के लिए कोई विशेष तरीका नहीं है; केवल एक सामान्य add। यह getResourceURL लौटा रहा है, और toExternalForm() केवल उस URL ऑब्जेक्ट का एक स्ट्रिंग संस्करण लौटा रहा है।

हालांकि, एक चीज है जिसे आप आजमा सकते हैं: data URI। एक स्टाइलशीट फ़ाइल में जेनरेट किए गए यूआरआई में गुजरने के बजाय, डेटा यूआरआई में पास करें, जिनकी सामग्री स्टाइलशीट है। मैं अगर एपीआई यूआरआई उस तरह स्वीकार करेंगे पता नहीं है, हालांकि, यह देखते हुए CSS Reference GuidegetStylesheets के दस्तावेज में जुड़े हुए कहता है कि

एक शैली पत्रक यूआरएल एक पूर्ण URL या सापेक्ष URL हो सकता है।

यह देखने के लिए कि क्या यह काम करता है, वास्तव में एक साधारण डेटा यूआरआई आज़माएं। आप this online tool का उपयोग कर एक उत्पन्न कर सकते हैं। जावा एक डेटा यूआरआई स्वीकार करता है, तो आप बस कुछ विधि कॉल कि एक डेटा URI का एक स्ट्रिंग में कनवर्ट करता है, कुछ इस तरह के साथ अपने सीएसएस युक्त स्ट्रिंग रैप करने के लिए की जरूरत है:

pane.getStylesheets().add(new DataURI(
    ".button:ok { -fx-background-color: green; }\n"+ 
    ".button:ko { -fx-background-color: red; }").toString()); 

वर्ग DataURI काल्पनिक है। यदि जावाएफएक्स मैन्युअल रूप से जेनरेट किए गए डेटा यूआरआई को स्वीकार करता है, तो आपको एक लाइब्रेरी मिलनी होगी जो DataURI स्वयं को कक्षा प्रदान करेगी; मुझे यकीन है कि कोई कहीं मौजूद है।

Node के लिए String के रूप में इनलाइन सीएसएस निर्दिष्ट करने का एक तरीका भी है, जो लगभग आप जो खोज रहे हैं। इसका उल्लेख CSS Reference Guide:

सीएसएस शैलियों स्टाइल शीट या इनलाइन शैलियों से आ सकता है। स्टाइल शीट को दृश्य ऑब्जेक्ट के stylesheets चर में निर्दिष्ट यूआरएल से लोड किया जाता है। यदि दृश्य ग्राफ़ में नियंत्रण होता है, तो एक डिफ़ॉल्ट उपयोगकर्ता एजेंट स्टाइल शीट लोड होती है। इनलाइन शैलियों को नोड setStyle एपीआई के माध्यम से निर्दिष्ट किया जाता है। इनलाइन शैलियों style="…" एचटीएमएल तत्व की विशेषता के समान हैं।

हालांकि, यह लग रहा है जैसे कि यह सीएसएस में चयनकर्ताओं, केवल नियमों का समर्थन नहीं करता - तो बल्कि .red { color: red; } कह से, आप केवल color: red; लिखने में सक्षम हो जाएगा, और यह कि Node के सभी बच्चों पर लागू होगा। यह आपकी इच्छा की तरह नहीं लगता है। तो एक यूआरआई एकमात्र आशा है।


संपादित करें: हालांकि यह एक स्मार्ट विचार है (मुझे पहले डेटा यूआरआई के बारे में पता नहीं था) यह काम नहीं करता है। मेरे पास एक ही आवश्यकता है इसलिए मैंने कोशिश की।यह एक अपवाद बढ़ा नहीं करता है लेकिन वहाँ लॉग में एक चेतावनी है और शैली लागू नहीं कर रहे हैं: का उपयोग कर प्रदान की उपकरण निम्न डेटा यूआरआई उत्पन्न

.root{ 
    -fx-font-family: "Muli"; 
    -fx-font-weight: lighter; 
    -fx-font-size: 35pt; 
    -fx-padding: 0; 
    -fx-spacing: 0; 
} 

और:

मैं इस शैली का इस्तेमाल: (क्षमा में

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"); 

परिणाम:

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 डेटा यूआरआई के बारे में पता नहीं हो रहा है।

+0

हाय दोस्तों। मेरा उत्तर नीचे यूआरएल स्ट्रीम हैंडलर सेवा प्रदाता का लाभ लेता है ताकि 'सीएसएस:/ब्लाह' यूआरएल के लिए समर्थन जोड़ने के लिए जो आप चाहते हैं ... डिफ़ॉल्ट स्ट्रीम फैक्ट्री को ओवरराइड किए बिना। – Ajax

2

यहाँ (ZioBytre के जवाब के आधार पर मेरे सीएसएस अपडेटर वर्ग है +1 काम करता है बहुत अच्छी तरह से) ।

यह एक स्वयं निहित वर्ग है जिसे आसानी से एक परियोजना में कॉपी किया जा सकता है और इसका उपयोग किया जा सकता है।

String पर आधारित Stream लौटने के लिए यह कॉमन्स IO IOUtils कक्षा पर निर्भरता है। लेकिन यदि आवश्यक हो तो इसे आसानी से रेखांकित किया जा सकता है या किसी अन्य लाइब्रेरी द्वारा प्रतिस्थापित किया जा सकता है।

मैं इस कक्षा का उपयोग ऐसे प्रोजेक्ट में करता हूं जहां सीएसएस अनुप्रयोग के अंदर गतिशील रूप से संपादन योग्य होता है, सर्वर पक्ष पर, और जावाएफएक्स क्लाइंट को धक्का देता है। इसका उपयोग किसी भी परिदृश्य में किया जा सकता है जहां सीएसएस स्ट्रिंग किसी फ़ाइल या यूआरएल से नहीं आती है लेकिन किसी अन्य स्रोत से (सर्वर ऐप, डेटाबेस, उपयोगकर्ता इनपुट ...)

इसमें स्ट्रिंग प्रॉपर्टी को बांधने का एक तरीका है ताकि जैसे ही वे होते हैं, सीएसएस परिवर्तन स्वचालित रूप से लागू हो जाएंगे।

/** 
* 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())); 
+0

धन्यवाद, मैंने ज़ियोबाइट के काम पर छोड़ दिया था - जो अपूर्ण था। – javadba

+0

क्या किसी ने इसे वास्तव में काम करने के लिए प्राप्त किया है? – Zephyr

+0

हाय दोस्तों। डिफ़ॉल्ट URL स्ट्रीम हैंडलर को ओवरराइड किए बिना, जो मैं उत्पादन में उपयोग करता हूं, के लिए नीचे दिए गए मेरे उत्तर को देखें (जो संभावित रूप से अन्य निर्भरताओं को खराब कर सकता है)। – Ajax

1

किसी को जो ढांचा स्तर कोड लिख रहा है है कि वैश्विक, स्थैतिक यूआरएल धारा कारखाने के एक और केवल ओवरराइड ऊपर उपयोग नहीं करना चाहते, तो आप के बजाय में टाई कर सकते हैं यूआरएल कक्षा में आंतरिक "सेवा लोडर" ढांचा।

ऐसा करने के लिए, आपको Handler extends URLStreamHandler नामक एक श्रेणी बनाना होगा और उस श्रेणी के पैकेज को इंगित करने के लिए सिस्टम प्रॉपर्टी java.protocol.handler.pkgs अपडेट करें, अंतिम पैकेज प्रत्यय को घटाएं। तो, com.fu.css संपत्ति को कॉम पर सेट करेगा।फू, तो सभी css:my/path अनुरोध इस हैंडलर के लिए मार्ग होंगे।

मैं उस वर्ग को पेस्ट करूँगा जिसका मैं नीचे उपयोग कर रहा हूं; अजीब संग्रह और आपूर्तिकर्ता इंटरफेस माफ कर दो; आप अनुमान लगा सकते हैं कि ये क्या करते हैं और बिना किसी परेशानी के मानक मानक के साथ उन्हें प्रतिस्थापित करते हैं।

package xapi.jre.ui.css; 

import xapi.collect.X_Collect; 
import xapi.collect.api.CollectionOptions; 
import xapi.collect.api.StringTo; 
import xapi.fu.Out1; 
import xapi.io.X_IO; 

import java.io.IOException; 
import java.io.InputStream; 
import java.net.URL; 
import java.net.URLConnection; 
import java.net.URLStreamHandler; 
import java.nio.charset.Charset; 

/** 
* I abhor the name of this class, 
* but it must be called "Handler" in order for java.net.URL to be able to find us. 
* 
* It sucks, but it's not our api, and it's the only way to get dynamic stylesheets in JavaFx, 
* short of overriding the url stream handler directly (and this can only be done once in a single 
* JVM, and as framework-level code, it is unacceptable to prevent clients from choosing to 
* override the stream handler themselves). 
* 
* Created by James X. Nelson (james @wetheinter.net) on 8/21/16. 
*/ 
public class Handler extends URLStreamHandler { 

    private static final StringTo<Out1<String>> dynamicFiles; 
    static { 
     // Ensure that we are registered as a url protocol handler for css:/path css files. 
     String was = System.getProperty("java.protocol.handler.pkgs", ""); 
     System.setProperty("java.protocol.handler.pkgs", Handler.class.getPackage().getName().replace(".css", "") + 
      (was.isEmpty() ? "" : "|" + was)); 
     dynamicFiles = X_Collect.newStringMap(Out1.class, 
      CollectionOptions.asConcurrent(true) 
       .mutable(true) 
       .insertionOrdered(false) 
      .build()); 
    } 

    public static void registerStyleSheet(String path, Out1<String> contents) { 
     dynamicFiles.put(path, contents); 
    } 

    @Override 
    protected URLConnection openConnection(URL u) throws IOException { 
     final String path = u.getPath(); 
     final Out1<String> file = dynamicFiles.get(path); 
     return new StringURLConnection(u, file); 
    } 

    private static class StringURLConnection extends URLConnection { 
     private final Out1<String> contents; 

     public StringURLConnection(URL url, Out1<String> contents){ 
      super(url); 
      this.contents = contents; 
     } 

     @Override 
     public void connect() throws IOException {} 

     @Override public InputStream getInputStream() throws IOException { 
      return X_IO.toStream(contents.out1(), Charset.defaultCharset().name()); 
     } 
    } 
} 

अब, किसी भी कोड (, "मेरे/मार्ग"() -> "* {-fx-सीएसएस: blah}") Handler.registerStylesheet कॉल कर सकते हैं ;, और आप के माध्यम से "कहीं भी इस स्टाइलशीट का उपयोग कर सकते सीएसएस: मेरे/पथ "।

ध्यान दें कि मैं केवल यूआरएल के पथ हिस्से को देख रहा हूं; मैं गतिशीलता को आगे बढ़ाने के लिए क्वेरी पैरामीटर का लाभ उठाने का इरादा रखता हूं (एक सीएसएस कारखाने का उपयोग करके जो पैरामीटर के मानचित्र को स्वीकार करता है), लेकिन यह इस प्रश्न के दायरे से बाहर है।

संबंधित मुद्दे