Programmēšana

Java 109. padoms: attēlojiet attēlus, izmantojot JEditorPane

Jūs varat izmantot pašreizējo JEditorPane HTML marķējuma parādīšanai, bet sarežģītāku uzdevumu veikšanai, JEditorPane ir jāpilnveido. Nesen man bija jāveido XML veidņu veidošanas programma. Viens nepieciešamais komponents bija WYSIWYG HTML redaktors, kas varēja rediģēt HTML iezīmēšanas saturu dažos XML tagos. JEditorPane bija acīmredzama Java komponentu izvēle HTML marķējuma parādīšanai, jo šī funkcionalitāte tajā jau bija iebūvēta. Diemžēl, ievietojot HTML marķējumā, JEditorPane nevarēja parādīt attēlus ar relatīviem ceļiem. Piemēram, ja XML tagā ir ietverts šāds attēls ar relatīvu ceļu, tas netiks pareizi parādīts:

Un otrādi, darbotos absolūts ceļš (pieņemot, ka dotais ceļš un attēls patiešām pastāv):

Manā lietojumprogrammā attēli vienmēr tika saglabāti apakšdirektorijā salīdzinājumā ar XML faila atrašanās vietu. Tāpēc es vienmēr gribēju izmantot relatīvu ceļu. Šajā rakstā tiks paskaidrots, kāpēc šī problēma pastāv un kā to novērst.

Kāpēc tas notiek?

Rūpīgāk aplūkojot celtniekus JEditorPane palīdzēs mums saprast, kāpēc tā nevar attēlot attēlus relatīvos ceļos.

  1. JEditorPane () rada jaunu JEditorPane.
  2. JEditorPane (virknes URL) izveido a JEditorPane pamatojoties uz virkni, kas satur URL specifikāciju.
  3. JEditorPane (virknes tips, virknes teksts) izveido a JEditorPane kas ir inicializēts dotajā tekstā.
  4. JEditorPane (URL initialPage) izveido a JEditorPane pamatojoties uz norādīto URL ievadei.

Otrais un ceturtais konstruktors inicializē objektu, atsaucoties uz attālo vai vietējo HTML failu. An HTMLDocument ir iekšā katrā JEditorPane, un tā bāze ir iestatīta uz URL konstruktora parametra bāzi. JEditorPanes, kas izveidoti, izmantojot šos konstruktorus, var apstrādāt relatīvos ceļus, jo HTMLDocument apvienojas ar relatīvo ceļu, lai izveidotu absolūtu ceļu.

Ja tiek izmantots pirmais konstruktors, parādītais teksts jāievieto pēc objekta izveides. Trešais konstruktors pieņem a Stīga kā saturs, bet bāze nav inicializēta. Tā kā es gribēju iegūt HTML marķējumu no XML taga, nevis faila, man bija jāizmanto vai nu pirmais, vai trešais konstruktors.

Kā mēs novēršam problēmu?

Pirms turpinu, atklāsim un atrisināsim vēl vienu mazāku problēmu. Visredzamākais veids, kā ievietot iezīmēšanu JEditorPane ir izmantot setText (virknes teksts). Tomēr šai metodei ir jāievada viss parādītais marķējums katru reizi, kad veicat izmaiņas. Ideālā gadījumā jaunā (-ās) atzīme (-es) būtu jāievieto esošajā tekstā. Jaunā marķējuma pievienošanai varat izmantot šādu kodu:

private void insertHTML (JEditorPane editor, String html, int location) throws IOException {// pieņem, ka redaktors jau ir iestatīts uz “text / html” tipa HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit (); Dokuments doc = editor.getDocument (); StringReader lasītājs = new StringReader (html); komplekts.lasīt (lasītājs, dokuments, atrašanās vieta); } 

Tagad, nonākot pie lietas būtības: kā tas notiek JEditorPane renderēt HTML? Katra veida JEditorPane atsauces gan a Dokuments un an EditorKit. Kad JEditorPane ir iestatīts ierakstīt "text / html", tajā ir HTMLDocument, kurā ir marķējums un HTMLEditorKit kas nosaka, kuras klases atveido katru marķējumā esošo tagu. Konkrēti HTMLEditorKit klasē ir HTMLFactory iekšējā klase, kuras izveidot (Element elem) metode faktiski pārbauda katru atsevišķo tagu. Šeit ir šīs rūpnīcas klases kods, kas apstrādā attēlu tagus:

 else if (kind == HTML.Tag.IMG) atgriezt jaunu ImageView (elem); 

Kā jūs tagad redzat, ImageView klase faktiski ielādē attēlu. Lai noteiktu attēla atrašanās vietu, getSourceURL () metodi sauc:

 privāts URL getSourceURL () {String src = (String) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); if (src == null) atgriež null; URL atsauce = ((HTMLDocument) getDocument ()). getBase (); mēģiniet {URL u = jauns URL (atsauce, src); atgriezties u; } catch (nepareizi veidotsURLException e) {return null; }} 

Lūk, getSourceURL () metode mēģina izveidot jaunu URL, lai atsauktu attēlu, izmantojot HTMLDocument bāze. Ja šī bāze ir nulle, tiek atgriezta vērtība null un attēla ielādes darbība tiek pārtraukta. Jūs vēlaties ignorēt šo uzvedību.

Ideālā gadījumā jūs apakšklasi ImageView klasē un ignorēt inicializēt (Element elem) metodi, kur tiek veikta attēlu ielāde. Diemžēl tā klase ir iepakojums aizsargāts, tāpēc jums ir jāizveido pilnīgi jauna klase. Vieglākais veids, kā to izdarīt, ir aizņemties un pēc tam modificēt kodu no oriģināla ImageView klasē. Sauksim to MyImageView.

Vispirms apskatiet kodu, kas ielādēja attēlu. Tālāk ir ņemts no inicializēt (Element elem) metode:

 URL src = getSourceURL (); if (src! = null) {Vārdnīcas kešatmiņa = (Vārdnīca) getDocument (). getProperty (IMAGE_CACHE_PROPERTY); if (kešatmiņa! = null) fImage = (Attēls) cache.get (src); else fImage = Toolkit.getDefaultToolkit (). getImage (src); } 

Šeit jūs iegūstat URL; ja tas ir nulle, jūs izlaižat attēla ielādi. In MyImageView, šis kods ir jāizpilda tikai tad, ja attēla atsauce ir URL. Šī ir metode, kuru varat pievienot, lai pārbaudītu attēla avotu:

 privātā būla vērtība irURL () virkne src = (virkne) fElement.getAttributes (). getAttribute (HTML.Attribute.SRC); return src.toLowerCase (). startsWith ("fails") 

Būtībā jūs iegūstat atsauci uz attēlu a formā Stīga un pārbaudiet, vai tas sākas ar vienu no diviem URL veidiem: failu vietējiem attēliem un http attālajiem attēliem. Jens Alfke, oriģināla autors javax.swing.text.html.ImageView klase, izmanto klases globālos mainīgos, tāpēc parametru nodošana funkcijām nav nepieciešama. Šeit globālais mainīgais ir fElement.

Jūs varat uzrakstīt kodu, kurā teikts ja (isURL ()) {}, bet ko jūs ievietojat citā paziņojumā par relatīvo ceļu? Tas ir pavisam vienkārši - vienkārši ielādējiet attēlu, kā parasti lietojumprogrammā:

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

Šeit nav īstas burvju, bet ir viens loms. The createImage (src) funkcija var atgriezties, pirms nav aizpildīti visi attēla pikseļi. Ja tas notiks, tiks parādīts bojāts attēls. Lai novērstu problēmu, varat vienkārši gaidīt, līdz attēla pikseļi ir pilnībā aizpildīti. Mana pirmā tieksme bija izmantot MediaTracker lai noteiktu, kad attēls bija gatavs, bet MediaTrackerkonstruktors pieprasa komponentu, kas attēlu atveido kā parametru. Tāpēc es vēlreiz aizņēmos kādu kodu no Džima Grehema java.awt.MediaTracker un uzrakstīju savu metodi, kā apiet problēmu:

 private void waitForImage () izmet InterruptedException {int w = fImage.getWidth (šis); int h = fImage.getHeight (šis); kamēr (taisnība)} 

Šī metode būtībā veic to pašu darbu kā MediaTracker's waitForID (int id) metodi, bet nav nepieciešams vecāku komponents. Zvanu uz šo metodi var veikt tūlīt pēc attēla izveides.

Ir neliela problēma, kas man jāpiemin, pirms turpinu. Apakšklasē nebija iespējams ImageView no javax.swing.text.html paketi, tāpēc es nokopēju visu failu, lai izveidotu savu klasi ar nosaukumu MyImageView, kuru neesmu ielicis iepakojumā. Oriģinālā ImageView kodu, ja attēlu nevar parādīt, jo tas nepastāv vai ir aizkavēts, tas no. ielādē noklusējuma bojātu attēlu javax.swing.text.html.icons iepakojums. Lai ielādētu šķelto attēlu, klase izmanto getResourceAsStream (virknes nosaukums) metode no Klase klasē. Faktiskais kods izskatās šādi:

 InputStream resurss = HTMLEditorKit.class.getResourceAsStream (MISSING_IMAGE_SRC); 

kur MISSING_IMAGE_SRC parametrs ir a Stīga ar saturu:

 MISSING_IMAGE_SRC = "ikonas" + System.getProperty ("file.separator", "/") + "image-fail.gif"; 

Šis fragments no ImageView pirmkods izskaidro Sun argumentāciju par getResourceAsStream (virknes nosaukums) metode sadalītā (-o) attēla (-u) ielādēšanai

 / * Kopēt resursu baitu masīvā. Tas ir * nepieciešams, jo vairākas pārlūkprogrammas uzskata, ka * Class.getResource ir drošības risks, jo to * var izmantot, lai ielādētu papildu klases. * Class.getResourceAsStream tikai atgriež neapstrādātus * baitus, kurus mēs varam pārveidot par attēlu. * / 

Ja jūs vēl neesat izlaidis šo sadaļu (es zinu, tas ir diezgan smalks!), Ļaujiet man paskaidrot, kāpēc es to pieminu. Ja jūs nezināt par šo rīcību, jūs nesapratīsit, kāpēc bojāti attēli netiek parādīti pareizi, un nevarēsiet novērst problēmu savā kodā. Lai novērstu problēmu, jums jāielādē savi attēli. Es izvēlējos turpināt izmantot to pašu metodi, bet tas nav īsti vajadzīgs. Iepriekš minētais brīdinājums ir paredzēts pārlūkprogrammām, kurās ir sīklietotnes, kuru drošības apsvērumi ierobežo piekļuvi diskam (protams, ja vien nav parakstīts). Jebkurā gadījumā šis raksts bija paredzēts lietošanai kopā ar lietojumprogrammu, tāpēc alternatīvas attēla ielādes metodes izmantošana nedrīkst radīt bažas.

Kad zvans uz getResourceAsStream (virknes nosaukums) ir izveidots, jūs varat iekļaut relatīvu ceļu uz attēlu, kā parādīts iepriekš. Iepriekš minētajā kodā bojātais attēls vienmēr tiks ielādēts no norādītā ceļa attiecībā pret HTMLEditorKit klasē. Piemēram, kopš HTMLEditorKit klase atrodas javax.swing.text.html, tas mēģinās ielādēt sadalīto attēlu image-fail.gif no javax.swing.text.html.icons. Tas attiecas arī uz vienkāršiem direktorijiem; klasēm nav jābūt iepakojumos. Visbeidzot, kopš HTMLEditorKit ir aizsargāts ar paketi, jums nav piekļuves tai getResourceAsStream (virknes nosaukums) metodi. Tā vietā jūs varat izmantot MyImageView klasē un salauž savus attēlus ikonu apakšdirektorijā. Kodu rinda izskatīsies šādi:

 InputStream resurss = MyImageView.class.getResourceAsStream (MISSING_IMAGE_SRC); 

Ja izvēlaties izmantot manai līdzīgu ieviešanu, jums būs jāizveido savas ikonas. Jūs joprojām varat izmantot ikonas, kas apvienotas ar Sun JDK, taču tam ir jāmaina resursa atrašanās vieta, lai relatīvā ceļa vietā izmantotu absolūto ceļu. Absolūtais ceļš ir:

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

Lai uzzinātu par lietošanu getResourceStream (virknes nosaukums), skatiet Javadoc informāciju Klase klase; saite ir sniegta resursos.

Šis raksts gandrīz pilnībā attiecas uz relatīvo ceļu pielāgošanu - bet ar ko tie ir saistīti? Līdz šim, ja izmantojat manis sniegto kodu, ceļus varēsiet izmantot tikai attiecībā pret vietu, kur sākāt lietojumprogrammu. Tas ir lieliski, ja visi jūsu attēli vienmēr atrodas šajos ceļos, bet tas ne vienmēr notiek. Es neiedziļināšos detalizēti, kā novērst šo problēmu, jo to var viegli novērst. Varat vai nu iestatīt lietojumprogrammas globālo mainīgo kaut kur savā lietojumprogrammā, vai arī iestatīt sistēmas mainīgo. In MyImageView, pirms attēla ielādes, jūs savienojat attēla relatīvo ceļu un absolūto ceļu, kas iegūts no globālā mainīgā. Ja tam nav jēgas, meklējiet processSrcPath () metode galīgajā avota kodā MyImageView.

Beidzot, MyImageView ir pabeigta. Tomēr jums ir jāizdomā, kā pateikt JEditorPane izmantot MyImageView tā vietā javax.swing.text.html.ImageView. The JEditorPane var atbalstīt trīs teksta formātus: vienkāršs, RTF un HTML. Ja JEditorPane parāda HTML, BasicHTML - apakšklase TextUI - tiek izmantots HTML renderēšanai. BasicHTML izmanto JEditorPane's HTMLEditorKit izveidot Skats. The HTMLEditorKit satur metodi, ko sauc getViewFactory (), kas atgriež sauktās iekšējās klases instanci HTMLFactory. The HTMLFactory satur metodi, ko sauc izveidot (Element elem), kas atgriež a Skats atbilstoši taga tipam. Konkrēti, ja tags ir IMG tagu, tas atgriež ImageView. Lai atgrieztu MyImageView, varat izveidot pats EditorKit sauca MyHTMLEditorKit, kuras apakšklases HTMLEditorKit. Jūsu iekšpusē MyHTMLEditorKit, jūs izveidojat jaunu iekšējo klasi ar nosaukumu MyHTMLFactory, kuras apakšklases HTMLFactory. Tajā iekšējā klasē jūs varat izveidot savu izveidot (Element elem) metodi, kas izskatās apmēram šādi:

 publiskais skats izveidot (Element elem) {Object o = elem.getAttributes (). getAttribute (StyleConstants.NameAttribute); if (o HTML.Tag instance) {HTML.Tag kind = (HTML.Tag) o; if (kind == HTML.Tag.IMG) atgriezt jaunu MyImageView (elem); } atgriešanās super.create (elem); } 

Vienīgais, kas tagad jādara, ir iestatīt JEditorPane izmantot MyHTMLEditorKit. Kods ir diezgan vienkāršs:

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