Ohjelmointi

Java-vinkki 109: Näytä kuvat JEditorPane-sovelluksella

Voit käyttää nykyistä JEditorPane komponentti HTML-merkintöjen näyttämiseksi, mutta monimutkaisempien tehtävien suorittamiseksi, JEditorPane tarvitsee parannusta. Äskettäin jouduin rakentamaan XML-lomakkeenrakennussovelluksen. Yksi välttämätön komponentti oli WYSIWYG HTML-editori, joka pystyi muokkaamaan HTML-merkintäsisältöä joissakin XML-tunnisteissa. JEditorPane oli ilmeinen Java-komponenttivalinta HTML-merkintöjen näyttämisessä, koska tämä toiminto oli jo rakennettu siihen. Valitettavasti, kun se lisätään HTML-merkintään, JEditorPane ei voi näyttää kuvia, joilla on suhteelliset polut. Esimerkiksi, jos seuraava kuva suhteellisen polun kanssa sisältyi XML-tagiin, sitä ei näytetä oikein:

Päinvastoin, absoluuttinen polku toimisi (olettaen, että annettu polku ja kuva todella olivat olemassa):

Sovelluksessani kuvat tallennettiin aina alihakemistoon suhteessa XML-tiedoston sijaintiin. Siksi halusin aina käyttää suhteellista polkua. Tässä artikkelissa kerrotaan, miksi ongelma esiintyy ja kuinka se korjataan.

Miksi näin tapahtuu?

Tarkastelemalla tarkemmin rakennuttajia JEditorPane auttaa meitä ymmärtämään, miksi se ei voi näyttää kuvia suhteellisilla poluilla.

  1. JEditorPane () luo uuden JEditorPane.
  2. JEditorPane (merkkijono-URL) luo JEditorPane perustuu URL-määritelmän sisältävään merkkijonoon.
  3. JEditorPane (Merkkijonotyyppi, Merkkijonoteksti) luo JEditorPane joka on alustettu annettuun tekstiin.
  4. JEditorPane (URL-aloitussivu) luo JEditorPane syötteen määritetyn URL-osoitteen perusteella.

Toinen ja neljäs konstruktori alustavat objektin viittaamalla etä- tai paikalliseen HTML-tiedostoon. An HTMLDocument on jokaisen sisällä JEditorPane, ja sen perusta on asetettu URL-rakentajan parametrin pohjaan. JEditorPaneNäillä konstruktoreilla luotut s voivat käsitellä suhteellisia polkuja, koska HTMLDocument yhdistyy suhteellisen polun kanssa absoluuttisen polun luomiseksi.

Jos käytetään ensimmäistä konstruktoria, näytetty teksti on lisättävä objektin luomisen jälkeen. Kolmas konstruktori hyväksyy a Merkkijono sisällönä, mutta alustaa ei alusteta. Koska halusin saada HTML-merkinnän XML-tagista eikä tiedostosta, minun oli käytettävä joko ensimmäistä tai kolmatta konstruktoria.

Kuinka korjaamme ongelman?

Ennen kuin jatkan, paljastetaan ja ratkaistaan ​​toinen pienempi ongelma. Ilmeisin tapa lisätä merkintä JEditorPane on käyttää setText (merkkijono). Tämä menetelmä edellyttää kuitenkin, että syötät koko näytetyn merkinnän joka kerta, kun teet muutoksen. Ihannetapauksessa uudet tunnisteet tulisi lisätä olemassa olevaan tekstiin. Voit lisätä uuden merkinnän seuraavalla koodilla:

private void insertHTML (JEditorPane-editori, merkkijono html, int sijainti) heittää IOException {// olettaa, että editori on jo asetettu "teksti / html" -tyyppiseksi HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit (); Asiakirja doc = editor.getDocument (); StringReader-lukija = uusi StringReader (html); kit.read (lukija, asiakirja, sijainti); } 

Nyt pääsemme asian ytimeen: Kuinka JEditorPane renderoidaanko HTML? Jokainen tyyppi JEditorPane viittaa molempiin a Asiakirja ja EditorKit. Kun JEditorPane on asetettu kirjoittamaan "text / html", se sisältää HTMLDocument, joka sisältää merkinnän ja HTMLEditorKit joka määrittää, mitkä luokat renderoivat kukin merkinnän sisältämän tagin. Erityisesti HTMLEditorKit luokka sisältää HTMLFactory sisempi luokka, jonka luoda (Element elem) menetelmä tutkii kukin erillinen tunniste. Tässä on kyseisen tehdasluokan koodi, joka käsittelee kuvatunnisteita:

 else if (kind == HTML.Tag.IMG) palauttaa uuden ImageView (elem); 

Kuten voit nyt nähdä, ImageView luokka tosiasiallisesti lataa kuvan. Voit määrittää kuvan sijainnin getSourceURL () menetelmää kutsutaan:

 yksityinen URL-osoite getSourceURL () {Merkkijono src = (Merkkijono) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); if (src == null) return null; URL-viite = ((HTMLDocument) getDocument ()). getBase (); kokeile {URL u = uusi URL (viite, src); paluu u; } catch (epämuodostunutURLException e) {return null; }} 

Tässä getSourceURL () - menetelmä yrittää luoda uuden URL-osoitteen kuvaan viittaamiseksi käyttämällä HTMLDocument pohja. Jos kyseinen pohja on tyhjä, null palautetaan ja kuvan lataustoiminto keskeytetään. Haluat ohittaa sen käyttäytymisen.

Ihannetapauksessa voit alaluokka ImageView luokan ja ohittaa alusta (Element elem) menetelmä, jossa kuvan lataus tapahtuu. Valitettavasti tuo luokka on paketti suojattu, joten sinun on luotava kokonaan uusi luokka. Helpoin tapa tehdä se on lainata koodi ja muokata sitä alkuperäisestä ImageView luokassa. Kutsutaan sitä MyImageView.

Katso ensin kuvan lataava koodi. Seuraava on otettu alusta (Element elem) menetelmä:

 URL src = getSourceURL (); if (src! = null) {Sanakirjavälimuisti = (Sanakirja) getDocument (). getProperty (IMAGE_CACHE_PROPERTY); if (välimuisti! = null) fImage = (Kuva) cache.get (src); else fImage = Toolkit.getDefaultToolkit (). getImage (src); } 

Täältä saat URL-osoitteen; jos se on nolla, ohitat kuvan lataamisen. Sisään MyImageView, suorita tämä koodi vain, jos kuvaviitteesi on URL-osoite. Seuraava on menetelmä, jonka voit lisätä kuvalähteen testaamiseen:

 private boolean isURL () Merkkijono src = (Merkkijono) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); return src.toLowerCase (). startsWith ("tiedosto") 

Periaatteessa saat viittauksen kuvaan a-muodossa Merkkijono ja testaa, aloitetaanko se jostakin kahdesta URL-tyypistä: tiedosto paikallisille kuville ja http etäkuvia varten. Jens Alfke, alkuperäisen kirjoittaja javax.swing.text.html.ImageView class, käyttää luokan globaaleja muuttujia, joten parametrien siirtäminen funktioille on tarpeetonta. Tässä globaali muuttuja on fElementti.

Voit kirjoittaa koodin, joka sanoo jos (isURL ()) {}, mutta mitä laitat toiseen lauseeseen suhteellisen polun suhteen? Se on melko yksinkertaista - lataa kuva tavalliseen tapaan sovelluksessa:

 else {String src = (Merkkijono) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); fImage = Toolkit.getDefaultToolkit (). createImage (src); } 

Täällä ei ole todellista taikuutta, mutta on yksi saalis. createImage (src) -toiminto voi palata, ennen kuin kaikki kuvan pikselit on täytetty. Jos näin tapahtuu, rikki kuva näytetään. Voit korjata ongelman odottamalla, kunnes kuvan pikselit ovat täysin täytettyjä. Ensimmäinen taipumukseni oli käyttää MediaTracker havaita, kun kuva oli valmis, mutta MediaTrackerkonstruktori vaatii komponentin, joka kuvaa kuvaa parametrina. Joten jälleen kerran lainasin koodin Jim Grahamilta java.awt.MediaTracker ja kirjoitin oman menetelmän ongelman kiertämiseksi:

 private void waitForImage () heittää InterruptedException {int w = fImage.getWidth (tämä); int h = fImage.getHeight (tämä); kun (tosi)} 

Tämä menetelmä tekee periaatteessa saman työn kuin MediaTrackeron waitForID (int id) menetelmä, mutta ei vaadi vanhempaa komponenttia. Kutsu tähän menetelmään voidaan soittaa heti kuvan luomisen jälkeen.

On pieni ongelma, joka minun pitäisi mainita ennen kuin jatkan. Alaluokkaa oli mahdotonta ImageView alkaen javax.swing.text.html paketin, joten kopioin koko tiedoston luodakseni oman luokan nimeltä MyImageView, jota en ole laittanut pakettiin. Alkuperäisessä muodossa ImageView koodi, jos kuvaa ei voida näyttää, koska sitä ei ole tai se viivästyy, se lataa oletuksena rikkoutuneen kuvan javax.swing.text.html.icons paketti. Rikkoutuneen kuvan lataamiseksi luokka käyttää getResourceAsStream (merkkijono) menetelmä Luokka luokassa. Varsinainen koodi näyttää tältä:

 InputStream-resurssi = HTMLEditorKit.class.getResourceAsStream (MISSING_IMAGE_SRC); 

missä MISSING_IMAGE_SRC parametri on a Merkkijono sisällöllä:

 MISSING_IMAGE_SRC = "kuvakkeet" + System.getProperty ("tiedosto.erotin", "/") + "kuva-epäonnistunut.gif"; 

Seuraava ote ImageView lähdekoodi selittää Sunin perustelut getResourceAsStream (merkkijono) menetelmä rikkoutuneiden kuvien lataamiseksi.

 / * Kopioi resurssi tavutaulukkoon. Tämä on * välttämätöntä, koska useat selaimet pitävät * Class.getResource -turvariskiä, ​​koska sitä * voidaan käyttää ylimääräisten luokkien lataamiseen. * Class.getResourceAsStream palauttaa vain raakat * tavut, jotka voimme muuntaa kuvaksi. * / 

Jos et ole vielä ohittanut tätä osaa (tiedän, se on melko hienoa!), Anna minun selittää, miksi mainitsen sen. Jos et ole tietoinen tästä toiminnasta, et ymmärrä, miksi rikki kuvat eivät näy oikein, etkä pysty korjaamaan ongelmaa omassa koodissasi. Voit korjata ongelman lataamalla omat kuvasi. Päätin jatkaa samaa menetelmää, mutta se ei todellakaan ole välttämätöntä. Yllä oleva varoitus koskee selaimia, jotka sisältävät sovelmia, joiden turvallisuusnäkökohdat rajoittavat levyn käyttöä (ellei allekirjoitettu tietenkään). Joka tapauksessa tämä artikkeli oli tarkoitettu käytettäväksi sovelluksen kanssa, joten vaihtoehtoisen kuvan latausmenetelmän käyttämisen ei pitäisi olla huolenaihe.

Kun soitat getResourceAsStream (merkkijono) on tehty, voit sisällyttää suhteellisen polun kuvaan, kuten yllä on esitetty. Yllä olevassa koodissa rikkoutunut kuva ladataan aina määritetyltä polulta suhteessa HTMLEditorKit luokassa. Esimerkiksi, koska HTMLEditorKit luokka sijaitsee javax.swing.text.html, se yrittää ladata rikkoutuneen kuvan image-Fail.gif alkaen javax.swing.text.html.icons. Tämä koskee myös yksinkertaisia ​​hakemistoja; luokkien ei tarvitse olla paketteja. Viimeiseksi, siitä lähtien HTMLEditorKit on pakettisuojattu, et voi käyttää sitä getResourceAsStream (merkkijono) menetelmä. Sen sijaan voit käyttää MyImageView luokassa ja laita rikkoutuneet kuvasi kuvakkeiden alihakemistoon. Koodirivi näyttää tältä:

 InputStream-resurssi = MyImageView.class.getResourceAsStream (MISSING_IMAGE_SRC); 

Jos päätät käyttää samanlaista toteutusta kuin minun, sinun on luotava omat kuvakkeet. Voit silti käyttää Sunin JDK: n mukana toimitettuja kuvakkeita, mutta se edellyttää resurssin sijainnin muuttamista absoluuttisen polun käyttämiseksi suhteellisen polun sijaan. Absoluuttinen polku on:

javax.swing.text.html.icons.imagename.gif 

Oppia käyttämisestä getResourceStream (merkkijono), katso Javadoc-tiedot Luokka luokka; linkki on resursseissa.

Tämä artikkeli koskee melkein kokonaan suhteellisten polkujen sovittamista - mutta mihin ne suhteutuvat? Toistaiseksi, jos käytät lähettämääni koodia, voit käyttää polkuja vain sovelluksen käynnistyspaikkaan nähden. Tämä on hienoa, jos kaikki kuvasi sijaitsevat aina näillä poluilla, mutta näin ei aina ole. En käsittele yksityiskohtaisesti tämän ongelman korjaamista, koska se voidaan korjata helposti. Voit joko asettaa sovelluksen globaalin muuttujan jonnekin sovelluksessasi tai asettaa järjestelmämuuttujan. Sisään MyImageView, ennen kuvan lataamista ketjutat suhteellisen polun kuvaan ja absoluuttisen polun, joka saadaan globaalista muuttujasta. Jos sillä ei ole järkeä, etsi processSrcPath () menetelmä lopullisessa lähdekoodissa MyImageView.

Viimeinkin, MyImageView on valmis. Sinun on kuitenkin selvitettävä, kuinka kertoa JEditorPane käyttää MyImageView sijasta javax.swing.text.html.ImageView. JEditorPane voi tukea kolmea tekstimuotoa: tavallinen, RTF ja HTML. Jos JEditorPane näyttää HTML: n, BasicHTML - alaluokka TextUI - käytetään renderöimään HTML. BasicHTML käyttää JEditorPaneon HTMLEditorKit luoda Näytä. HTMLEditorKit sisältää menetelmän nimeltä getViewFactory (), joka palauttaa kutsutun sisäisen luokan esiintymän HTMLFactory. HTMLFactory sisältää menetelmän nimeltä luoda (Element elem), joka palauttaa a Näytä tunnistetyypin mukaan. Erityisesti, jos tunniste on IMG -tunniste, se palauttaa ImageView. Palauttaa MyImageView, voit luoda oman EditorKit olla nimeltään MyHTMLEditorKit, joka alaluokkia HTMLEditorKit. Sisällä sinun MyHTMLEditorKit, luot uuden sisäisen luokan nimeltä MyHTMLFactory, joka alaluokkia HTMLFactory. Tuossa sisäisessä luokassa voit tehdä oman luoda (Element elem) menetelmä, joka näyttää tältä:

 public View create (Element elem) {Object o = elem.getAttributes (). getAttribute (StyleConstants.NameAttribute); if (o HTML.Tag-esimerkki) {HTML.Tag kind = (HTML.Tag) o; if (kind == HTML.Tag.IMG) palauttaa uuden MyImageView (elem); } return super.create (elem); } 

Ainoa tehtävä jäljellä on nyt asettaa JEditorPane käyttää MyHTMLEditorKit. Koodi on melko yksinkertainen:

$config[zx-auto] not found$config[zx-overlay] not found