Uporabite lahko trenutno JEditorPane
komponenta za prikaz oznak HTML, vendar za izvajanje bolj zapletenih nalog, JEditorPane
potrebuje nekaj izboljšav. Pred kratkim sem moral zgraditi aplikacijo za ustvarjanje obrazcev XML. Ena nujna komponenta je bil urejevalnik HTML WYSIWYG, ki je lahko urejal vsebino oznak HTML znotraj nekaterih oznak XML. JEditorPane
je bila očitna izbira komponente Java za prikaz oznak HTML, ker je bila ta funkcionalnost že vgrajena vanj. Na žalost, ko je vstavljen v oznako HTML, JEditorPane
ni mogel prikazati slik z relativnimi potmi. Če bi bila na primer naslednja slika z relativno potjo v oznaki XML, ne bi bila pravilno prikazana:
Nasprotno pa bi delovala absolutna pot (ob predpostavki, da dana pot in slika res obstajata):
V moji aplikaciji so bile slike vedno shranjene v podimeniku glede na lokacijo datoteke XML. Zato sem si vedno želel uporabiti relativno pot. Ta članek bo razložil, zakaj ta težava obstaja in kako jo odpraviti.
Zakaj se to zgodi?
Podrobnejši pogled na konstruktorje za JEditorPane
nam bo pomagal razumeti, zakaj ne more prikazati slik v relativnih poteh.
JEditorPane ()
ustvari novoJEditorPane
.JEditorPane (URL niza)
ustvari aJEditorPane
temelji na nizu, ki vsebuje specifikacijo URL-ja.JEditorPane (vrsta niza, besedilo niza)
ustvari aJEditorPane
ki je bil inicializiran v dano besedilo.JEditorPane (URL začetna stran)
ustvari aJEditorPane
na podlagi določenega URL-ja za vnos.
Drugi in četrti konstruktor inicializirata objekt s sklicevanjem na oddaljeno ali lokalno datoteko HTML. An HTMLDocument
je znotraj vsakega JEditorPane
, njegova osnova pa je nastavljena na osnovo parametra konstruktorja URL-jev. JEditorPane
Ustvarjeni s temi konstruktorji lahko obdelujejo relativne poti, ker je osnova datoteke HTMLDocument
kombinira z relativno potjo, da ustvari absolutno pot.
Če je uporabljen prvi konstruktor, mora biti prikazano besedilo vstavljeno po ustvarjanju predmeta. Tretji konstruktor sprejme a Vrvica
kot vsebina, vendar osnova ni inicializirana. Ker sem hotel pridobiti oznako HTML iz oznake XML in ne iz datoteke, sem moral uporabiti prvi ali tretji konstruktor.
Kako rešimo težavo?
Preden nadaljujem, razkrijmo in rešimo še en manjši problem. Najbolj očiten način za vstavljanje oznak v JEditorPane
je uporaba setText (besedilo v nizu)
. Vendar ta metoda zahteva, da ob vsaki spremembi vnesete celotno prikazano oznako. V idealnem primeru bi bilo treba nove oznake vstaviti v obstoječe besedilo. Za dodajanje novega označevanja lahko uporabite naslednjo kodo:
private void insertHTML (urejevalnik JEditorPane, niz html, int lokacija) vrže IOException {// predvideva, da je urejevalnik že nastavljen na "text / html" tip HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit (); Dokument doc = editor.getDocument (); StringReader čitalec = nov StringReader (html); kit.read (čitalnik, dokument, lokacija); }
Zdaj, ko gremo k bistvu: kako JEditorPane
upodabljati HTML? Vsaka vrsta JEditorPane
sklicuje na a Dokument
in an EditorKit
. Kdaj JEditorPane
je nastavljen na tip "text / html", vsebuje HTMLDocument
, ki vsebuje oznako in HTMLEditorKit
ki določa, kateri razredi upodabljajo posamezne oznake, ki jih vsebuje oznaka. Natančneje, HTMLEditorKit
razred vsebuje HTMLFactory
notranji razred, katerega ustvari (element elem)
metoda dejansko preuči vsako ločeno oznako. Tu je koda iz tega tovarniškega razreda, ki obravnava slikovne oznake:
sicer če (kind == HTML.Tag.IMG) vrne nov ImageView (elem);
Kot zdaj lahko vidite, ImageView
razred dejansko naloži sliko. Za določitev lokacije slike se prikaže getSourceURL ()
metoda se imenuje:
zasebni URL getSourceURL () {String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); if (src == null) vrne null; Sklic na URL = ((HTMLDocument) getDocument ()). getBase (); poskusite {URL u = nov URL (referenca, src); vrni u; } catch (MalformedURLException e) {return null; }}
Tukaj getSourceURL ()
metoda poskuša ustvariti nov URL za sklicevanje na sliko s pomočjo HTMLDocument
osnova. Če je osnova nična, se vrne nič in operacija nalaganja slik se prekine. To vedenje želite preglasiti.
V idealnem primeru bi podrazred ImageView
razred in preglasite inicializira (element elem)
metoda, pri kateri se nalaganje slike izvede. Na žalost je ta razred paket zaščiten, zato morate ustvariti povsem nov razred. To najlažje storite tako, da si sposodite in nato spremenite kodo iz izvirnika ImageView
razred. Recimo temu MyImageView
.
Najprej si oglejte kodo, ki je naložila sliko. Naslednje je vzeto iz inicializira (element elem)
metoda:
URL src = getSourceURL (); if (src! = null) {Predpomnilnik slovarja = (slovar) getDocument (). getProperty (IMAGE_CACHE_PROPERTY); if (cache! = null) fImage = (Image) cache.get (src); else fImage = Toolkit.getDefaultToolkit (). getImage (src); }
Tu dobite URL; če je nič, preskočite nalaganje slike. V MyImageView
, izvedite to kodo samo, če je vaša referenca slike URL. Sledi metoda, ki jo lahko dodate za preizkus vira slike:
zasebni logični isURL () niz src = (niz) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); vrni src.toLowerCase (). startWith ("datoteka")
V bistvu dobite sklic na sliko v obliki Vrvica
in preizkusite, ali se začne z eno od dveh vrst URL-jev: mapa za lokalne slike in http za oddaljene slike. Jens Alfke, avtor izvirnika javax.swing.text.html.ImageView
class, uporablja globalne spremenljivke razreda, zato posredovanje parametrov funkcijam ni potrebno. Tu je globalna spremenljivka fElement
.
Lahko napišete kodo, ki pravi if (isURL ()) {
, ampak kaj vnesete v izjavo else za relativno pot? Preprosto - samo naložite sliko, kot bi običajno v aplikaciji:
else {String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); fImage = Toolkit.getDefaultToolkit (). createImage (src); }
Tu ni prave čarovnije, je pa en ulov. The createImage (src)
funkcija se lahko vrne, preden so zaseljene vse slikovne pike. Če se to zgodi, se prikaže zlomljena slika. Če želite odpraviti težavo, lahko počakate, da se slikovne pike slike popolnoma zapolnijo. Moja prva nagnjenost je bila k uporabi MediaTracker
da zazna, kdaj je bila slika pripravljena, vendar MediaTracker
Konstruktor 's komponento upodablja sliko kot parameter. Tako sem si še enkrat sposodil kodo od Jima Grahama java.awt.MediaTracker
in napisal svojo metodo za izogibanje težavi:
private void waitForImage () vrže InterruptedException {int w = fImage.getWidth (this); int h = fImage.getHeight (to); medtem ko (res)}
Ta metoda v bistvu opravlja enako delo kot MediaTracker
je waitForID (int id)
metoda, vendar ne zahteva nadrejene komponente. Klic na to metodo lahko pokličete takoj po ustvarjanju slike.
Obstaja majhen problem, ki bi ga moral omeniti, preden nadaljujem. Podrazred je bilo nemogoče ImageView
Iz javax.swing.text.html
paket, zato sem kopiral celotno datoteko in ustvaril svoj razred, imenovan MyImageView
, ki ga nisem dal v paket. V originalu ImageView
kodo, če slike ni mogoče prikazati, ker ne obstaja ali zamuja, naloži privzeto zlomljeno sliko iz javax.swing.text.html.icons
paket. Za naložitev pokvarjene slike razred uporablja getResourceAsStream (ime niza)
metoda iz Razred
razred. Dejanska koda je videti takole:
InputStream resource = HTMLEditorKit.class.getResourceAsStream (MISSING_IMAGE_SRC);
kje za MISSING_IMAGE_SRC
parameter je a Vrvica
z vsebino:
MISSING_IMAGE_SRC = "ikone" + System.getProperty ("file.separator", "/") + "image-failed.gif";
Naslednji odlomek iz ImageView
izvorna koda pojasnjuje Sun-ove razloge za uporabo getResourceAsStream (ime niza)
način nalaganja zlomljenih slik.
/ * Kopirajte vir v bajtno matriko. To je * potrebno, ker več brskalnikov meni, da je * Class.getResource varnostno tveganje, ker ga lahko * uporabite za nalaganje dodatnih razredov. * Class.getResourceAsStream samo vrne neobdelane * bajte, ki jih lahko pretvorimo v sliko. * /
Če tega odseka še niste preskočili (vem, da je precej grozljiv!), Naj pojasnim, zakaj ga omenjam. Če se tega vedenja ne zavedate, ne boste razumeli, zakaj zlomljene slike niso pravilno prikazane, in težave ne boste mogli odpraviti v svoji kodi. Če želite odpraviti težavo, morate naložiti lastne slike. Odločil sem se, da bom še naprej uporabljal isto metodo, vendar to v resnici ni potrebno. Zgornje opozorilo je za brskalnike, ki vsebujejo programčke, ki imajo varnostne vidike, ki omejujejo dostop do diska (razen če so seveda podpisani). Vsekakor je bil ta članek namenjen uporabi z aplikacijo, zato uporaba nadomestnega načina nalaganja slik ne bi smela biti zaskrbljujoča.
Ko je klic getResourceAsStream (ime niza)
lahko naredite, lahko vključite relativno pot do slike, kot je prikazano zgoraj. V zgornji kodi se bo zlomljena slika vedno naložila z določene poti glede na HTMLEditorKit
razred. Na primer, od HTMLEditorKit
razred se nahaja v javax.swing.text.html
, bo poskusil naložiti pokvarjeno sliko image-failed.gif
iz javax.swing.text.html.icons
. To velja tudi za preproste imenike; razredi niso nujno v paketih. Nazadnje, od HTMLEditorKit
je zaščiten s paketom, do njega nimate dostopa getResourceAsStream (ime niza)
metoda. Namesto tega lahko uporabite MyImageView
razreda in svoje zlomljene slike shranite v podimenik ikon. Vrstica kode bo videti tako:
InputStream resource = MyImageView.class.getResourceAsStream (MISSING_IMAGE_SRC);
Če se odločite za izvedbo, podobno moji, boste morali ustvariti lastne ikone. Še vedno lahko uporabljate ikone, priložene Sun-ovemu JDK, vendar to zahteva spremembo lokacije vira, tako da namesto relativne poti uporabite absolutno pot. Absolutna pot je:
javax.swing.text.html.icons.imagename.gif
Če želite izvedeti več o uporabi getResourceStream (ime niza)
, glejte informacije o Javadocu za Razred
razred; povezava je na voljo v virih.
Ta članek skoraj v celoti govori o prilagoditvi relativnih poti - toda na kaj so relativne? Do zdaj, če uporabljate kodo, ki sem jo posredoval, boste lahko uporabljali samo poti glede na to, kje ste zagnali aplikacijo. To je super, če se vse vaše slike vedno nahajajo na teh poteh, vendar ni vedno tako. Ne bom se spuščal v podrobnosti, kako odpraviti to težavo, ker jo je mogoče enostavno odpraviti. Globalno spremenljivko aplikacije lahko nastavite nekje v aplikaciji ali pa sistemsko spremenljivko. V MyImageView
, preden naložite sliko, združite relativno pot do slike in absolutno pot, pridobljeno iz globalne spremenljivke. Če to nima smisla, poiščite processSrcPath ()
v končni izvorni kodi za MyImageView
.
Končno, MyImageView
je končana. Vendar morate ugotoviti, kako to povedati JEditorPane
uporabiti MyImageView
namesto javax.swing.text.html.ImageView
. The JEditorPane
lahko podpira tri oblike besedila: navaden, RTF in HTML. Če JEditorPane
prikazuje HTML, BasicHTML
- podrazred TextUI
- se uporablja za upodabljanje HTML-ja. BasicHTML
uporablja JEditorPane
je HTMLEditorKit
ustvariti Pogled
. The HTMLEditorKit
vsebuje metodo, imenovano getViewFactory ()
, ki vrne primerek notranjega razreda, imenovanega HTMLFactory
. The HTMLFactory
vsebuje metodo, imenovano ustvari (element elem)
, ki vrne a Pogled
glede na vrsto oznake. Natančneje, če je oznaka IMG
, vrne primerek ImageView
. Če želite vrniti primerek MyImageView
, lahko ustvarite svojega EditorKit
poklical MyHTMLEditorKit
, kateri podrazredi HTMLEditorKit
. Znotraj vašega MyHTMLEditorKit
, ustvarite nov notranji razred z imenom MyHTMLFactory
, kateri podrazredi HTMLFactory
. V tem notranjem razredu lahko naredite svoje ustvari (element elem)
metoda, ki je videti nekako takole:
javni pogled create (Element elem) {Objekt o = elem.getAttributes (). getAttribute (StyleConstants.NameAttribute); if (o instanceof HTML.Tag) {HTML.Tag kind = (HTML.Tag) o; če (kind == HTML.Tag.IMG) vrne nov MyImageView (elem); } vrni super.create (elem); }
Edino, kar moramo zdaj storiti, je nastaviti JEditorPane
uporabiti MyHTMLEditorKit
. Koda je zelo preprosta: