Môžete použiť prúd JEditorPane
komponent na zobrazenie značiek HTML, ale na vykonávanie zložitejších úloh, JEditorPane
potrebuje nejaké vylepšenie. Nedávno som musel zostaviť aplikáciu na vytváranie formulárov XML. Jedným z nevyhnutných komponentov bol editor WYSIWYG HTML, ktorý dokázal upravovať obsah značkovania HTML vo vnútri niektorých značiek XML. JEditorPane
bola zrejmá voľba komponentu Java na zobrazovanie značiek HTML, pretože táto funkcia už bola v ňom zabudovaná. Po vložení do značky HTML, JEditorPane
nemohol zobraziť obrázky s relatívnymi cestami. Ak by bol napríklad nasledujúci obrázok s relatívnou cestou obsiahnutý v značke XML, nezobrazil by sa správne:
Naopak, absolútna cesta by fungovala (za predpokladu, že daná cesta a obraz skutočne existovali):
V mojej aplikácii boli obrázky vždy uložené v podadresári relatívnom k umiestneniu súboru XML. Preto som vždy chcel použiť relatívnu cestu. V tomto článku bude vysvetlené, prečo tento problém existuje a ako ho vyriešiť.
Prečo sa to stalo?
Keď sa pozrieme bližšie na konštruktérov pre JEditorPane
pomôže nám pochopiť, prečo nemôže zobrazovať obrázky na relatívnych cestách.
JEditorPane ()
vytvára novýJEditorPane
.JEditorPane (adresa URL reťazca)
vytvára aJEditorPane
na základe reťazca obsahujúceho špecifikáciu adresy URL.JEditorPane (typ reťazca, text reťazca)
vytvára aJEditorPane
ktorý bol inicializovaný pre daný text.JEditorPane (URL initialPage)
vytvára aJEditorPane
na základe zadanej adresy URL pre vstup.
Druhý a štvrtý konštruktér inicializujú objekt s odkazom na vzdialený alebo lokálny súbor HTML. An HTMLDocument
je vo vnútri každého JEditorPane
a jeho základňa je nastavená na základňu parametra konštruktora adresy URL. JEditorPane
s vytvorené pomocou týchto konštruktorov zvládne relatívne cesty, pretože základňa HTMLDocument
kombinuje s relatívnou cestou a vytvára absolútnu cestu.
Ak sa použije prvý konštruktor, musí sa po vytvorení objektu vložiť zobrazený text. Tretí konštruktér prijíma a String
ako obsah, ale základňa nie je inicializovaná. Pretože som chcel získať značky HTML zo značky XML a nie zo súboru, musel som použiť prvý alebo tretí konštruktor.
Ako odstránime problém?
Než budem pokračovať, poďme odhaliť a vyriešiť ďalší menší problém. Najzrejmejší spôsob vloženia značiek do súboru JEditorPane
je použitie setText (text reťazca)
. Táto metóda však vyžaduje, aby ste pri každej zmene zadali celé zobrazené označenie. V ideálnom prípade by mali byť nové značky vložené do existujúceho textu. Na pridanie nového označenia môžete použiť nasledujúci kód:
private void insertHTML (editor JEditorPane, reťazec html, int umiestnenie) vyvolá IOException {// predpokladá, že editor je už nastavený na „text / html“ typ HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit (); Dokument doc = editor.getDocument (); Čítačka StringReader = nový StringReader (html); kit.read (čitateľ, dokument, umiestnenie); }
Teraz sa dostaneme k jadru veci: Ako to ide JEditorPane
vykresliť HTML? Každý typ JEditorPane
referencie obidva a Dokument
a an EditorKit
. Kedy JEditorPane
je nastavený na typ "text / html", obsahuje HTMLDocument
, ktorý obsahuje označenie a HTMLEditorKit
ktorá určuje, ktoré triedy vykresľujú každú značku obsiahnutú v značke. Konkrétne HTMLEditorKit
trieda obsahuje HTMLFactory
vnútorná trieda ktorého vytvoriť (prvok elem)
metóda vlastne skúma každú samostatnú značku. Tu je kód z tejto továrenskej triedy, ktorý narába so značkami obrázkov:
else if (kind == HTML.Tag.IMG) return new ImageView (elem);
Ako teraz vidíte, ImageView
trieda skutočne načíta obrázok. Ak chcete zistiť polohu obrázka, kliknite na ikonu getSourceURL ()
metóda sa volá:
súkromná URL getSourceURL () {String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); if (src == null) return null; Odkaz na URL = ((HTMLDocument) getDocument ()). getBase (); skúsiť {URL u = nová URL (referencia, src); návrat u; } catch (MalformedURLException e) {return null; }}
Tu je getSourceURL ()
metóda sa pokúsi vytvoriť novú URL na odkazovanie na obrázok pomocou HTMLDocument
základňa. Ak má táto základňa hodnotu null, vráti sa hodnota null a operácia načítania obrazu sa preruší. Chcete prepísať toto správanie.
V ideálnom prípade by ste podtriedu ImageView
triedy a prepísať inicializovať (prvok elem)
metóda, pri ktorej sa načítanie obrazu vykonáva. Bohužiaľ, táto trieda je balík chránený, takže musíte vytvoriť úplne novú triedu. Najjednoduchší spôsob, ako to urobiť, je požičať si a potom upraviť kód z originálu ImageView
trieda. Nazvime to MyImageView
.
Najskôr sa pozrite na kód, ktorý načítal obrázok. Nasledujúce informácie sú prevzaté z inicializovať (prvok elem)
metóda:
URL src = getSourceURL (); if (src! = null) {cache slovníka = (slovník) getDocument (). getProperty (IMAGE_CACHE_PROPERTY); if (cache! = null) fImage = (Obrázok) cache.get (src); else fImage = Toolkit.getDefaultToolkit (). getImage (src); }
Tu získate adresu URL; ak je nulový, preskočíte načítanie obrázka. V MyImageView
, tento kód by ste mali vykonať, iba ak je vaším odkazom na obrázok adresa URL. Nasleduje metóda, ktorú môžete pridať na otestovanie zdroja obrázka:
private boolean isURL () String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); vrátiť src.toLowerCase (). startsWith ("súbor")
V zásade získate odkaz na obrázok vo forme a String
a otestujte, či sa začína jedným z dvoch typov adries URL: spis pre miestne obrázky a http pre vzdialené obrázky. Jens Alfke, autor originálu javax.swing.text.html.ImageView
trieda, používa globálne premenné triedy, takže odovzdávanie parametrov funkciám je zbytočné. Tu je globálna premenná prvok
.
Môžete napísať kód, ktorý hovorí if (isURL ()) {
, ale čo vložíte do výrazu else pre relatívnu cestu? Je to celkom jednoduché - stačí načítať obrázok obvyklým spôsobom v aplikácii:
else {String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); fImage = Toolkit.getDefaultToolkit (). createImage (src); }
Neexistuje tu žiadna skutočná mágia, ale má to jeden háčik. The createImage (src)
funkcia sa môže vrátiť skôr, ako sa naplnia všetky pixely obrázka. Ak sa to stane, zobrazí sa nefunkčný obrázok. Na vyriešenie problému stačí počkať, kým sa pixely obrázka úplne vyplnia. Mojou prvou záľubou bolo použiť MediaTracker
zistiť, kedy bol obrázok pripravený, ale MediaTracker
Konštruktor vyžaduje, aby komponent vykresľoval obrázok ako parameter. Opäť som si teda požičal nejaký kód od Jima Grahama java.awt.MediaTracker
a napísal svoju vlastnú metódu na obídenie problému:
private void waitForImage () vyvolá InterruptedException {int w = fImage.getWidth (this); int h = fImage.getHeight (toto); while (true)}
Táto metóda v zásade vykonáva rovnakú prácu ako MediaTracker
je waitForID (int id)
metóda, ale nevyžaduje nadradený komponent. Túto metódu je možné zavolať ihneď po vytvorení obrázka.
Než budem pokračovať, mal by som spomenúť malý problém. Bolo nemožné podtriedu ImageView
z javax.swing.text.html
balíček, tak som skopíroval celý súbor, aby som vytvoril svoju vlastnú triedu, tzv MyImageView
, ktoré som nevložil do obalu. V origináli ImageView
kód, ak obrázok nie je možné zobraziť, pretože neexistuje alebo je oneskorený, načíta predvolený nefunkčný obrázok z priečinka javax.swing.text.html.icons
balíček. Na načítanie rozbitého obrázka používa trieda getResourceAsStream (názov reťazca)
metóda z Trieda
trieda. Skutočný kód vyzerá takto:
Zdroj InputStream = HTMLEditorKit.class.getResourceAsStream (MISSING_IMAGE_SRC);
kde MISSING_IMAGE_SRC
parameter je a String
s obsahom:
MISSING_IMAGE_SRC = "ikony" + System.getProperty ("file.separator", "/") + "image-failed.gif";
Nasledujúci výňatok z ImageView
zdrojový kód vysvetľuje dôvody spoločnosti Sun na používanie súboru getResourceAsStream (názov reťazca)
spôsob načítania rozbitých obrázkov.
/ * Skopírujte prostriedok do bajtového poľa. To je * nevyhnutné, pretože niekoľko prehľadávačov považuje * Class.getResource za bezpečnostné riziko, pretože * sa dá použiť na načítanie ďalších tried. * Class.getResourceAsStream vráti iba surové * bajty, ktoré môžeme previesť na obrázok. * /
Ak ste túto časť ešte nepreskočili (viem, je to dosť drsné!), Dovoľte mi vysvetliť, prečo to spomínam. Ak si nie ste vedomí tohto správania, nebudete rozumieť tomu, prečo sa nefunkčné obrázky zobrazujú správne, a nebudete môcť problém vyriešiť vo svojom vlastnom kóde. Ak chcete problém vyriešiť, musíte načítať svoje vlastné obrázky. Rozhodol som sa pokračovať v používaní rovnakej metódy, ale nie je to skutočne nevyhnutné. Vyššie uvedené varovanie sa týka prehliadačov obsahujúcich applety, ktoré majú bezpečnostné hľadiská obmedzujúce prístup na disk (samozrejme, pokiaľ nie sú podpísané). V každom prípade bol tento článok určený na použitie s aplikáciou, takže použitie alternatívnej metódy načítania obrázkov by nemalo spôsobovať obavy.
Pri volaní na getResourceAsStream (názov reťazca)
je vytvorená, môžete zahrnúť relatívnu cestu k obrázku, ako je znázornené vyššie. Vo vyššie uvedenom kóde sa rozbitý obrázok vždy načíta zo zadanej cesty vzhľadom na HTMLEditorKit
trieda. Napríklad od HTMLEditorKit
trieda sa nachádza v javax.swing.text.html
, pokúsi sa načítať poškodený obrázok image-failed.gif
od javax.swing.text.html.icons
. To platí aj pre jednoduché adresáre; triedy nemusia byť v balíkoch. Na záver od HTMLEditorKit
je chránený balíkom, nemáte k nemu prístup getResourceAsStream (názov reťazca)
metóda. Namiesto toho môžete použiť MyImageView
triedy a rozbité obrázky vložte do podadresára ikon. Riadok kódu bude vyzerať takto:
InputStream resource = MyImageView.class.getResourceAsStream (MISSING_IMAGE_SRC);
Ak sa rozhodnete použiť podobnú implementáciu ako ja, budete si musieť vytvoriť vlastné ikony. Stále môžete používať ikony dodávané s JDK spoločnosti Sun, ale to si vyžaduje zmenu umiestnenia zdroja, aby ste namiesto relatívnej cesty mohli používať absolútnu cestu. Absolútna cesta je:
javax.swing.text.html.icons.imagename.gif
Ak sa chcete dozvedieť viac o používaní getResourceStream (názov reťazca)
, pozrite si informácie o Javadoc pre Trieda
trieda; odkaz je uvedený v zdrojoch.
Tento článok je takmer výlučne o prispôsobení sa relatívnym cestám - k čomu však patria? Pokiaľ zatiaľ použijete kód, ktorý som uviedol, budete môcť používať iba cesty k miestu, kde ste aplikáciu spustili. To je skvelé, ak sú všetky vaše obrázky vždy umiestnené v týchto cestách, ale nie je to tak vždy. Nebudem sa veľmi venovať tomu, ako tento problém vyriešiť, pretože sa dá ľahko vyriešiť. Globálnu premennú aplikácie môžete nastaviť niekde v aplikácii alebo môžete nastaviť systémovú premennú. V MyImageView
, pred načítaním obrázka zreťazíte relatívnu cestu k obrázku a absolútnu cestu získanú z globálnej premennej. Ak to nemá zmysel, vyhľadajte processSrcPath ()
metóda v konečnom zdrojovom kóde pre MyImageView
.
Nakoniec, MyImageView
je kompletný. Musíte však prísť na to, ako to povedať JEditorPane
použit MyImageView
namiesto javax.swing.text.html.ImageView
. The JEditorPane
môže podporovať tri textové formáty: obyčajný, RTF a HTML. Ak JEditorPane
zobrazuje HTML, BasicHTML
- podtrieda TextUI
- sa používa na vykreslenie HTML. BasicHTML
používa JEditorPane
je HTMLEditorKit
vytvoriť vyhliadka
. The HTMLEditorKit
obsahuje metódu nazvanú getViewFactory ()
, ktorá vracia inštanciu vnútornej triedy s názvom HTMLFactory
. The HTMLFactory
obsahuje metódu nazvanú vytvoriť (prvok elem)
, ktorá vracia a vyhliadka
podľa typu štítku. Konkrétne, ak je značka znakom IMG
značka, vráti inštanciu ImageView
. Vrátiť inštanciu MyImageView
, môžete si vytvoriť svoj vlastný EditorKit
zavolal MyHTMLEditorKit
, ktoré podtriedy HTMLEditorKit
. Vo vnútri vášho MyHTMLEditorKit
, vytvoríte novú vnútornú triedu s názvom MyHTMLFactory
, ktoré podtriedy HTMLFactory
. V tejto vnútornej triede si môžete vytvoriť svoj vlastný vytvoriť (prvok elem)
metóda, ktorá vyzerá asi takto:
verejné zobrazenie create (Element elem) {Object o = elem.getAttributes (). getAttribute (StyleConstants.NameAttribute); if (o instanceof HTML.Tag) {HTML.Tag kind = (HTML.Tag) o; if (kind == HTML.Tag.IMG) return new MyImageView (elem); } return super.create (elem); }
Jediné, čo teraz zostáva, je nastaviť JEditorPane
použit MyHTMLEditorKit
. Kód je celkom jednoduchý: