Innholdsoversikt for programutvikling
Når vi bruker profesjonelle programmer, så er vi vant med at de på en måte er knyttet sammen. Dersom det for eksempel i en e-post står en nettadresse og vi klikker på den, så er vi vant med at den nettleseren vi pleier å bruke starter opp, om den ikke allerede er i gang, og at den viser den nettsida som det var adresse til i e-posten. Dersom det omvendt på en nettside er angitt en e-postadresse og vi klikker på den, så er vi vant med at e-postprogrammet vårt starter opp, om det ikke allerede er i gang, og at det gjør seg klart til å skrive en e-post nettopp til den adressen som var angitt. Et annet eksempel på slik samvirkning er at dersom vi i filutforskeren vår (for eksempel Windows Explorer) dobbeltklikker på et filnavn, så vil det programmet som er satt opp til å behandle vedkommende filtype bli aktivert til å behandle nettopp den fil vi har dobbeltklikket på. Dersom for eksempel TextPad er assosiert med javafiler, så vil TextPad da vise vedkommende javafil.
Fra og med Javaversjon 6 er det mulig også for et javaprogram å knytte seg til andre programmer vi har liggende på "skrivebordet" på tilsvarende måte som det er nevnt eksempler på ovenfor. Et javaprogram kan dermed utføre følgende operasjoner:
Vi skal se nærmere på hvordan det er mulig å legge den nevnte funksjonaliteten
inn i et javaprogram. Det skal vi gjøre ved å ta for oss programmet
DesktopDemo.java
fra The Java Tutorials. Når programmet har startet opp, så blir følgende
vindu vist:
Ikonet som blir brukt i øverste venstre hjørne av programvinduet har bildefil
desk32.gif
.
Det blir lagt inn som følge av at metoden loadFrameIcon
blir kalt opp
i vinduets konstruktør. Som vi ser av metodedefinisjonen som er gjengitt under,
må bildefila ligge i en katalog images
under katalogen som
inneholder vinduets class
-fil.
305 /** 306 * Load the "desktop" icon into our frame window. 307 */ 308 private void loadFrameIcon() 309 { 310 URL imgUrl = null; 311 ImageIcon imgIcon = null; 312 313 imgUrl = DesktopDemo.class.getResource("images/desk32.gif"); 314 imgIcon = new ImageIcon(imgUrl); 315 Image img = imgIcon.getImage(); 316 this.setIconImage(img); 317 }
Funksjonalitet for de operasjonene som er omtalt i innledningen er lagt inn i klassen
Desktop
.
Men en forutsetning for at et javaprogram skal kunne ha funksjonaliteten,
er at den plattform som programmet blir kjørt på tilbyr støtte for den for javaprogrammer.
Det kan vi få sjekket ved kall på metoden
isDesktopSupported
.
På Linux, for eksempel, er dette avhengig av Gnome-biblioteker. Dersom disse ikke er
tilgjengelige, så vil isDesktopSupported
returnere
false
. Dersom metoden
isteden returnerer true
, så er
alt i orden, da kan vi få tak i det relevante Desktop
-objektet som vi
trenger ved å gjøre kall på metoden
getDesktop
.
I programmet vårt blir et slikt objekt lagret på klassenivå så sant støtte foreligger.
Når støtte foreligger, så kan vi også gå videre og sjekke i detalj hvilken
type støtte som foreligger. Klassen
Desktop.Action
inneholder en opplisting av konstanter som kan brukes til å sjekke om de forskjellige operasjonene
blir støttet. I programmet DesktopDemo
blir dette sjekket i
konstruktøren for programvinduet. Den starter med følgende instruksjon:
74 initComponents();
Metoden initComponents
oppretter skjermkomponenter og knytter
lytteobjekter til dem. Vi skal se nærmere på lytteobjektene etter hvert som vi
tar for oss de enkelte operasjonene. Neste instruksjon er
77 disableActions();
Metoden disableActions
deaktiverer alle
operasjoner som har å gjøre med den funksjonaliteten som er omtalt i innledningen.
Operasjonene vil bli aktivert igjen når det er blitt sjekket om det foreligger
støtte for dem. Komponentene som blir deaktivert er de forskjellige tekstfeltene
og knappene i vinduet.
Metoden ser ut som følger:
478 /** 479 * Disable all graphical components until we know whether their 480 * functionality is supported. 481 */ 482 private void disableActions() 483 { 484 txtBrowserURI.setEnabled(false); 485 btnLaunchBrowser.setEnabled(false); 486 487 txtMailTo.setEnabled(false); 488 btnLaunchEmail.setEnabled(false); 489 490 rbEdit.setEnabled(false); 491 rbOpen.setEnabled(false); 492 rbPrint.setEnabled(false); 493 494 txtFile.setEnabled(false); 495 btnLaunchApplication.setEnabled(false); 496 btnFile.setEnabled(false); 497 }
Neste trinn er å sjekke om det foreligger støtte for den funksjonaliteten vi er interessert i og i så fall aktivere igjen de komponentene som det foreligger støtte for. Dette gjøres ved å utføre følgende instruksjoner:
80 if (Desktop.isDesktopSupported()) 81 { 82 desktop = Desktop.getDesktop(); 83 // now enable buttons for actions that are supported. 84 enableSupportedActions(); 85 }
Metoden enableSupportedActions
har følgende innhold:
441 /** 442 * Enable actions that are supported on this host. The actions are: 443 * open browser, open email client, and open, edit, and print files 444 * using their associated application 445 */ 446 private void enableSupportedActions() 447 { 448 if (desktop.isSupported(Desktop.Action.BROWSE)) 449 { 450 txtBrowserURI.setEnabled(true); 451 btnLaunchBrowser.setEnabled(true); 452 } 453 if (desktop.isSupported(Desktop.Action.MAIL)) 454 { 455 txtMailTo.setEnabled(true); 456 btnLaunchEmail.setEnabled(true); 457 } 458 if (desktop.isSupported(Desktop.Action.OPEN)) 459 { 460 rbOpen.setEnabled(true); 461 } 462 if (desktop.isSupported(Desktop.Action.EDIT)) 463 { 464 rbEdit.setEnabled(true); 465 } 466 if (desktop.isSupported(Desktop.Action.PRINT)) 467 { 468 rbPrint.setEnabled(true); 469 } 470 if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) 471 { 472 txtFile.setEnabled(true); 473 btnLaunchApplication.setEnabled(true); 474 btnFile.setEnabled(true); 475 } 476 }
Vi skal etter tur ta for oss hvordan de tre typene av funksjonalitet som er nevnt i innledningen er programmert i dette programmet.
Som det går fram av programvinduet, er det tekstfeltet bak URI:
og
knappen Launch Browser
som har med nettleser å gjøre.
Tekstfeltet txtBrowserURI
og knappen
btnLaunchBrowser
er tilknyttet lytteobjekt på følgende måte:
129 txtBrowserURI.addActionListener(new ActionListener() 130 { 131 public void actionPerformed(ActionEvent e) 132 { 133 onLaunchBrowser(null); 134 } 135 }); 136 137 btnLaunchBrowser.addActionListener(new ActionListener() 138 { 139 public void actionPerformed(ActionEvent evt) 140 { 141 onLaunchBrowser(evt); 142 } 143 });
Vi ser at både tekstfeltet og knappen gjør kall på metoden
onLaunchBrowser
når de blir aktivert. Metoden er definert på følgende måte:
405 /** 406 * Launch the default browser with the text provided by the user. 407 * 408 */ 409 private void onLaunchBrowser(ActionEvent evt) 410 { 411 URI uri = null; 412 try 413 { 414 uri = new URI(txtBrowserURI.getText()); 415 desktop.browse(uri); 416 } 417 catch (IOException ioe) 418 { 419 System.out.println("The system cannot find the " + uri + 420 " file specified"); 421 } 422 catch (URISyntaxException use) 423 { 424 System.out.println("Illegal character in path"); 425 } 426 }
Dersom vi i tekstfeltet for URI lar være å skrive noe, men bare trykker på Returtasten mens markøren befinner seg i feltet, eller klikker på knappen bak feltet, så vil det være systemets filutforsker som blir aktivert og vist på skjermen, det vil si Windows Explorer på Windows-systemer. Når programmet blir kjørt ved hjelp av NetBeans, så viser filutforskeren innholdet av katalogen for vedkommende NetBeans-prosjekt. Skriver vi i tekstfeltet en nettadresse og enten trykker på Returtast eller klikker på knappen bak tekstfeltet, så vil nettleseren vise vedkommende nettside så sant den er tilgjengelig.
Programvinduet indikerer at det er tekstfeltet bak E-mail:
og knappen
Launch Mail
som har med e-post å gjøre. Tekstfeltet
txtMailTo
og knappen btnLaunchEmail
er tilknyttet lytteobjekt på følgende måte:
146 txtMailTo.addActionListener(new ActionListener() 147 { 148 public void actionPerformed(ActionEvent e) 149 { 150 onLaunchMail(null); 151 } 152 }); 153 154 btnLaunchEmail.setText("Launch Mail"); 155 btnLaunchEmail.addActionListener(new ActionListener() 156 { 157 public void actionPerformed(ActionEvent evt) 158 { 159 onLaunchMail(evt); 160 } 161 });
Vi ser at både tekstfeltet og knappen gjør kall på metoden
onLaunchMail
når de blir aktivert. Den er definert på følgende måte:
378 /** 379 * Launch the default email client using the "mailto" protocol and 380 * the text supplied by the user. 381 * 382 */ 383 private void onLaunchMail(ActionEvent evt) 384 { 385 String mailTo = txtMailTo.getText(); 386 URI uriMailTo = null; 387 try 388 { 389 if (mailTo.length() > 0) 390 { 391 uriMailTo = new URI("mailto", mailTo, null); 392 desktop.mail(uriMailTo); 393 } 394 else 395 { 396 desktop.mail(); 397 } 398 } 399 catch (IOException | URISyntaxException ioe) 400 { 401 ioe.printStackTrace(); 402 } 403 }
Dersom vi i tekstfeltet for E-mail lar være å skrive noe, men bare trykker på Returtasten mens markøren befinner seg i feltet, eller klikker på knappen bak feltet, så vil det være systemets standard e-postprogram som blir aktivert og gjør seg klar til sending av e-post, uten at det er lagt inn noen adresse for mottaker. Gjør vi det samme etter at vi i tekstfeltet har skrevet noe, vil det samme skje, i tilleg til at det vi har skrevet vil stå som mottaker for e-posten. Det blir ikke foretatt noe sjekk på at det er en adresse med gyldig format.
Dette er den mest komplekse operasjonen å programmere, siden det er så mange alternativer som skal håndteres. Bildet av programvinduet er lagt inn tidligere, men for at det skal være lettere å referere til det, legger jeg inn bildet på nytt.
De tre radioknappene rbEdit, rbOpen
og rbPrint
,
med tilhørende tekst Open, Edit
og Print
,
skal
brukes til å bestemme hva som skal gjøres med den fil som blir valgt.
Som nevnt tidligere, så inneholder klassen
Desktop.Action
en opplisting av konstanter som kan brukes til å identifisere de forskjellige typene aksjoner.
Det som er aktuelt i dette tilfelle, er å åpne fil, editere fil, eller printe ut fil.
For å registre det valget som brukeren foretar, er det på klassenivå lagt inn et
datafelt av type Desktop.Action
:
66 private Desktop.Action action = Desktop.Action.OPEN;
Vi ser at startverdien er alternativet for å åpne fil, noe som gjenspeiler seg i programvinduet ved at det er radioknappen for åpning som er valgt i utgangspunktet. Under programkjøring blir datafeltet tilordnet ny verdi ved at hver radioknapp er tilordnet lytteobjekt på følgende måte:
172 rbOpen.addActionListener(new ActionListener() 173 { 174 public void actionPerformed(ActionEvent evt) 175 { 176 onOpenAction(evt); 177 } 178 }); 179 180 181 rbEdit.addActionListener(new ActionListener() 182 { 183 public void actionPerformed(ActionEvent evt) 184 { 185 onEditAction(evt); 186 } 187 }); 188 189 190 rbPrint.addActionListener(new ActionListener() 191 { 192 public void actionPerformed(ActionEvent evt) 193 { 194 onPrintAction(evt); 195 } 196 });
De tre metodene som blir kalt opp av de tre lytteobjektene er programmert på følgende måte:
319 /* 320 * Set the Desktop.Action to PRINT before invoking the default 321 * application. 322 */ 323 private void onPrintAction(ActionEvent evt) 324 { 325 action = Desktop.Action.PRINT; 326 } 327 328 /** 329 * Set the Desktop.Action to EDIT before invoking the default 330 * application. 331 */ 332 private void onEditAction(ActionEvent evt) 333 { 334 action = Desktop.Action.EDIT; 335 } 336 337 /** 338 * Set the Desktop.Action to OPEN before invoking the default 339 * application. 340 */ 341 private void onOpenAction(ActionEvent evt) 342 { 343 action = Desktop.Action.OPEN; 344 }
Vi ser at virkningen er å sette ny verdi for datafeltet action
.
Tekstfeltet txtFile
bak File:
har naturligvis som
oppgave å inneholde navn eller filsti for den fil som skal behandles. Det er
tilordnet lytteobjekt på følgende måte:
164 txtFile.addActionListener(new ActionListener() 165 { 166 public void actionPerformed(ActionEvent e) 167 { 168 onLaunchDefaultApplication(null); 169 } 170 });
Vi vet at når vi skal velge fil, er det svært praktisk å kunne bruke en filvelger.
Knappen btnFile
merket ...
bak tekstfeltet er ment til dette formål.
Den er tilknyttet lytteobjekt på følgende måte:
206 btnFile.addActionListener(new ActionListener() 207 { 208 public void actionPerformed(ActionEvent evt) 209 { 210 onChooseFile(evt); 211 } 212 });
Metoden onChooseFile
som lytteobjektet kaller opp har følgende innhold:
428 private void onChooseFile(ActionEvent evt) 429 { 430 if (evt.getSource() == btnFile) 431 { 432 int returnVal = fc.showOpenDialog(DesktopDemo.this); 433 if (returnVal == JFileChooser.APPROVE_OPTION) 434 { 435 file = fc.getSelectedFile(); 436 txtFile.setText(file.getAbsolutePath()); 437 } 438 } 439 }
Metoden gjør bruk av JFileChooser
-objektet fc
opprettet på
klassenivå og den tilordner verdi til datafeltet file
som også er deklarert
på klassenivå:
63 JFileChooser fc = new JFileChooser(); 64 File file;
I tillegg legger metoden filstien for den valgte fil inn som tekst i tekstfeltet for filvalg.
Når vi har valgt fil, enten ved å skrive filnavn eller filsti i tekstfeltet for dette,
eller ved å bruke filvelger, så er vi klar til å behandle den. Én mulighet
for dette er å trykke Returtast mens markøren står i tekstfeltet for filnavn, en
annen mulighet er å klikke på knappen btnLaunchApplication
med tekst
Launch Application
. Den er tilordnet lytteobjekt på følgende måte:
198 btnLaunchApplication.addActionListener(new ActionListener() 199 { 200 public void actionPerformed(ActionEvent evt) 201 { 202 onLaunchDefaultApplication(evt); 203 } 204 });
Vi ser at lytteobjektet gjør kall på metoden onLaunchDefaultApplication
.
Det er den samme metoden som lytteobjektet for tekstfeltet gjør kall på. Den er definert slik:
346 /** 347 * Launch the default application associated with a specific 348 * filename using the preset Desktop.Action. 349 * 350 */ 351 private void onLaunchDefaultApplication(ActionEvent evt) 352 { 353 String fileName = txtFile.getText(); 354 File file = new File(fileName); 355 356 try 357 { 358 switch (action) 359 { 360 case OPEN: 361 desktop.open(file); 362 break; 363 case EDIT: 364 desktop.edit(file); 365 break; 366 case PRINT: 367 desktop.print(file); 368 break; 369 } 370 } 371 catch (IOException | IllegalArgumentException ioe) 372 { 373 System.out.println("Cannot perform the given operation to the " 374 + file + " file"); 375 } 376 }
Vi ser at avhengig av hvilken radioknapp som er valgt for øyeblikket, så vil vedkommende fil, så sant det er mulig, bli åpnet for visning, for editering, eller den vil bli sendt til printeren for utskrift.
Det viste seg at programmet som ble hentet inn fra The Java Tutorials hadde en liten mangel.
Dersom operasjon for filbehandling ble aktivert uten at det var skrevet noe i tekstfeltet
for filnavn, ble det kastet ut en IllegalArgumentException
. Jeg har lagt inn
i programmet oppfanging av en slik exception, slik at programmet nå virker greit også i dette
tilfelle. Men det viser seg også at det ikke blir akseptert filnavn som inneholder
mellomrom (blanke tegn). For sammenhengende filnavn virker det som det skal.
For øvrig så ser vi av programkoden at i tilfelle en operasjon ikke lar seg utføre,
slik at det isteden blir skrevet ut en melding til brukeren om dette, så vil meldingen bli skrevet
i det såkalte konsollvinduet, eller i Output-feltet når programmet blir kjørt ved hjelp
av NetBeans. For et profesjonelt program burde selvsagt denne meldingen kommet i en
meldingsboks.
Innholdsoversikt for programutvikling
Copyright © Kjetil Grønning og Eva Hadler Vihovde, revidert 2014