Innholdsoversikt for programutvikling
For mange vindusprogrammer er det slik at brukeren har mulighet til å tilpasse programmet
til sine egne ønsker. Det kan dreie seg om farger på komponenter, type og størrelse for fonter,
hva som skal vises på skjermen av diverse typer skjermkomponenter, størrelse på programvinduet og
hvor dette skal ligge på skjermen, etc. Vi venter oss dessuten at slike opplysninger skal lagres og
automatisk brukes på nytt når vi starter opp igjen programmet for en ny brukersesjon. I dette
notatet skal vi se på hvilke muligheter vi har for å få til dette for et javaprogram.
Den tradisjonelle, og enkleste måten å gjøre det på, er å lagre slike opplysninger i såkalte
property-filer. Måten er vanlig brukt i andre sammenhenger enn i forbindelse med javaprogrammer.
Framgangsmåten har imidlertid sine svakheter. Fra og med java-versjon 1.4
ble det innført en ny, sikrere og mer avansert måte å lagre slike opplysninger på.
Siden den første måten har vært mye brukt og fortsatt brukes i en del sammenhenger, er det
likevel nyttig å kjenne til også den. Vi skal derfor i dette notatet ta for oss begge
framgangsmåtene, og vi starter med den første, enkle måten å gjøre det på. Framstillingen er,
særlig i omtalen av Preferences
, basert på framstillingen i boka Core Java
av Cay S. Horstmann og Gary Cornell.
Det engelske ordet map pleier vi på norsk, iallfall i matematisk sammenheng, å oversette
med ordet avbildning. Vi kjenner map-begrepet fra javas Collections
-bibliotek.
Det er der beskrevet som en samling av det som kalles (nøkkel, verdi)-par. En omtale av det er gitt
i notatet Avbildning (Map). Slike avbildninger kan også
brukes til å lagre konfigurasjonsinformasjon for programmer. Avbildningene vil da være karakterisert ved følgende
spesielle egenskaper:
I java er denne typen avbildning implementert av klassen
java.util.Properties
.
(Den er subklasse til Hashtable
, som er en type avbildning, se kommentaren
HashMap
versus Hashtable
.)
Et slikt Properties
-objekt med en samling av (nøkkel, verdi)-par kan inneholde en annen
samling av samme type med default-verdier. Denne vil bli gjennomsøkt i tilfelle den søkte egenskapsnøkkel
ikke finnes i den originale samlingen.
Siden Properties
-klassen arver fra Hashtable
, er det mulig å bruke metodene
put
og putAll
for lagring av verdier. Det blir imidlertid sterkt anbefalt
ikke å gjøre det, siden de vil gjøre det mulig å legge inn nøkler eller verdier som ikke er av type
String
. Isteden blir det anbefalt å bruke metoden setProperty
. Denne vil
sjekke at verdiene er av riktig datatype og i sin tur gjøre kall på den nevnte put
-metode.
For å lagre
på fil de egenskaper som er satt, bruker vi metoden store
. Et kall på denne vil mislykkes
dersom det finnes (nøkkel, verdi)-par som har komponenter av en annen type enn String
.
(Den tidligere save
-metoden for lagring er nå merket som foreldet og bør derfor ikke brukes.)
For å lese inn igjen fra fil lagrede verdier, bruker vi metoden load
. (Det er også mulig
å lagre og lese inn igjen egenskapene i form av en xml-fil.)
Det er vanlig å lagre programegenskaper i en underkatalog til brukerens såkalte hjemmekatalog. Navnet for en slik underkatalog er vanlig å starte med et punkt-tegn. (På et UNIX-system indikerer dette at det er en systemkatalog som er skjult for brukeren.) I programeksemplet som følger nedenfor skal vi følge denne konvensjonen.
Brukerens settinger (brukeregenskaper) er lagret nettopp i et Properties
-objekt.
For å få tak i dette kan vi skrive
Properties brukeregenskaper = System.getProperties();
Nøkkelen for brukerens hjemmekatalog er
"user.home"
. Vi kan få tak i filstien for hjemmekatalogen
direkte ved å bruke denne nøkkelen:
String hjemmekatalog = System.getProperty("user.home");
Vi ønsker for program å lagre brukerens ønske om bredde, høyde og plassering for programvinduet,
samt brukerens valg av vindustittel. Men samtidig vil vi sørge for å legge inn default-verdier som
kan brukes i tilfelle det ikke er mulig å lese inn de nevnte egenskaper fra fil. For å gjøre det,
skal vi omarbeide programmet Vindustest
som ble brukt
i forbindelse med en generell omtale av vinduer. I eksemplet ble vinduets bredde og høyde satt til en
firedel av skjermens bredde og høyde. Disse verdiene skal vi nå bruke som defaultverdier. Plassering
av vinduet på skjermen overlot vi til plattformens vindussystem. Som defaultplassering skal vi nå
velge skjermens øverste venstre hjørne. Vi skal overlate til brukeren å velge tittel for vinduet
når programmet starter opp første gangen.
På klassenivå har vi datafelt for lagrede settinger og hvilken fil som inneholder disse;
23 class Vindu extends JFrame 24 { 25 private File egenskapsfil; 26 private Properties settinger; ...
Instruksjoner for å lese inn lagrede verdier for de egenskaper som er nevnt ovenfor må vi legge inn i konstruktøren. Instruksjonene kan se ut som følger:
35 //Får tak i ønsket posisjon, størrelse og tittel fra 36 //lagrede egenskaper. 37 String brukerkatalog = System.getProperty("user.home"); 38 File egenskapskatalog = new File(brukerkatalog, 39 ".programutvikling"); 40 if (!egenskapskatalog.exists()) 41 { 42 egenskapskatalog.mkdir(); 43 } 44 egenskapsfil = new File(egenskapskatalog, 45 "propertiestest.egenskaper"); 46 47 Properties defaultsettinger = new Properties(); 48 defaultsettinger.setProperty("venstre", "0"); 49 defaultsettinger.setProperty("topp", "0"); 50 defaultsettinger.setProperty("bredde", "" + skjermbredde / 4); 51 defaultsettinger.setProperty("høyde", "" + skjermhøyde / 4); 52 defaultsettinger.setProperty("tittel", ""); 53 settinger = new Properties(defaultsettinger); 54 55 if (egenskapsfil.exists()) 56 { 57 try (FileInputStream inn = new FileInputStream(egenskapsfil)) 58 { 59 settinger.load(inn); 60 } 61 catch (IOException ex) 62 { 63 ex.printStackTrace(); 64 } 65 } 66 67 int venstre = Integer.parseInt(settinger.getProperty("venstre")); 68 int topp = Integer.parseInt(settinger.getProperty("topp")); 69 int bredde = Integer.parseInt(settinger.getProperty("bredde")); 70 int høyde = Integer.parseInt(settinger.getProperty("høyde")); 71 72 setSize(bredde, høyde); 73 setLocation(venstre, topp); 74 75 //Dersom det ikke er satt noen tittel, spør bruker: 76 String tittel = settinger.getProperty("tittel"); 77 if (tittel.equals("")) 78 { 79 tittel = JOptionPane.showInputDialog( 80 "Skriv ønsket tittel for vinduet:"); 81 } 82 if (tittel == null) 83 { 84 tittel = ""; 85 } 86 setTitle(tittel);
Først får vi tak i brukerens hjemmekatalog, siden det er under denne at katalogen
med programegenskaper skal ligge. Som navn på denne underkatalogen er brukt
".programutvikling"
, og dersom den ikke eksisterer, blir den opprettet.
Fila med programegenskaper forutsettes å hete
"propertiestest.egenskaper"
.
(En slik fil vil bli opprettet eller overskrevet når programmet blir avsluttet.)
De neste instruksjonene oppretter et Properties
-objekt med defaultegenskaper.
Det brukes som konstrukørparameter ved opprettelsen av Properties
-objektet
som skal inneholde programegenskapene:
53 settinger = new Properties(defaultsettinger);
Virkningen av dette blir at defaultegenskapene blir inneholdt som et eget
Properties
-objekt i objektet settinger
, og defaultegenskaper
blir brukt dersom det for tilsvarende nøkler ikke finnes verdi i objektet settinger
.
Dersom fila med programegenskaper finnes, blir innholdet i den lest inn i
Properties
-objektet ved hjelp av load
-metoden. Deretter hentes verdiene
fra Properties
-objektet ut og brukes til å sette verdier for vinduets bredde, høyde
og plassering på skjermen. (I tilfelle det ikke fantes fil med lagrede egenskaper, er det
defaultverdiene som blir brukt.) Dersom den tittelstrengen som leses ut av egenskapene er tom,
blir brukeren bedt om å bestemme tittel.
Mens programmet kjører, kan brukeren endre størrelse og plassering for vinduet. Vi ønsker at brukerens
valg for dette skal lagres og brukes automatisk når programmet starter opp igjen etter å ha vært avsluttet.
For å oppnå dette, må vi knytte en vinduslytter til programmet, slik den sørger for å lagre egenskapene på fil
før programmet blir avsluttet. Den indre klassen Vinduslytter
definerer lytteobjektet:
102 private class Vinduslytter extends WindowAdapter 103 { 104 public void windowClosing(WindowEvent e) 105 { 106 settinger.setProperty("venstre", "" + getX()); 107 settinger.setProperty("topp", "" + getY()); 108 settinger.setProperty("bredde", "" + getWidth()); 109 settinger.setProperty("høyde", "" + getHeight()); 110 settinger.setProperty("tittel", getTitle()); 111 try (FileOutputStream ut = new FileOutputStream(egenskapsfil)) 112 { 113 settinger.store(ut, "Programønsker"); 114 } 115 catch (IOException ex) 116 { 117 ex.printStackTrace(); 118 } 119 System.exit(0); 120 } 121 }
Vi setter altså som programegenskaper de verdiene som gjelder for øyeblikket og skriver til
fil Properties
-objektet ved hjelp av store
-metoden. Fila med programegenskaper vil
være en tektsfil som vi kan kikke på, og eventuelt redigere, i en editor. Den kan ha følgende innhold:
#Programønsker #Mon Mar 19 14:25:44 CET 2012 h\u00F8yde=392 topp=442 venstre=699 bredde=472 tittel=Testvindu
Vi ser at i nøkkelverdien "høyde"
blir bokstaven ø
lagret som
\u00F8
, som er et såkalt Unicode escape-tegn.
Merknad På skolens nettverk er vi gitt begrensede rettigheter til å se
hva som ligger av filer på hjemmeområdet vårt. På grunn av dette er det ikke mulig
å få opp system
-katalogen for eksempel i Windows Explorer. Men når vi vet
adressen til en fil under denne katalogen, kan vi likevel få sett på innholdet ved å bruke
for eksempel TextPad. Når du bruker Open-kommandoen i TextPad, kan du stille den inn på H:,
og så i tekstfeltet for filnavn skrive system\.programutvikling\propertiestest.egenskaper
.
Fullstendig programkode er gjengitt nedenfor og finnes i fila
Vindustest.java
. For at
programmet skal finne det ikon som brukes må bildefila
hiologo_90x100.gif"
for dette ligge i underkatalogen bilder
under katalogen som inneholder programmets
class
-filer.
1 import java.awt.*; 2 import javax.swing.*; 3 import java.awt.event.*; 4 import java.io.*; 5 import java.net.URL; 6 import java.util.Properties; 7 8 public class Vindustest 9 { 10 public static void main(String[] args) 11 { 12 EventQueue.invokeLater(new Runnable() 13 { 14 public void run() 15 { 16 Vindu vindu = new Vindu(); 17 vindu.setVisible(true); 18 } 19 }); 20 } 21 } 22 23 class Vindu extends JFrame 24 { 25 private File egenskapsfil; 26 private Properties settinger; 27 28 public Vindu() 29 { 30 Toolkit verktøykasse = Toolkit.getDefaultToolkit(); 31 Dimension skjermdimensjon = verktøykasse.getScreenSize(); 32 int skjermbredde = skjermdimensjon.width; 33 int skjermhøyde = skjermdimensjon.height; 34 35 //Får tak i ønsket posisjon, størrelse og tittel fra 36 //lagrede egenskaper. 37 String brukerkatalog = System.getProperty("user.home"); 38 File egenskapskatalog = new File(brukerkatalog, 39 ".programutvikling"); 40 if (!egenskapskatalog.exists()) 41 { 42 egenskapskatalog.mkdir(); 43 } 44 egenskapsfil = new File(egenskapskatalog, 45 "propertiestest.egenskaper"); 46 47 Properties defaultsettinger = new Properties(); 48 defaultsettinger.setProperty("venstre", "0"); 49 defaultsettinger.setProperty("topp", "0"); 50 defaultsettinger.setProperty("bredde", "" + skjermbredde / 4); 51 defaultsettinger.setProperty("høyde", "" + skjermhøyde / 4); 52 defaultsettinger.setProperty("tittel", ""); 53 settinger = new Properties(defaultsettinger); 54 55 if (egenskapsfil.exists()) 56 { 57 try (FileInputStream inn = new FileInputStream(egenskapsfil)) 58 { 59 settinger.load(inn); 60 } 61 catch (IOException ex) 62 { 63 ex.printStackTrace(); 64 } 65 } 66 67 int venstre = Integer.parseInt(settinger.getProperty("venstre")); 68 int topp = Integer.parseInt(settinger.getProperty("topp")); 69 int bredde = Integer.parseInt(settinger.getProperty("bredde")); 70 int høyde = Integer.parseInt(settinger.getProperty("høyde")); 71 72 setSize(bredde, høyde); 73 setLocation(venstre, topp); 74 75 //Dersom det ikke er satt noen tittel, spør bruker: 76 String tittel = settinger.getProperty("tittel"); 77 if (tittel.equals("")) 78 { 79 tittel = JOptionPane.showInputDialog( 80 "Skriv ønsket tittel for vinduet:"); 81 } 82 if (tittel == null) 83 { 84 tittel = ""; 85 } 86 setTitle(tittel); 87 88 //Bildefil for ikon er plassert i underkatalogen bilder: 89 String bildefil = "bilder/hiologo_90x100.gif"; 90 URL kilde = Vindu.class.getResource(bildefil); 91 if (kilde != null) 92 { 93 ImageIcon bilde = new ImageIcon(kilde); 94 Image ikon = bilde.getImage(); 95 setIconImage(ikon); 96 } 97 addWindowListener(new Vinduslytter()); 98 String hjemmekatalog = System.getProperty("user.home"); 99 System.out.println(hjemmekatalog); 100 } 101 102 private class Vinduslytter extends WindowAdapter 103 { 104 public void windowClosing(WindowEvent e) 105 { 106 settinger.setProperty("venstre", "" + getX()); 107 settinger.setProperty("topp", "" + getY()); 108 settinger.setProperty("bredde", "" + getWidth()); 109 settinger.setProperty("høyde", "" + getHeight()); 110 settinger.setProperty("tittel", getTitle()); 111 try (FileOutputStream ut = new FileOutputStream(egenskapsfil)) 112 { 113 settinger.store(ut, "Programønsker"); 114 } 115 catch (IOException ex) 116 { 117 ex.printStackTrace(); 118 } 119 System.exit(0); 120 } 121 } 122 }
Preferences
I eksemplet i avsnittet foran så vi at det fungerte greit med å lagre brukerpreferanser i et
Properties
-objekt som ble skrevet til fil før programslutt og lest inn igjen fra
fil ved programstart. Men denne teknikken har sine svakheter: Det finnes ingen standarder for hvor
slike filer skal lagres på disk, eller hva de skal hete. Derfor blir det svært vanskelig å ta
backup av slik informasjon, eller overføre den fra én maskin til en annen. Ved økende
antall applikasjoner øker også muligheten for navnekonflikt mellom filnavn.
For å få en bedre og sikrere måte å lagre data om applikasjonstilpasninger på, uten de svakheter
som er nevnt ovenfor, ble det fra og med javaversjon 1.4 innført opplegget med Preferences
.
Det implementerer på en plattformuavhengig måte lagring av slike data. Hvor dataene fysisk lagres på
en plattform, vil være plattformavhengig. På Windows-systemer lagres de på det sted som på engelsk
kalles registry, mens de på Linux-systemer isteden lagres i det lokale filsystemet.
Preferences
-lageret har en trestruktur bygget opp på tilsvarende måte som det blir
anbefalt for pakkenavn, slik at vi får stinavn av typen /no/hio/minapplikasjon
(når det
dreier seg om et program installert her på skolen) fram til Preferences
-noder med informasjon.
Det kan være flere parallelle trær av denne typen. Hver programbruker har sitt eget tre, og et
ekstra tre, kalt systemtreet, er tilgjengelig for settinger som er felles for alle brukere.
Preferences
-klassen
bruker operativsystemets begrep for 'aktuell bruker' til å
aksessere det riktige treet til en aktuell bruker. Javas API for Preferences
ligger
i pakken java.util.prefs
.
Hver Preferences
-node i treet inneholder en tabell av (nøkkel, verdi)-par der vi
kan lagre verdier. Nøklene må være av type String
, mens verdiene kan være tall, strenger,
logiske verdier, eller byte
-arrayer.
For å gjøre aksess på en node i brukertreet, må vi starte med å få tak i rotnoden i dette:
Preferences rot = Preferences.userRoot();
Den tilsvarende rotnoden i systemtreet får vi tak i ved å skrive
Preferences rot = Preferences.systemRoot();
Når vi har fått tak i rota, kan vi videre få tak i andre noder ved å bruke stien fram til noden. Med det eksemplet på nodesti som ble brukt ovenfor, blir det som følger:
Preferences node = rot.node("/no/hio/minapplikasjon");
Dersom noden ikke eksisterer, vil den, i tillegg til eventuelle supernoder, bli opprettet og returnert.
Dersom stinavnet til en node er lik pakkenavnet til en klasse, kan vi bruke en snarvei.
På grunnlag av et objekt obj
av vedkommende klasse kan vi for en node i brukertreet skrive
Preferences node = Preferences.userNodeForPackage(obj.getClass());
Tilsvarende instruksjon for en node i systemtreet vil være
Preferences node = Preferences.systemNodeForPackage(obj.getClass());
Istedenfor objektreferansen obj
, vil det vanligvis være aktuelt å bruke
this
-referansen.
Når vi har fått tak i en Preferences
-node, kan vi få tak i lagret verdi ved å kalle opp
en av følgende metoder på noden:
String get(String nøkkel, String defaultverdi) int getInt(String nøkkel, int defaultverdi) long getLong(String nøkkel, long defaultverdi) float getFloat(String nøkkel, float defaultverdi) double getDouble(String nøkkel, double defaultverdi) boolean getBoolean(String nøkkel, boolean defaultverdi) byte[] getByteArray(String nøkkel, byte[] defaultverdi)
Merk deg at alltid når du skal hente ut en verdi, så må du samtidig angi en defaultverdi. Det er flere grunner til dette kravet. Det kan være at data mangler fordi brukeren aldri spesifiserte noen foretrukket verdi. Det kan videre tenkes at enkelte systemer med begrenset lagringsplass ikke har lagret noen brukerpreferanser i det hele tatt. Mobile systemer kan dessuten midlertidig være uten forbindelse med lageret.
De tilsvarende metodene for å legge inn verdier i en node har tilsvarende navn som listet opp
ovenfor, bare at vi bruker put
istedenfor get
, og at metodene ikke returnerer noe:
put(String nøkkel, String verdi)
putInt(String nøkkel, int verdi)
etc.
Sentrale lagre slik som registry hos Windows, lider vanligvis under to problemer:
Javas Preferences
-klasse har en løsning for det andre av disse to problemene:
Vi kan eksportere preferansene i et subtre (eller, mindre vanlig, en enkelt node) ved å kalle opp metodene
void exportSubtree(OutputStream ut) void exportNode(OutputStream ut)
Data blir lagret i xml-format. Vi kan importere dem inn i et annet lager ved å gjøre kall på
static
-metoden
void importPreferences(InputStream inn)
Dette er implementert i det etterfølgende programeksemplet. Det gir for programeksemplet følgende xml-fil:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> <preferences EXTERNAL_XML_VERSION="1.0"> <root type="user"> <map/> <node name="progutv"> <map/> <node name="programmer"> <map/> <node name="preferences"> <map> <entry key="venstre" value="626"/> <entry key="topp" value="439"/> <entry key="bredde" value="417"/> <entry key="høyde" value="305"/> <entry key="tittel" value="Test av preferences"/> </map> </node> </node> </node> </root> </preferences>
De verdiene som er vist ovenfor, er avhengig av det brukeren valgte da programmet ble kjørt.
Det blir anbefalt at dersom du bruker opplegget med preferences, så bør brukeren gis mulighet til å eksportere og importere dem, slik at de lett kan flytte over sine settinger fra én datamaskin til en annen. Dette er altså implementert i følgende programeksempel.
Vi skal omarbeide programmet fra eksempel 1 slik at brukerens settinger blir lagret ved
hjelp av opplegget med Preferences
istedenfor opplegget med Property maps
.
Dessuten skal vi implementere funksjonalitet for eksport og import av preferanser. Kommandoer for
å gjøre dette legger vi inn som menyalternativer, sammen med et menyalternativ for programavslutning,
der vi sørger for at brukerens preferanser blir registrert før programmet blir avsluttet.
Slik programmet er organisert, trenger vi for dette noen datafelt på klassenivå:
22 class Preferencesvindu extends JFrame 23 { 24 private JFileChooser filvelger; 25 private JMenuItem eksport, importer, avslutt; 26 private Preferences node; //for brukerpreferanser ...
Opplysninger om skjermstørrelse henter vi inn slik vi har gjort tidligere. For å hente lagrede brukerpreferanser, og eventuelt sette defaultverdier dersom slike ikke finnes, legger vi i konstruktøren inn følgende instruksjoner:
35 //Får tak i posisjon, størrelse og tittel fra preferences: 36 Preferences rot = Preferences.userRoot(); 37 node = rot.node("/progutv/programmer/preferences"); 38 int venstre = node.getInt("venstre", 0); 39 int topp = node.getInt("topp", 0); 40 int bredde = node.getInt("bredde", skjermbredde / 4); 41 int høyde = node.getInt("høyde", skjermhøyde / 4); 42 setSize(bredde, høyde); 43 setLocation(venstre, topp);
Tittel henter vi også inn fra brukeren slik vi har gjort tidligere, i tilfelle en slik ikke er satt tidligere. Til bruk ved eventuell eksport eller import av preferanser, setter vi opp en filvelger til å filtrere ut xml-filer:
59 //Setter opp filvelgeren til å vise xml-filer: 60 filvelger = new JFileChooser(); 61 filvelger.setCurrentDirectory(new File(".")); 62 //vil akseptere alle filer som ender med .xml 63 filvelger.setFileFilter(new javax.swing.filechooser.FileFilter() 64 { 65 public boolean accept(File f) 66 { 67 return f.getName().toLowerCase().endsWith(".xml") 68 || f.isDirectory(); 69 } 70 71 public String getDescription() 72 { 73 return "XML-filer"; 74 } 75 });
For eksport av preferanser er følgende metode implementert. Legg der merke til at
det er Preferences
-objektet node
lagret på klassenivå som blir
brukt til å kalle opp metoden exportSubtree
.
98 public void eksporterPreferences() 99 { 100 int resultat = filvelger.showSaveDialog(this); 101 if (resultat == JFileChooser.APPROVE_OPTION) 102 { 103 try (OutputStream ut = new FileOutputStream( 104 filvelger.getSelectedFile())) 105 { 106 node.exportSubtree(ut); 107 } 108 catch (Exception ex) 109 { 110 ex.printStackTrace(); 111 } 112 } 113 }
Den tilsvarende metoden for import av preferanser,
importPreferences
, er en
static
-metode. Legg merke til at det
ikke blir spesifisert noe objekt som de innleste data skal plasseres i:
115 public void importerPreferences() 116 { 117 int resultat = filvelger.showOpenDialog(this); 118 if (resultat == JFileChooser.APPROVE_OPTION) 119 { 120 try (InputStream inn = new FileInputStream( 121 filvelger.getSelectedFile())) 122 { 123 Preferences.importPreferences(inn); 124 } 125 catch (Exception ex) 126 { 127 ex.printStackTrace(); 128 } 129 } 130 }
Metoden som sørger for å lagre brukerens preferanser før programavslutning ser ut som følger. Legg merke til at den ikke inneholder noen instruksjoner for lagring på fil, eller gjør kall på noen metode for dette.
132 public void avsluttProgram() 133 { 134 node.putInt("venstre", getX()); 135 node.putInt("topp", getY()); 136 node.putInt("bredde", getWidth()); 137 node.putInt("høyde", getHeight()); 138 node.put("tittel", getTitle()); 139 System.exit(0); 140 }
Fullstendig programkode er gjengitt nedenfor og finnes i fila
Preferencestest.java
. For at
programmet skal finne det ikon som brukes må bildefila
hiologo_90x100.gif"
for dette ligge i underkatalogen bilder
under katalogen som inneholder programmets
class
-filer.
1 import java.awt.*; 2 import javax.swing.*; 3 import java.awt.event.*; 4 import java.io.*; 5 import java.util.prefs.*; 6 7 public class Preferencestest 8 { 9 public static void main(String[] args) 10 { 11 EventQueue.invokeLater(new Runnable() 12 { 13 public void run() 14 { 15 Preferencesvindu vindu = new Preferencesvindu(); 16 vindu.setVisible(true); 17 } 18 }); 19 } 20 } 21 22 class Preferencesvindu extends JFrame 23 { 24 private JFileChooser filvelger; 25 private JMenuItem eksport, importer, avslutt; 26 private Preferences node; //for brukerpreferanser 27 28 public Preferencesvindu() 29 { 30 Toolkit verktøykasse = Toolkit.getDefaultToolkit(); 31 Dimension skjermdimensjon = verktøykasse.getScreenSize(); 32 int skjermbredde = skjermdimensjon.width; 33 int skjermhøyde = skjermdimensjon.height; 34 35 //Får tak i posisjon, størrelse og tittel fra preferences: 36 Preferences rot = Preferences.userRoot(); 37 node = rot.node("/progutv/programmer/preferences"); 38 int venstre = node.getInt("venstre", 0); 39 int topp = node.getInt("topp", 0); 40 int bredde = node.getInt("bredde", skjermbredde / 4); 41 int høyde = node.getInt("høyde", skjermhøyde / 4); 42 setSize(bredde, høyde); 43 setLocation(venstre, topp); 44 addWindowListener(new Vinduslytter()); 45 46 //Dersom det ikke er satt noen tittel, spør bruker: 47 String tittel = node.get("tittel", ""); 48 if (tittel.equals("")) 49 { 50 tittel = JOptionPane.showInputDialog( 51 "Skriv ønsket tittel for vinduet:"); 52 } 53 if (tittel == null) 54 { 55 tittel = ""; 56 } 57 setTitle(tittel); 58 59 //Setter opp filvelgeren til å vise xml-filer: 60 filvelger = new JFileChooser(); 61 filvelger.setCurrentDirectory(new File(".")); 62 //vil akseptere alle filer som ender med .xml 63 filvelger.setFileFilter(new javax.swing.filechooser.FileFilter() 64 { 65 public boolean accept(File f) 66 { 67 return f.getName().toLowerCase().endsWith(".xml") 68 || f.isDirectory(); 69 } 70 71 public String getDescription() 72 { 73 return "XML-filer"; 74 } 75 }); 76 77 //Setter opp menyer 78 JMenuBar menylinje = new JMenuBar(); 79 setJMenuBar(menylinje); 80 JMenu filmeny = new JMenu("Fil"); 81 menylinje.add(filmeny); 82 83 eksport = new JMenuItem("Eksporter preferences"); 84 filmeny.add(eksport); 85 86 importer = new JMenuItem("Importer preferences"); 87 filmeny.add(importer); 88 89 avslutt = new JMenuItem("Exit"); 90 filmeny.add(avslutt); 91 92 Menylytter lytter = new Menylytter(); 93 eksport.addActionListener(lytter); 94 importer.addActionListener(lytter); 95 avslutt.addActionListener(lytter); 96 } 97 98 public void eksporterPreferences() 99 { 100 int resultat = filvelger.showSaveDialog(this); 101 if (resultat == JFileChooser.APPROVE_OPTION) 102 { 103 try (OutputStream ut = new FileOutputStream( 104 filvelger.getSelectedFile())) 105 { 106 node.exportSubtree(ut); 107 } 108 catch (Exception ex) 109 { 110 ex.printStackTrace(); 111 } 112 } 113 } 114 115 public void importerPreferences() 116 { 117 int resultat = filvelger.showOpenDialog(this); 118 if (resultat == JFileChooser.APPROVE_OPTION) 119 { 120 try (InputStream inn = new FileInputStream( 121 filvelger.getSelectedFile())) 122 { 123 Preferences.importPreferences(inn); 124 } 125 catch (Exception ex) 126 { 127 ex.printStackTrace(); 128 } 129 } 130 } 131 132 public void avsluttProgram() 133 { 134 node.putInt("venstre", getX()); 135 node.putInt("topp", getY()); 136 node.putInt("bredde", getWidth()); 137 node.putInt("høyde", getHeight()); 138 node.put("tittel", getTitle()); 139 System.exit(0); 140 } 141 142 private class Menylytter implements ActionListener 143 { 144 public void actionPerformed(ActionEvent e) 145 { 146 Object kilde = e.getSource(); 147 if (kilde == eksport) 148 { 149 eksporterPreferences(); 150 } 151 else if (kilde == importer) 152 { 153 importerPreferences(); 154 } 155 else if (kilde == avslutt) 156 { 157 avsluttProgram(); 158 } 159 } 160 } 161 162 private class Vinduslytter extends WindowAdapter 163 { 164 public void windowClosing(WindowEvent e) 165 { 166 avsluttProgram(); 167 } 168 } 169 }
Innholdsoversikt for programutvikling
Copyright © Kjetil Grønning, revidert av Eva Hadler Vihovde 2014