Visuele Gebruikers Omgevingen. 2 ste jaar Toegepaste Informatica Technologie

Maat: px
Weergave met pagina beginnen:

Download "Visuele Gebruikers Omgevingen. 2 ste jaar Toegepaste Informatica Technologie"

Transcriptie

1 Visuele Gebruikers Omgevingen 2 ste jaar Toegepaste Informatica Technologie Lector: Janssens Luc Auteur: Janssens Luc Academiejaar

2 Evaluatie 30 % van de punten staan op jaarwerk (oefeningen). Oefeningen tellen maar mee indien ze op blackboard onder de juiste rubriek staan. (groups-reeks X- groups discussion forum- oefening x). Onder het jaar moet men in groep object georienteerde patronen beschrijven (een voorbeeldje met uitleg neerschrijven). Elk lid van de groep bestudeert 1 patroon (ik verdeel ze, je mag onderling wisselen). Men legt dit patroon in de oefensessie aan elkaar uit. De groep komt in de oefensessie 1 patroon uitleggen ( ik bepaal op het laatste moment welk patroon en wie van de groep het zal uitleggen: het kan dus zijn dat u een patroon van iemand anders komt uitleggen.). U mag uw oefeningen laten groeien tot een eindwerk of u mag oefeningen maken die los van elkaar staan. 70% van de punten staan op een groter programma dat u op het examen demonstreert. (Uw programma moet maar klaar zijn op het examen zelf. ) U test daarom best eerst uw programma eens op voorhand op school.(u mag bij twijfel een eigen pc of laptop meebrengen. (Er zijn soms problemen met verschillende versies van java.). u kunt onderling afspreken om gezamelijk een pc te gebruiken.). U moet tussenversies op blackboard plaatsen. Bij het niet aanwezig zijn van drie tussenversies, wordt uw programma als 'verdacht' bestempeld. (van internet gehaald?) Kwotatie van groter programma: De helft van de punten staan op het kunnen gebruiken van Swing klassen (het maken van een gewoon interactief programma): meerdere vensters gebruiken knoppen, lijsten, tabellen een werkend geheel bekomen De andere helft van de punten van dit groter programma staan op speciale topics: per gebruikt object georienteerd patroon 1 punt erbij (de object georienteerde patronen zijn te vinden op blackboard/course documents. Er zijn daar drie tutorials aanwezig die 23 patronen beschrijven.) andere topics of speciale dingen kunnen punten opleveren: o gebruik van threads o gebruik van xml o maken en gebruiken van een javabean o andere speciale dingen die u zelf vindt. voorbeelden van grotere programma s staan op i:/lujan/i2java/ examenstudenten. (de jars files werken dadelijk in Rega: gewoon er dubbel op klikken. De voorbeelden die tekst tussen haakjes bevatten zijn flexibel geprogrammeerd). Op het examen geeft men af: schermafdrukken klassenschema overzicht van gebruikte patronen diskettes in tweevoud alles in een eenvoudige kartonnen plooimap Visuele Gebruikers Omgevingen: Swing 2 2/395

3 Packages Until now we placed our classes in separated directories. Our classpath was set to '.' to indicate that the class files should be searched in the current directory. But what if we need a class like the Screen class in 100 little projects? Until now we copied the Screen.java file to all the directories where we needed it, and we compiled it again. What if a fundamental change was made to the Screen.java file? We should make the changes in all copies of Screen.java. Therefore it is better to only have one Screen.java and one Screen.class file. Suppose we make a directory 'c:\javaclasses' with Screen.java, Screen.class, Keyboard.class, Mouse.class in it. How can we access those files from other locations? Suppose we have a directory c:\test with a Test.java file and we want to use the Screen class from there. public class Test {public static void main(string args[]) {Screen.gotoxy(30,30); Screen.write("hallo"); When we compile this file, we get the following error messages: c:\test>javac Test.java Test.java:3: cannot resolve symbol symbol : variable Screen location: class Test {Screen.gotoxy(30,30); ^ Test.java:4: cannot resolve symbol symbol : variable Screen location: class Test Screen.write("hallo"); ^ 2 errors When we let the classpath environment variable point to the directory with our Screen class, we can compile our Test.java file. c:\test>set classpath=c:\javaclasses c:\test>javac Test.java Once you have set the classpath environment variable, the java compiler will always be able to locate the useful classes. You have to set this classpath command only once. You can also indicate the classpath for every compilation by providing the classpath option. c:\test>javac -classpath c:\javaclasses Test.java Visuele Gebruikers Omgevingen: Swing 3 3/395

4 Classifying hundreds of useful classes The classpath method is good for a small amount of classes. Suppose you have thousands of classes. There are thousands of java classes and they are classified in packages. They are grouped in packages. When you group files in a package X all the classes of package X must be placed in de directory X. You can't place them in another location or they won't work anymore. By forcing the naming of directories to the naming of the packages, you even can't quick copy a class file to another directory. The class file won't work anymore. Let give an example. We start making a package for our Screen tools and a package for funny stuff. The first thing we have to do is choosing a name for the package. Screen isn't a good name for the package because it's possible you will work afterwards with other packages, which also contain a class named Screen. Therefore Sun recommends to choose a unique name for the package: you take the name of the website of your organization. For me it's Name of websites are unique. All packages made in our organization will start with be.khleuven. Nowhere else in the world there will be a package with the same name. Because we work with object oriented programming, we choose oop and the util next. This leaves the possibility to add other subpackages later. All classes made for fun will be placed in be.khleuven.funnystuff. We provide a test directory, from where we are accessing our packages. The programs in this test directory aren t part of a package. It's for personal use, not to be passed to others. c:...javaclasses...be...khleuven...oop...util...screen.java...funnystuff...messages.java.test..test.java To prevent that you copy the program files of the packages to other directory, we add the package statement as first line to the files Screen.java and Messages.java. After compiling those two files, the corresponding generated class files will be marked as belonging to a package. The java virtual machine will control that a file belonging to a package resides in a directory with the same name structure. This prevents you placing copies all over your disk. First use the Screen.java file that is used in this book. Make it part of a package be.khleuven.oop.util by adding the command package be.khleuven.oop.util as first line. It must be the first real statement in your program. All the rest of the file stays unchanged. Place this file in c:\javaclasses\be\khleuven\oop\util directory. c:\javaclasses\be\khleuven\oop\util\screen.java: // Screen.java file package be.khleuven.oop.util; // rest of class stays unchanged Set the classpath to c:\javaclasses indicating that our packages will reside in that directory. set classpath=c:\javaclasses Compile the Screen.java file from inside the be\khleuven\oop\util directory: c:\javaclasses\be\khleuven\oop\util> javac Screen.java Place the file Messages.java in the directory c:\javaclasses\be\khleuven\funnystuff and add the package statement 'package be.khleuven.funnystuff' to lock this file to the corresponding directory. // Messages.java file Visuele Gebruikers Omgevingen: Swing 4 4/395

5 package be.khleuven.funnystuff; import be.khleuven.oop.util.screen; public class Messages {public void a(){screen.write("aaa"); Because we use in the class Messages the Screen class of the package be.khleuven.oop.util we cannot simply work with Screen as name of the class. We have two possibilities: first we do as above and use an import statement to indicate that the screen class is in the be.khleuven.oop.util package. Because we set our classpath to c:\javaclasses, it will be possible to locate that file as c:\javaclasses\be\khleuven\oop\util\screen.java. Second possibility is not to work with an import statement, but supplying the whole class name: // Messages.java file package be.khleuven.funnystuff; public class Messages {public void a(){be.khleuven.oop.util.screen.write("aaa"); We can compile this class with: c:\javaclasses\be\khleuven\funnystuff > javac Messages.java Because the classpath is still set to c:\javaclasses, the compilation will give no errors. To test our packages, we have a c:\test\test.java file which is using the be.khleuven.oop.util package (which is using the be.khleuven.funnystuff package). // Test.java file import be.khleuven.oop.util.screen; import be.khleuven.funnystuff.messages; public class Test {public static void main(string args[]) {Screen.gotoxy(30,30); Screen.write("hallo:"); Messages m=new Messages(); m.a() ; We use two import statements to locate the Screen and Message class. Instead of using two import statements, it is possible to use the complete name of the classes, including the package they belong to. // Test.java file public class Test {public static void main(string args[]) {be.khleuven.oop.util.screen.gotoxy(30,30); be.khleuven.oop.util.screen.write("hallo:"); be.khleuven.funnystuff.messages m=new be.khleuven.funnystuff.messages(); m.a() ; The import statement diminish the typing effort considerably. If you want to import all the classes of the be.khleuven.oop.util package, you can use a wildcard in the import statement: import be.khleuven.oop.util.*; To compile this file, you have to adjust the classpath: Visuele Gebruikers Omgevingen: Swing 5 5/395

6 set classpath=.;c:\javaclasses Because Test.java isn't belonging to a package, we have to add '.', which is the current directory, to the classpath. Otherwise, the compiler won't find the Test.java file. The java compiler encourage you to work with packages. We compile and run the Test.java with: c:\test\javac Test.java c:\test\java Test Working with jar files Suppose you want to send a complete package over the internet. To facilitate this, you can compress a whole directory structure in one file by using the java jar utility. The java jar utility uses the same compression as the winzip utility. It's very easy to use this tool. You go to the directory where the package resides. In this case this is c:\javaclasses. You call the jar utility and specify some options, the name of the jar file and the directory (or files) that have to be added to the jar file. c:\javaclasses>jar cf BeKhleuven.jar be The jar utility has three options: 'cf' indicates that we will use the jar utility to create and that we will provide the filename. 'BeKhleuven.jar' is the name of the jar file that will be created 'be' is the directory that we be compressed in the jar file. If you want to know the other possibilities of the jar utility, just type jar without options. After using the jar utility to create the jar, the javaclasses directory will contain the be directory structure and the BeKhleuven.jar file. c:\javaclasses>dir. <DIR> 11/09/00 14:37... <DIR> 11/09/00 14:37.. BEKHLE~1 JAR /09/00 10:36 BeKhleuven.jar BE <DIR> 11/09/00 16:26 be The jar file contains the complete directory structure of be. You can easily take a loop inside the jar file by means of the winzip utility. You will notice that the complete directory structure of be is saved inside the jar file. Visuele Gebruikers Omgevingen: Swing 6 6/395

7 Apart from Screen.class, Mouse.class and Keyboard.class, also helper classes are saved. The manifest.mf file is an extra file that generated by the jar utility. Don't bother about it. It's very easy to work with jar files. They are used in the same way as normal package directories are used. To compile the previous Test.java file we can point our classpath to the jar file. c:\test>set classpath=c:\javaclasses\bekhleuven.jar c:\test>javac Test.java During this compilation, the packages inside the jar file are used. If you classpath wasn't set previously, you can also provide the classpath each time you compile: c:\test>javac -classpath c:\javaclasses\bekhleuven.jar Test.java To run the Test class we also need to provide the jar file with packages, otherwise the Screen and Message class won't be located: c:\test>set classpath=c:\javaclasses\bekhleuven.jar;. c:\test>java Test You can also provide the jar directly with the java command: c:\test>java -classpath c:\javaclasses\bekhleuven.jar;. Test Remark the '.' we specify for the classpath to locate the Test.class itself. Visuele Gebruikers Omgevingen: Swing 7 7/395

8 classpath Geval 1 : het package A staat in de huidige directory (.) package A; public class A1 {public void druk(){system.out.println("a"); Het probleem met package is: hoe ze terugvinden. In een willekeurige directory MYDIR staat Test.java: Test.java import A.*; public class Test {static public void main(string []args) {new A1().druk(); MYDIR Test.java Test.class A A1.java A1.class (is dus A.A1) In MYDIR/A staat A1.java : A1.java package A; // moet eerste lijn zijn public class A1 {public void druk(){system.out.println("a"); package A: staat in directory '.' (huidige directory compilatie: MYDIR\A> javac A1.java om package terug te vinden (is geen standaard package, moeten we opgeven waar het staat met behulp van classpath: set CLASSPATH=. (. stelt huidige directory voor) MYDIR> javac Test.java (A.A1 zal teruggevonden worden omdat in. wordt gezocht achter package A) Het package A wordt hier teruggevonden door middel van het set classpath=. bevel. U kan meerdere directories opgeven waarin de compiler moet zoeken achter uw packages. vb: set CLASSPATH=.;c:\javaclasses Meestal wordt dit bevel in autoexec.bat geplaatst. Als u het classpath niet gezet hebt in autoexec.bat, kunt u het ook nog tijdens de compilatie opgegeven: javac classpath.;c:\javaclasses Test.java Visuele Gebruikers Omgevingen: Swing 8 8/395

9 Geval 2: package A staat in een aparte directory (vb d:\javaclasses) java classpath.;d:\javaclasses Test We overschrijven het classpath, dus moeten we alles terug opgeven, ook '.' of zelfs de klasse Test wordt niet gevonden. Ook package A zal gevonden worden. MYDIR Test.java Test.class d:\javaclasses\ A A1.java A1.class (is dus A.A1) package A is te vinden in d:\javaclasses Geval 3: package A wordt in een zip file gezet (jar genoemd) Een jar file wordt gemaakt door middel van de tool 'jar'. MYDIR> jar cf jara.jar A c: create f: dadelijk volgt de naam van de te creëren jar file (hier jara.jar) MYDIR Test.java Test.class jara.jar A A1.java A1.class (is dus A.A1) A : is de naam van de directory (en subdirectories ) die in de jar file moeten gestoken worden. Een jar file is een zip file en kan bekeken worden met behulp van zip: Let vooral op het feit dat IN de zip file heel de directory structuur wordt bijgehouden. Het is alsof de directory A (het package A) IN de zip file zit. Dit geeft de mogelijkheid om deze jar file overal te gebruiken. (De naam van de jar file heeft geen belang). De jar file is een soort verplaatsbare zip file en die kan over het internet verzonden worden Let op de meta-inf/manifest.mf file. Uitleg volgt later. Visuele Gebruikers Omgevingen: Swing 9 9/395

10 MYDIR> java classpath.;d:\jara.jar Test Het A package wordt teruggevonden dmv de jar file. '.' is nodig om Test.class te vinden. MYDIR Test.java Test.class d:\ jara.jar bevat : A A1.java A1.class (is dus A.A1) Geval 4 : hoofdklasse met main + alle bijhorende classen, packages in één jar file zetten Stel dat je een 'main' klasse (een publieke klasse met een static void main) over het internet moet zenden, samen met de nodige packages. U kunt dit alles in één jar file steken (jar=java archiving file). Wel moet u een manifest file toevoegen die aangeeft welke klasse de main bevat. De naam van de manifest file (hier mmaanniiffeesstt.txt) heeft geen belang. Main-Class: Test mmaanniiffeesstt.txt: er wordt aangegeven welke klasse de main klasse is. Er moet een lege lijn achter staan. MYDIR> jar cfm jartest.jar mmaanniiffeesstt.txt. c: create, f:naam jar file volgt(jartest.jar), m: manifest file volgt '.' : alles van de huidige directory wordt in de jar geplaatst MYDIR Test.java Test.class mmaanniiffeesstt.txt A A1.java A1.class (is dus A.A1) resultaat: Visuele Gebruikers Omgevingen: Swing 10 10/395

11 Merk op dat de jar file alles bevat van de huidige directory en de subdirectory. Tevens wordt de directory structuur bijgehouden. Hierdoor wordt package A teruggevonden. We kunnen nu heel deze jar file over internet verzenden. Door dubbel te klikken op de jar file vanuit windows verkenner, wordt er automatisch gezocht naar een manifest.mf file in meta-inf. Als er een main file werd aangeduid, dan wordt deze automatisch uitgevoerd. U kunt ook vanuit de command line deze jar file uitvoeren: java jar jartest.jar Deze jar file bevat klasse met main, plus package A. Inhoud van meta-inf\manifest.mf: Geval 5: jdk1.3\jre\lib\ext Als in de autoexec.bat het bevel set java_home=c:\jdk1.3 werd geplaatst, dan kunt u steeds jars plaatsen in c:\jdk1.3\jre\lib\ext. Deze jars worden dan automatisch gevonden. Tevens hebben de classes in deze jars alle toegangsrechten. Naamkeuze Sun raadt aan om wereldwijd unieke namen voor uw klassen te gebruiken. Hiertoe wordt aangeraden om te beginnen met de URL van uw organisatie en deze om te keren. Uw packages moeten aldus beginnen met be.khleuven. U wordt gevraagd om uw packages te laten beginnen met be.khleuven.rega.ti2.reeksx.naam waarbij reeksx moet vervangen worden door uw reeks en naam door uw naam. U maakt hiertoe best een directory c:\javaclasses en plaatst het bevel set classpath=.;c:\javaclasses in autoexec.bat. Vervolgens maakt u de directory c:\javaclasses\be\khleuven\rega\ti2\reeksx\naam. Als eerste statement in uw classen komt dan package be.khleuven.rega.ti2.reeksx.naam;. Als u deze klassen ergens anders wilt gebruiken moet u een import statement gebruiken: import be.khleuven.rega.ti2.reeksx.naam.*; Visuele Gebruikers Omgevingen: Swing 11 11/395

12 swing KLEINE VOORBEELDEN : opbouw componenten import javax.swing.*; import java.awt.*; public class Swing extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JLabel l=new JLabel("eerstelabel"); c.add(l); JTextField t=new JTextField(10); c.add(t); Een applet wordt steeds opgeroepen vanuit een webbrowser. JApplet (Swing klasse) wordt nog niet door web browsers ondersteund (Applet wel). Daarom moet je volgende file openen met appletviewer (jdk/bin). De init methode van een applet wordt opgeroepen bij het laden van de applet (: slechts de eerste maal). <HTML> <HEAD> <!-- Generated by Kawa IDE --> <TITLE>Applet title</title> </HEAD> <BODY> <H1>First Heading</H1> <HR> <APPLET CODE=Swing.class WIDTH=300 HEIGHT=300></APPLET> <HR> </BODY> </HTML> getcontentpane geeft een Container object terug. Dit object beheert een Vector. Je kunt toevoegen, weglaten uit deze vector. Het verschil met de klasse Vector is dat Container ook functies heeft om alle objecten te tekenen. Het is een soort visuele Vector. FlowLayout zorgt ervoor dat de elementen van de Container van links naar rechts op het scherm worden geplaatst. Als het scherm groter of kleiner wordt, worden de elementen van de container herschikt. (Automatisch). Een Container doet dus duidelijk meer dan een Vector. Hetzelfde maar nu met JFrame. import javax.swing.*; import java.awt.*; public class Swing01 extends JFrame {public Swing01() {super("swing01"); Visuele Gebruikers Omgevingen: Swing 12 12/395

13 Container c=getcontentpane(); c.setlayout(new FlowLayout()); JLabel l=new JLabel("eerstelabel"); c.add(l); JTextField t=new JTextField(10); c.add(t); public static void main(string args[]) {JFrame fr=new Swing01(); fr.setlocation(100,100); fr.setsize(250,80); fr.setvisible(true); fr.setdefaultcloseoperation(jframe.exit_on_close); Een JFrame is een gewoon venster dat los van een browser kan gemaakt worden. Meestal wordt het vanuit een main aangemaakt of vanuit een andere frame. Een JFrame wordt slechts zichtbaar wanneer setvisible(true) wordt uitgevoerd. JFrame.EXIT_ON_CLOSE (een final static int in JFrame klasse) geeft aan dat het venster moet gesloten worden indien op de sluittoets wordt gedrukt. Een JFrame heeft ook een content pane waarin je componenten (hier een label) kunt plaatsen. We positioneren het frame op kolom 100, rij 100 met een breedte van 250 pixels en een hoogte van 80 pixels. We kunnen bevelen van de main naar de klasse die overerft van JFrame verschuiven: import javax.swing.*; import java.awt.*; public class Swing02 extends JFrame {public Swing02() {super("swing02"); Container c=getcontentpane(); c.setlayout(new FlowLayout()); JLabel l=new JLabel("eerstelabel"); c.add(l); JTextField t=new JTextField(10); c.add(t); this.setlocation(100,100); this.setsize(250,80); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {JFrame fr=new Swing02(); Als we de setlocation, setsize, bevelen verschuiven naar de klasse die overerft van JFrame, dan moeten die methodes gewoon toegepast worden op this. Als je geen layoutmanager gebruikt, moet u null opgeven en alle maten zelf ingeven: Visuele Gebruikers Omgevingen: Swing 13 13/395

14 import javax.swing.*; import java.awt.*; public class Swing03 extends JFrame {public Swing03() {super("swing03"); Container c=getcontentpane(); c.setlayout(null); JLabel l=new JLabel("eerstelabel"); c.add(l); JTextField t=new JTextField(10); c.add(t); l.setlocation(10,10);l.setsize(80,30); t.setlocation(10,40);t.setsize(80,30); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {JFrame fr=new Swing03(); Layout managers (zoals FlowLayout ) kunnen handig zijn om snel resultaat te hebben. Wil je echter zelf bepalen waar elke komponent komt, dan kan je besluiten om geen layoutmanager te gebruiken. Dit doe je door null op te geven als LayoutManager. Hierdoor overschrijf je de standaard layout manager die bij de component hoort.(bij JFrame is de standaard layout een BorderLayout). Containers kunnen een layout manager toegekend hebben, vb BorderLayout import javax.swing.*; import java.awt.*; public class Swing04 extends JFrame {public Swing04() {super("swing04"); Container c=getcontentpane(); c.setlayout(new BorderLayout()); JButton l=new JButton("een"); c.add(l,borderlayout.north); c.add(new JButton("twee"),BorderLayout.SOUTH); c.add(new JButton("drie"),BorderLayout.EAST); c.add(new JButton("vier"),BorderLayout.WEST); c.add(new JTextArea(),BorderLayout.CENTER); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {JFrame fr=new Swing04(); Deze layout manager zal de componenten zelf verplaatsen. Het venster is hierbij ingedeeld in NORTH, SOUTH, WEST, EAST en CENTER. Als het venster groter of Visuele Gebruikers Omgevingen: Swing 14 14/395

15 kleiner wordt, zal de Container de componenten automatisch proportioneel aanpassen. U merk dus: een Container doet meer dan een gewone Vector. flowlayout import javax.swing.*; import java.awt.*; public class Swing05 extends JFrame {public Swing05() {super("swing05"); Container c=getcontentpane(); c.setlayout(new FlowLayout()); JButton l=new JButton("een"); c.add(l); c.add(new JButton("twee")); c.add(new JButton("drie")); c.add(new JButton("vier")); c.add(new JTextArea(10,5)); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {JFrame fr=new Swing05(); Visuele Gebruikers Omgevingen: Swing 15 15/395

16 Een JMenuBar De JLayeredPane bestaat nu uit een JMenuBar en een contentpane. De contentpane wordt kleiner omdat er een menubar bijkomt. Beiden zitten in de JLayeredPane.Frame_Content_Layer van de JLayeredPane. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Swing06 { public static void main(string s[]) { JFrame frame = new JFrame("TopLevelDemo"); frame.setdefaultcloseoperation(jframe.exit_on_close); JLabel yellowlabel = new JLabel(""); yellowlabel.setopaque(true); yellowlabel.setbackground(color.yellow); yellowlabel.setpreferredsize(new Dimension(200, 180)); JMenuBar cyanmenubar = new JMenuBar(); cyanmenubar.setopaque(true);// ondoorschijnend cyanmenubar.setbackground(color.cyan); cyanmenubar.setpreferredsize(new Dimension(200, 20)); frame.setjmenubar(cyanmenubar); frame.getcontentpane().add(yellowlabel, BorderLayout.CENTER); frame.pack(); frame.setvisible(true); Met setpreferredsize kan je toch richtlijnen geven aan de Container. (setsize wordt gewoon door de Container genegeerd van zodra er een layoutmanager is.) Van zodra je een JMenuBar toevoegd, wordt de contentpane kleiner gemaakt. Dit komt omdat beiden in een laag van de JLayeredPane zitten van het venster. Dus de contentpane is een container, waar u normaal componenten toevoegd, maar zelf zit het ook in een container. U moet hier niet teveel van aantrekken. Dit beheer gebeurt voor u automatisch. Maar je kan er wel mee spelen. Visuele Gebruikers Omgevingen: Swing 16 16/395

17 JTextArea, actie bij JButton Wordt breder en langer automatisch bij toevoegen letters of lijnen import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing2 extends JFrame {public Swing2() {super("swing2"); this.setlocation(100,100); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); Container c=getcontentpane(); c.setlayout(new FlowLayout()); JButton l= new JButton("eerste_button"); JTextArea t=new JTextArea(10,3); c.add(t); c.add(l); l.setaction(new Aktie()); this.pack(); public static void main(string args[]) {new Swing2(); class Aktie extends AbstractAction {static int count=0; public Aktie(){super(" na vier maal wordt venster gesloten"); public void actionperformed(actionevent e) { count++; JOptionPane.showMessageDialog(null,"gedrukt"+count); JFrame fr=new JFrame("gedrukt"+count); fr.setvisible(true); fr.setbounds(10,10,180,40); Visuele Gebruikers Omgevingen: Swing 17 17/395

18 if(count==4) System.exit(0); We hebben hier een JTextArea van 10 rijen en 3 kolommen. Als we meer letters intypen, wordt de textarea breder (De container zal alles opschuiven). Interessant is de Aktie klasse. We pluggen een object van deze klasse in in de JButton met behulp van setaction. Als we dan op de button klikken zal automatisch de routine actionperformed worden opgeroepen. (Deze moet als parameter een actionevent object hebben. In dit actionevent object zitten gegevens over welke button de routine heeft opgeroepen. Als je op de button klikt, wordt er met behulp van JOptionPane klasse een boodschap getoond. Het programma gaat slechts verder indien u op ok drukt. Er wordt ook een JFrame gemaakt (dit blijft gewoon staan). Als u de vierde maal op de button klikt, zal het programma stoppen. Dank zij Action objecten, kunt u het gedrag van een button wijzigen, zelfs wanneer het programma aan het werken is. JSlider public class Swing3 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JSlider l= new JSlider(); c.add(l); String [] tabel={"een","twee","drie","vier"; JList t=new JList(tabel); c.add(t); JRadioButton, JCheckBox Visuele Gebruikers Omgevingen: Swing 18 18/395

19 public class Swing4 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JCheckBox cb= new JCheckBox("stip mij aan"); c.add(cb); JRadioButton cb2=new JRadioButton("kies mij"); c.add(cb2); JButton, JMenuItem, JRadioButton, JCheckBox: Action Bovenstaand voorbeeld bevat een menubar met twee menuitems. We hebben ook een button. Telkens we de button klikken, verhoogt de count met 1. Als de count gelijk wordt aan max (initieel 4), dan sluit het venster. We kunnen de bovengrens veranderen. (Als we 10 selecteren, wordt 4 ont-kozen en omgekeerd). We kunnen de count terug op nul zetten. Speciaal aan dit programma zijn de veranderlijken die globaal zijn aan de hoofdklasse. Hierdoor kunnen deze componenten benaderd worden vanuit de Aktie klasse. De Aktie klasse hebben we nu in de hoofdklasse gezet, juist opdat we aan de globale componenten zouden kunnnen. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing2B extends JFrame { JMenuItem reset = new JMenuItem(); JRadioButton setmax10= new JRadioButton("bla bla"); JRadioButton setmax4= new JRadioButton("bla bla"); JTextField text = new JTextField(4); JTextField maxtext = new JTextField(4); static int count=0; int max=4; public Swing2B() {super("swing2b"); this.setlocation(100,100); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); Container c=getcontentpane(); c.setlayout(new FlowLayout()); JButton but= new JButton("bla bla"); c.add(but);c.add(setmax10);c.add(setmax4); c.add(new JLabel("count:"));c.add(text); Visuele Gebruikers Omgevingen: Swing 19 19/395

20 c.add(new JLabel("max:"));c.add(maxtext); JMenuBar menubar = new JMenuBar(); JMenuItem menuitem1 = new JMenuItem(); menubar.add(menuitem1); menubar.add(reset); setjmenubar( menubar); but.setaction(new Aktie("verhoog count")); menuitem1.setaction(new Aktie("quit")); reset.setaction(new Aktie("reset_count")); setmax10.setaction(new Aktie("bovengrens 10")); setmax4.setaction(new Aktie("bovengrens 4")); this.pack(); class Aktie extends AbstractAction // INNER KLASSE!!!!! { public Aktie(String s){super(s); public void actionperformed(actionevent e) { if(e.getactioncommand().equals("verhoog count")) count++; if(e.getactioncommand().equals("quit")) System.exit(0); if(e.getsource()==reset ) count=0; if(e.getactioncommand().equals("bovengrens 10")) { max=10; setmax4.setselected(false); setmax10.setselected(true); if(e.getsource()==setmax4) { max=4; setmax10.setselected(false); setmax4.setselected(true); if(count==max) System.exit(0); text.settext(""+count); maxtext.settext(""+max); public static void main(string args[]) {new Swing2B(); In de klasse Aktie kunnen we op twee verschillende wijzen nagaan welke component de actie veroorzaakt heeft: vooreerst kunnen we met getactioncommand de text van de component opvragen. Ten slotte kunnen we met getsource een referentie naar het object verkrijgen en dit vergelijken met onze componenten. GridLayout For example, the following is an applet that lays out six buttons into three rows and two columns: Visuele Gebruikers Omgevingen: Swing 20 20/395

21 import java.awt.*; import javax.swing.*; public class Swing8E extends JFrame { public Swing8E() { Container c=getcontentpane(); c.setlayout(new GridLayout(3,2)); c.add(new Button("1")); c.add(new Button("2")); c.add(new Button("3")); c.add(new Button("4")); c.add(new Button("5")); c.add(new Button("6")); this.setlocation(100,100); this.setsize(200,200); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) { new Swing8E(); Een gridlayout is een matrix waarin de elementen worden geplaatst. In tegenstelling tot bijvoorbeeld een flowlayout, zal deze matrix altijd behouden blijven. Alleen de spaties tussen de elementen wordt vergroot of verkleind. In het voorbeeld hieronder hebben we een slechts één kolom. Het aantal rijen is op nul gezet, zodat dit niet beperkt is. Als we het scherm vergroten of verkleinen blijven we steeds één rij hebben. import javax.swing.*; import java.awt.*; public class Swing8D extends JFrame { Visuele Gebruikers Omgevingen: Swing 21 21/395

22 JButton gobutton; JLabel namelabel; JTextField nametf; public Swing8D() { gobutton = new JButton("Go"); gobutton.setfont(new Font("Times", Font.BOLD, 14)); namelabel = new JLabel("Name"); nametf = new JTextField(20); // setting the number of rows to 0 means the number of rows // will be increased to match the number of components added // to the layout Container c=getcontentpane(); c.setlayout(new GridLayout(0, 1)); JPanel p1 = new JPanel(); p1.add(namelabel); c.add(p1); JPanel p2 = new JPanel(); p2.add(nametf); c.add(p2); JPanel p3 = new JPanel(); p3.add(gobutton); c.add(p3); this.setlocation(100,100); this.setsize(200,200); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) { new Swing8D(); Als we hetzelfde programma nemen, maar nu zonder de componenten eerst in panels te steken, dan zal de layout manager de componenten vergroten of verkleinen: import javax.swing.*; import java.awt.*; public class Swing8F extends JFrame { JButton gobutton; JLabel namelabel; JTextField nametf; public Swing8F() { gobutton = new JButton("Go"); gobutton.setfont(new Font("Times", Font.BOLD, 14)); Visuele Gebruikers Omgevingen: Swing 22 22/395

23 namelabel = new JLabel("Name"); nametf = new JTextField(20); // setting the number of rows to 0 means the number of rows // will be increased to match the number of components added // to the layout Container c=getcontentpane(); c.setlayout(new GridLayout(0, 1)); c.add(namelabel); c.add(nametf); c.add(gobutton); this.setlocation(100,100); this.setsize(200,200); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) { new Swing8F(); Visuele Gebruikers Omgevingen: Swing 23 23/395

24 Layout GridBagLayout Een gridbaglayout is als een gridlayout. Een component kan nu echter meer dan één cel van de matrix in beslag nemen. In onderstaand voorbeeld hebben we een label, een textveldje en een button. De button neemt twee cellen in beslag, de anderen maar één cel. Tevens blijkt de button de horizontale breedte steeds op te vullen. De label zit in cel (0,0). Het tekstveldje in cel (1,0). De button in cel (0,1) (steeds kolom, rij). De label neemt 1 rij 1 kolom in beslag, zo ook het tekstveldje. De button neemt 2 kolommen en 1 rij in beslag. De cellen raken elkaar niet omdat de insets op (4,4,4,4) werd gezet: dit is links, rechts, boven en onder is er steeds een band van 4 pixels rond de componenten. Als we het vensters horizontaal verbreden, merken we dat de button meer vergroot. Dit komt omdat we GridBagConstraints.HORIZONTAL hebben opgegeven: de button zal zijn cel steeds horizontaal opvullen. (niet verticaal: als we het venster hoger maken, zal de botton verticaal niet vergroten. ). U hebt de volgende opties: HORIZONTAL, VERTICAL, BOTH, NONE. Bij BOTH zal de component zowel horizontaal als verticaal de vergrotingen volgen. Bij NONE blijft de component op zijn vaste grootte staan. We merken ook op dat de label horizontaal niet mee vergroot, en dat hij in het oosten van zijn cel verankerd blijft. Als anker hebben we de volgende mogelijkheden: GridBagConstraints.CENTER, NORTH, NORTHWEST, NORTHEAST, SOUTH, SOUTHWEST, SOUTHEAST, EAST, WEST. Eigenlijk heeft deze parameter alleen maar zin indien de component zijn cel niet volledig opvult. Als we het venster verticaal vergroten, dan merken dat de cel waarin de button staat proportioneel minder (de helft) toeneemt en dat de botton helemaal niet deze cel verticaal gaat opvullen. De label gaat alleen verticaal opvullen, niet horizontaal. Het tekstveldje gaat BOTH opvullen: ze volgt de vergroting zowel horizontaal als verticaal. De label blijft in het oosten van zijn cel staan. Visuele Gebruikers Omgevingen: Swing 24 24/395

25 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class Swing8G extends JFrame { JButton gobutton; JLabel namelabel; JTextField nametf; GridBagConstraints gbc; public Swing8G() { Insets i = new Insets(4, 4, 4, 4); gobutton = new JButton("Go"); gobutton.setfont(new Font("Times", Font.BOLD, 14)); namelabel = new JLabel("Name"); namelabel.setborder(new EtchedBorder(Color.green,Color.red)); nametf = new JTextField(20); // A GridBagLayout object is created and it is set to be // the layout manager for the Frame. An instance of // GridBagLayout is needed later to call the setconstraints() // method. Container c=getcontentpane(); c.setbackground(color.white); GridBagLayout gbl = new GridBagLayout(); c.setlayout(gbl); // The constraints are set for the Label object. All of the // constraint values are passed to the GridBagConstraints // constructor. The setconstraints() method is used to associate. // the GridBagConstraints object with the Label. gbc = new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, i, 0, 0); gbl.setconstraints(namelabel, gbc); // The constraints are similarly set for the TextField Kolom en rij van de cel Grootte van cel: # kolommen,#rijen Proportionele horizontale vergroting: 1.0 Proportionele verticale vergroting: 1.0 Ankerpunt (EAST) Opvulpolitiek: verticaal, horizontaal,both, none Insets (4,4,4,4) 0:minder belangrijk 0:minder belangrijk gbc = new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, i, 0, 0); gbl.setconstraints(nametf, gbc); // The constraints are similarly set for the Button Visuele Gebruikers Omgevingen: Swing 25 25/395

26 gbc = new GridBagConstraints(0, 1, 2, 1, 0.0, 0.5, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, i, 0, 0); gbl.setconstraints(gobutton, gbc); c.add(namelabel); c.add(nametf); c.add(gobutton); this.setlocation(100,100); this.setsize(200,200); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) { Swing8G tgbl = new Swing8G(); Boxlayout Een GridBagLayout kan nogal ingewikkeld zijn om vormen. Daarom bestaat er ook een BoxLayout die bedoeld is om horizontaal of verticaal een rij componenten te rangschikken. U kunt gebruik maken van de klasse Box die automatisch een BoxLayout layout manager heeft. De klasse Box is een Container zoals JPanel maar met BoxLayout in plaats van FlowLayout zoals JPanel. Een Box object wordt gemaakt met Box.createHorizontalBox() of Box.createVerticalBox(). Naast componenten kan je ook andere hulpmiddelen toevoegen: Box.createHorizontalStrut(50) : een horizontale schraag van 50 breed. (heeft geen hoogte) Box.createVerticalStrut(50) : een verticale schraag van 50 hoog (heeft geen breedte) Box.createRigidArea(new Dimension(50,50)); : een onzichtbare rechthoek van 50x50 import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; class SwingBox extends JFrame { public SwingBox() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); Visuele Gebruikers Omgevingen: Swing 26 26/395

27 Box b1,b2,b3,b4; JPanel p1,p2,p3,p4; p1=new JPanel();p2=new JPanel();p3=new JPanel();p4=new JPanel(); b1=box.createhorizontalbox(); b1.add(new JButton("but")); b1.add(new JButton("but")); p1.setborder(new LineBorder(Color.blue)); b2=box.createhorizontalbox(); b2.add(box.createhorizontalstrut(30)); b2.add(new JButton("but")); b2.add(box.createhorizontalstrut(30)); b2.add(new JButton("but")); b2.add(box.createhorizontalstrut(30)); p2.setborder(new LineBorder(Color.blue)); b3=box.createhorizontalbox(); b3.add(new JButton("but")); b3.add(box.createverticalstrut(50)); b3.add(new JButton("but")); p3.setborder(new LineBorder(Color.blue)); b4=box.createhorizontalbox(); b4.add(box.createrigidarea(new Dimension(50,50))); b4.add(new JButton("but")); b4.add(new JButton("but")); p4.setborder(new LineBorder(Color.blue)); p1.add(b1);p2.add(b2);p3.add(b3);p4.add(b4); c.add(p1);c.add(p2);c.add(p3);c.add(p4); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); pack(); setvisible(true); public static void main(string args[]) {new SwingBox(); BoxLayout met Glue In dit voorbeeld gaan we de layout van de 4 panels veranderen in BoxLayout. We gaan dus niet werken met de Container klasse Box die automatisch een BoxLayout heeft. U kunt BoxLayout met elke Container gebruiken. Het gebeurt wel op een speciale wijze: p1.setlayout(new BoxLayout(p1,BoxLayout.X_AXIS)); De panel p1 krijgt als referebtie de nieuwe BoxLayout mee. De BoxLayout krijgt als eerste argument dezelfde p1 door. Dit komt omdat beide klassen met elkaar moeten samenwerken en dus een referentie van de andere klasse moeten hebben. Langs deze referentie kunnen ze methodes van de andere klasse oproepen. Visuele Gebruikers Omgevingen: Swing 27 27/395

28 Glue is een soort veer. Als je die ergens zet, dan zal bij extra plaats deze heel de vrije plaats proberen op te vullen. (Als een veer die zich ontspant). In het tweede panel staat de veer vooraan en duwt de componenten naar rechts. In het derde panel staat de veer in het midden en duwt de componenten uit elkaar. In het vierde panel hebben we twee veren gebruikt. import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; class SwingBox2 extends JFrame { public SwingBox2() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); Box b1,b2,b3,b4; JPanel p1,p2,p3,p4; p1=new JPanel();p2=new JPanel();p3=new JPanel();p4=new JPanel(); p1.setpreferredsize(new Dimension(150,70)); p2.setpreferredsize(new Dimension(150,70)); p3.setpreferredsize(new Dimension(150,70)); p4.setpreferredsize(new Dimension(150,70)); p1.setlayout(new BoxLayout(p1,BoxLayout.X_AXIS)); p2.setlayout(new BoxLayout(p2,BoxLayout.X_AXIS)); p3.setlayout(new BoxLayout(p3,BoxLayout.X_AXIS)); p4.setlayout(new BoxLayout(p4,BoxLayout.X_AXIS)); p1.add(new JButton("but")); p1.add(new JButton("but")); p1.setborder(new LineBorder(Color.blue)); p2.add(box.createhorizontalglue()); p2.add(new JButton("but")); p2.add(new JButton("but")); p2.setborder(new LineBorder(Color.blue)); p3.add(new JButton("but")); p3.add(box.createhorizontalglue()); p3.add(new JButton("but")); p3.setborder(new LineBorder(Color.blue)); p4.add(new JButton("but")); p4.add(box.createhorizontalglue()); p4.add(new JButton("but")); p4.add(box.createhorizontalglue()); p4.setborder(new LineBorder(Color.blue)); c.add(p1);c.add(p2);c.add(p3);c.add(p4); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); Visuele Gebruikers Omgevingen: Swing 28 28/395

29 pack(); setvisible(true); public static void main(string args[]) {new SwingBox2(); Een drie dimensionale Container: JLayeredPane import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Swing07 { public static void main(string s[]) { JFrame frame = new JFrame("TopLevelDemo"); Container container = frame.getcontentpane(); container.setbackground(color.white); container.setlayout(new FlowLayout()); frame.setdefaultcloseoperation(jframe.exit_on_close); JLabel yellowlabel = new JLabel("text "); yellowlabel.setpreferredsize(new Dimension(150, 70)); yellowlabel.setopaque(true); yellowlabel.setbackground(color.yellow); JMenuBar cyanmenubar = new JMenuBar(); cyanmenubar.setbackground(color.cyan); cyanmenubar.setpreferredsize(new Dimension(200, 20)); frame.setjmenubar(cyanmenubar); container.add(yellowlabel); container.add(new JLabel("text ")); container.add(new JLabel("text ")); container.add(new JLabel("text ")); JLayeredPane lay = frame.getlayeredpane(); JButton but2 = new JButton("hello,how are you"); JButton but3 = new JButton("??????"); JButton but4 = new JButton("******"); but2.setbounds(30,100,140,30); but3.setbounds(120,115,70,30); but4.setbounds(5,5,70,30); lay.add(but2,jlayeredpane.default_layer,0); lay.add(but3,jlayeredpane.default_layer,0); lay.add(but4,jlayeredpane.default_layer,0); frame.pack(); frame.setvisible(true); We werken nu in meerdere lagen. De Content pane en de JMenuBar zitten beiden in één laag van een JLayeredPane. Maar je kunt zoveel lagen ervoor voegen als je wilt. Elke laag heeft gewoon een nummer. Hoe hoger het nummer, hoe meer de laag naar voor komt (en over de onderliggende lagen zit). In het voorbeeld hierboven voegen we enkele buttons toe in een hogere laag. Deze laag komt gewoon voor de contentpane. De contentpane hebben we een FlowLayout Visuele Gebruikers Omgevingen: Swing 29 29/395

30 gegeven. De buttons die we extra in de DEFAULT_LAYER toevoegen, hebben we op absolute posities gezet. Als we het scherm vergroten en verkleinen, dan merken we dat de achtergrond (FRAME_CONTENT_LAYER) zich herschikt en dat de voorgrond (DEFAULT_LAYER) hetzelfde blijft. U merkt ook duidelijk dat de DEFAULT_LAYER zich gewoon over de contentpane en menubar stelt. De Content pane waar we tot nu toe gewoon componenten toevoegden is dus gewoon de onderste laag (aangeduid met JLayeredPane.FRAME_CONTENT_LAYER). Alle lagen hebben een nummer. Voor de standaard JLayeredPane gelden de volgende nummers : FRAME_CONTENT_LAYER ² DEFAULT_LAYER New Integer( ) new Integer(0) This layer is used to position the frame's content pane and menu bar. Most programs won't use this. The root pane adds the menu bar and content pane to its layered pane at this depth. Most components go in this layer. If you don't specify a component's depth, the layered pane puts it at this depth. PALETTE_LAYER MODAL_LAYER POPUP_LAYER DRAG_LAYER new Integer(100) new Integer(200) new Integer(300) new Integer(400) This layer is useful for floating toolbars and palettes. Modal internal-frame dialogs would belong in this layer. Popups go in this layer because they need to appear above just about everything. Move a component to this layer when dragging. Return the component to its regular layer when dropped. U mag ook zelf JLayeredPane objecten maken en een eigen nummering volgen. Binnen elke laag van een layered pane, mogen componenten over elkaar gepositioneerd worden. Je moet voor elke component in een layered pane zijn absolute positie aangeven. (Er is bij default geen layout manager aanwezig, maar je mag er wel één toekennen met setlayout). In bovenstaande tekening, merk je dat hello, how are you button (but2) in zijn layer op de voorgrond staat. Hiervoor hebben het programma lichtjes aangepast: lay.movetofront(but2); In een bepaalde layer kunnen we met movetofront en movetoback een component naar voor of naar achter verplaatsen. Met setposition kunnen we zelfs heel nauwkeurig binnen in één layer aangeven waar het komt ( 0 is top, -1 is button, : van voor naar achter.) Visuele Gebruikers Omgevingen: Swing 30 30/395

31 De volgende klassen hebben allemaal een JLayeredPane (en dus een Content pane, plaats voor een JMenuBar) : JDialog, JWindow, JInternalFrame, JFrame, JApplet. Internal Frames komen steeds in een JLayeredPane (JDesktopPane) U kunt deze subvensters verslepen, en verkleinen tot een icoontje import javax.swing.*; import java.awt.*; public class Swing5 extends JFrame {public Swing5() {super("swing5"); JDesktopPane desktop = new JDesktopPane(); setcontentpane(desktop); Container c=getcontentpane();//=desktop JInternalFrame cb= new JInternalFrame("stip mij aan",true,true,true,true); JInternalFrame cb2= new JInternalFrame("stip mij aan2",true,true,true,true); desktop.add(cb);c.add(cb2);// c == desktop cb2.setsize(100,130);cb.setlocation(80,80); cb.setsize(120,140);cb.setlocation(10,10); cb.setvisible(true); cb2.setvisible(true); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {new Swing5(); De internal frame zijn hier versleepbaar, verkleinbaar, vergrootbaar (maximize), en sluitbaar. Deze opties worden met de constructor meegegeven. Internal frames worden bijna steeds toegevoegd aan een JDesktopPane. Deze container zorgt ervoor dat de internal frame over elkaar kunnen gesleept worden. Je kan alle methodes van JLayeredPane toepassen op JDesktopPane (erft ervan over). Het verschil met JLayeredPane zit in het beheer van verslepen en verkleinen tot icoontjes. Als je met JInternalFrames werkt, moet je de normale contentpane vervangen door een JDesktopPane. Internal frames moeten steeds gepositioneerd worden met setsize en setlocation. Je kan ook setbounds gebruiken hiervoor. Visuele Gebruikers Omgevingen: Swing 31 31/395

32 We overdrijven maar eens In een internal frame ga je normaal gezien gewone componenten toevoegen, (analoog als bij een JFrame). In dit voorbeeld hebben we twee internal frames die in een frame zitten. Hiervoor moet de frame (Swing6) een desktop pane bevatten. De tweede internal frame bevat terug een destop frame met daarin een internal frame (Composite). Deze laatste internal frame gedraagt zich normaal en bevat gewone componenten (label, button, ). import javax.swing.*; import java.awt.*; public class Swing6 extends JFrame {public Swing6() {super("swing6"); JDesktopPane desktop=new JDesktopPane(); setcontentpane(desktop); desktop.setbackground(color.white); Container c=getcontentpane();//== desktop JInternalFrame cb= new JInternalFrame("stip mij aan",true); JInternalFrame cb2= new JInternalFrame("stip mij aan2",true); cb.setsize(200,150);cb.setlocation(10,10); cb2.setsize(200,130);cb2.setlocation(50,50); c.add(cb);c.add(cb2); cb.setvisible(true);cb2.setvisible(true); JDesktopPane desktop2=new JDesktopPane(); cb2.setcontentpane(desktop2); JInternalFrame fr2= new JInternalFrame("Composite",true); fr2.setsize(140,150);fr2.setlocation(10,10); fr2.setvisible(true); Container cc=fr2.getcontentpane(); cc.setbackground(color.white); cc.setlayout(new FlowLayout()); cc.add( new JLabel("hoe kan dit")); cc.add( new JButton("druk")); cc.add( new JRadioButton("klik")); cc.add( new JCheckBox("check")); cc.add( new JSlider() ); desktop2.add(fr2); this.setlocation(100,100); this.setsize(350,320); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {new Swing6(); Visuele Gebruikers Omgevingen: Swing 32 32/395

33 Menu samen met JInternalFrame Het komt dikwijls voor dat je met internalframes werkt en dat je met behulp van een menu acties wilt ondernemen die voor het internal frame dat de focus heeft. U weet dat u met een JDesktopPane moet werken als u internal frames wilt gebruiken. De methode getselectedframe hebben we hiervoor nodig: het bevat een referentie naar het frame dat de focus heeft. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.beans.*; public class Swing5 extends JFrame {JMenuBar bar=new JMenuBar(); JMenu men=new JMenu("opties"); JMenuItem item=new JMenuItem("sluit actief subvenster"); JMenuItem item2=new JMenuItem("plaats venster links boven" ); public Swing5() {Container c=getcontentpane(); final JDesktopPane thedesktop = new JDesktopPane(); //thedesktop.setlayout(new FlowLayout()); JInternalFrame cb= new JInternalFrame("stip mij aan",true,true,true,true); thedesktop.add(cb);cb.setvisible(true); cb.setbounds(10,10,120,40); JInternalFrame cb2= new JInternalFrame("stip mij aan2",true); //cb2 is vergrootbaar thedesktop.add(cb2);cb2.setvisible(true); cb2.setbounds(150,10,100,30); c.add(thedesktop); men.add(item);men.add(item2); bar.add(men); setjmenubar(bar); setbounds(1,1,3500, 250); setvisible(true); setdefaultcloseoperation(jframe.exit_on_close); item.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {JInternalFrame frame=thedesktop.getse lectedframe(); if(frame!=null) {frame.dispose(); Visuele Gebruikers Omgevingen: Swing 33 33/395

34 thedesktop.repaint(); ); item2.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {JInternalFrame frame=thedesktop.getse lectedframe(); frame.setlocation(10,10); thedesktop.repaint(); ); pub lic static void main(string args[]) {new Swing5(); WindowListener WindowListeners kunnen gebruikt worden voor reactie op verkleinen tot icoon, terug vergroten vanuit icoon, sluiten. De interface WindowListener heeft vele routines en omdat we meestal maar geïnteresseerd zijn in één (vb WindowClosing), zal men meestal overerven van WindowAdapter. Deze klasse voorziet lege versies voor alle routines uit de interface WindowListener. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing16 extends JFrame { static public void main(string args[]) { new mij nframe("nog een ander venster"); class mijnframe extends JFrame {DefaultListModel lm =new Defaul tlistmodel(); public mijnframe(string titel) { super(titel); Container c=this.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new JList(lm)); this.setsize( 120,50); this.setvisible(true); addwindowlistener(new WindowListener() { public void windowactivated(windowevent e) // Invoked when the window is set to be the user's active window, // which means the window (or one of its subcomponents) will receive keyboard //events. {lm.addelement("activated"); public void windowclosed(windowevent e) // Invoked when a window has been closed as the result of // calling dispose on the window. {JOptionPane.showMessageDialog(null,"Closed"); public void windowclosing(windowevent e) // Invoked when the user attempts to close the window // from the window's system menu. {JOptionPane.showMessageDialog(null,"Closing"); public void windowdeactivated(windowevent e) Visuele Gebruikers Omgevingen: Swing 34 34/395

35 //Invoked when a window is no longer the user's active window, which means //that keyboard events will no longer be delivered //to the window or its subcomponents. {lm.addelement("deactivated"); public void windowdeiconified(windowevent e) // Invoked when a window is changed from a minimized to a normal state. {lm.addelement("deiconified"); public void windowiconified(windowevent e) // Invoked when a window is changed from a normal to a minimized state. {lm.addelement("iconified"); public void windowopened(windowevent e) //Invoked the first time a window is made visible. {lm.addelement("windowopened"); ); Visuele Gebruikers Omgevingen: Swing 35 35/395

36 Eigen layout managers maken LayoutManager is een interface die de volgende methoden heeft (We kunnen klassen die deze interface implementeren, inpluggen (strategy patroon) in onze interfaces. Zulk een klasse zal de layout voor ons doen.) void addlayoutcomponent(string name, Component comp) If the layout manager uses a per-component string, adds the component comp to the layout, associating it with the string specified by name. void layoutcontainer(container parent) Lays out the specified container. Dimension minimumlayoutsize(container parent) Calculates the minimum size dimensions for the specified container, given the components it contains. Dimension preferredlayoutsize(container parent) Calculates the preferred size dimensions for the specified container, given the components it contains. void removelayoutcomponent(component comp) Removes the specified component from the layout. Vooral de methode layoutcontainer is van belang: telkens u het venster vergroot of verkleint, zal deze methode terug opgeroepen worden om de componenten terug op de juiste plaats te zetten. Een diagonale layout In deze zelfgemaakte layout worden de componenten gewoon diagonaal geplaatst. Ook de breedte en hoogte van elke component staat vast. import javax.swing.*; import java.awt.*; import java.util.*; public class DiagonaalLayout implements LayoutManager { public void addlayoutcomponent(string name, Component comp) { Visuele Gebruikers Omgevingen: Swing 36 36/395

37 public void layoutcontainer(container target) { int nmembers = target.getcomponentcount(); int x = 10, y = 10; for (int i = 0 ; i < nmembers ; i++) { Component m = target.getcomponent(i); if (m.isvisible()) { Dimension d = m.getpreferredsize(); m.setsize(d.width, d.height); m.setlocation(x,y); x+=50;y+=50; public Dimension minimumlayoutsize(container parent) {return new Dimension(300,100); public Dimension preferredlayoutsize(container parent) {return new Dimension(300,200); public void removelayoutcomponent(component comp) { het testprogramma import javax.swing.*; import java.awt.event.*; import java.awt.*; public class Eigen_layout extends JFrame { public static void main(string args[]) { Visuele Gebruikers Omgevingen: Swing 37 37/395

38 Eigen_layout mainframe = new Eigen_layout(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(new DiagonaalLayout()); c.add(new JButton("test")); c.add(new JButton("test2")); c.add(new JButton("test3")); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); mainframe.setvisible(true); Een random layout Bij deze layout zullen alle componenten op een random plaats worden gezet. Ook de breedte en hoogte van elke component is random. Dus telkens we het scherm vergroten of verkleinen, worden de componenten op een andere plaats gezet en verandert hun grootte. Visuele Gebruikers Omgevingen: Swing 38 38/395

39 import javax.swing.*; import java.awt.*; import java.util.*; public class RandomLayout implements LayoutManager { public void addlayoutcomponent(string name, Component comp) { public void layoutcontainer(container target) { int nmembers = target.getcomponentcount(); for (int i = 0 ; i < nmembers ; i++) { Component m = target.getcomponent(i); if (m.isvisible()) { m.setsize(70+geefrandom(50), 30+geefRandom(50)); m.setlocation(10+geefrandom(300),10+geefrandom(300)); private int geefrandom(int max) {return (int)(math.random()*max); public Dimension minimumlayoutsize(container parent) {return new Dimension(300,100); public Dimension preferredlayoutsize(container parent) {return new Dimension(300,200); public void removelayoutcomponent(component comp) { Het testprogramma import javax.swing.*; import java.awt.event.*; import java.awt.*; Visuele Gebruikers Omgevingen: Swing 39 39/395

40 public class Eigen_layout extends JFrame { public static void main(string args[]) { Eigen_layout mainframe = new Eigen_layout(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(new RandomLayout()); c.add(new JButton("test")); c.add(new JButton("test2")); c.add(new JButton("test3")); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing 40 40/395

41 OO Hoe beginnen aan het flexibel maken van een probleem - denk eerst aan welke acties uw programma anders kan doen: o anders bewegen o anders reageren op muiskliks o iets anders genereren: html of xml o andere input: een databank, het toetsenbord o andere output: spraak of een visuele gebruikersinterface o andere interface: msdos interface, muisbestuurd programma De nadruk ligt hierbij op anders DOEN. (het is dus niet voldoende dat je twee versies van uw programma hebt, met bijvoorbeeld gewoon andere kleuren of andere tekeningen of een ander aantal verldjes: voor een ander aantal veldjes moet je niet echt flexibel programmeren door overerven: een andere parameter met de constructor meegeven volstaat.) Vanuit het eerste jaar kent men zeer goed de gedetermineerd wijze om een probleem te beschrijven: het probleem wordt in blokken onderverdeeld. Hoe die blokken met elkaar verbonden zijn, staat vast: het is een star netwerk van blokken. (Dit sluit aan bij starre functieoproepen: op voorhand is bij elke oproep onwrikbaar bepaald welk blok zal uitgevoerd worden.). Bij flexibele (polymorfe) functieoproepen is dit niet het geval. Flexibel programmeren is een wijze van naar een probleem te kijken. U moet van bij de aanvang van de programmatie al denken aan mogelijke veranderingen: welke delen zullen later iets anders moeten doen. (hierbij hoort dan andere code die zal uitgevoerd worden.) Probleem met gegevensgestuurde analyse In de analyse begint met traditioneel met het in schema brengen van de gegevens. (Normalisatie volgens Codd). Stel dat men KLANT, LEVERANCIER, BESTELLING, ARTIKEL heeft. Een klasse KLANT met als methoden : getnaam, setnaam, getadres, setadres, ligt voor de hand. Maar dit is niet echt object georiënteerd. Door deze get ters en set ters hebben we eigenlijk geen incapseling van de data. Meestal bevat zulke klasse ook geen echte logica, code: Er is dan helemaal niet voldaan aan de voorwaarden om een echte klasse te hebben: incapseling van data én code. Het zou wel erg eenvoudig zijn indien de overschakeling van gestructureerd naar object georiënteerd programmeren eenvoudig kon, door voor elk traditioneel bestand gewoon een klasse te voorzien. Visuele Gebruikers Omgevingen: Swing 41 41/395

42 De kern van het probleem ligt erin dat het probleem: klanten, leveranciers, bestellingen, artikels meestal gedetermineerd wordt gekozen: het ligt voor 100% vast wat het programma moet doen. (en veranderingen zijn voor later). Bij object georiënteerde aanpak staan veranderingen, flexibiliteit voorop. Men kan zeggen dat er toch bijvoorbeeld verschillende typen van klanten zijn, eventueel aangeduid met een speciale code in het record? Hierdoor varieert het programma toch? Hierbij ligt de variatie in variatie in data, niet in code. (Vroeger was er geen ander alternatief dan het programma iets anders te laten uitvoeren, dan een andere code in het record op te nemen en dan het programma anders te laten reageren op deze andere code). Meestal bevatten de klassieke problemen zoals KLANT, LEVERANCIER, ARTIKEL en BESTELLING bijna geen logica : meestal bestaat het uit het laten zien van lijsten, selecteren uit lijsten, weglaten en wijzigen van records. Hoe kan men zulk probleem flexibel maken als er bijna geen logica is. Misschien de enige logica die er is is: je mag een artikel weglaten als er nog bestellingen zijn van dat artikel. (deze regel is ook gedetermineerd.). Andere logica die men vindt bij klassieke problemen: bij een factuur moeten alle bedragen opgeteld worden, btw berekend worden: lijkt ook nogal gedetermineerd. Probeer met zulke problemen maar flexibel te werken. Objecten en records (klassen en bestanden) zijn zoals water en vuur. - van klassen kan je overerven, hierdoor kan je gedrag differentiëren. Het equivalent naar bestanden toe zijn object georiënteerde databanken. Object georiënteerde databanken zijn geen successtory. - Sql is een krachtige taal. Vanuit sql kan men alle velden van een record bevragen. Dit staat lijnrecht tegenover inkapseling bij klassen: bij klassen mogen de velden niet toegankelijk zijn (=inkapseling). - Klassen bevatten data en code. Bestanden bevatten alleen data. Hoe dan wel te werk gaan bij bovenstaand voorbeeld? Enkele tips: - de verwerking van klanten en leveranciers is grotendeels gelijkaardig: misschien kan overerving gebruikt worden. - Een object zou kunnen zijn: (kunnen later vervangen worden door andere versie) o Database access o Uitvoer o Delen van de interface Template pattern samen met Swing Stel dat we twee versies van hetzelfde programma moeten maken. In de eerste versie kunnen we op een knop drukken en zal een teller met één verhoogd worden. In een tweede versie, hebben we een andere tekst in de knop en zal de teller met een steeds groter getal verhoogd worden. Hoe dit programmeren? Voorzie functies voor de verschillen : stap (waarmee teller verhoogd wordt en boodch (boodschap die in de knop moet verschijnen). In de afgeleideklasse worden andere versies van deze functies opgegeven. Visuele Gebruikers Omgevingen: Swing 42 42/395

43 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class opgave extends JFrame { private static int teller1 = 1; private JTextField text = new JTextField(4); private JButton b1 = new JButton("button1"); public opgave() { super("opgave"); this.setlocation(100, 100); this.setdefaultcloseoperation(jframe.exit_on_close); Container c = getcontentpane(); c.setlayout( new FlowLayout()); c.add(new JLabel("teller1:")); c.add(text);c.add(b1); b1.setaction(new Aktie(this.boodch())) ; this.pack(); this.setvisible(true); int stap(){return 1; String boodch() { return "verhoog met een"; private class Aktie extends AbstractAction { public Aktie (String s) { super(s); public void actionperformed(actionevent e) {teller1=teller1+opgave.this.stap(); text.settext(""+teller1); public static void main(string args[]){new opgave(); public class opgave2 extends opgave {int j=1; public opgave2(){super(); String boodch(){return "ander verhoog"; int stap(){return j++; public static void main(string args[]){new opgave2(); Visuele Gebruikers Omgevingen: Swing 43 43/395

44 Strategy samen met Swing import javax.swing.*; import java.awt.*; import java.awt.event.*; public class opgave extends JFrame { private static int teller1 = 1; private JTextField text = new JTextField(4); private JButton b1 = new JButton("button1"); private VerhoogMet verhoog; public opgave(verhoogmet verhoog) { super("opgave"); this.verhoog = verhoog; this.setlocation(100, 100); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); Container c = getcontentpane(); c.setlayout( new FlowLayout()); c.add(new JLabel("teller1:")); c.add(text);c.add(b1); b1.setaction(new Aktie(verhoog.boodch())) ; this.pack(); private class Aktie extends AbstractAction { public Aktie (String s) { super(s); public void actionperformed(actionevent e) {teller1=teller1+ verhoog.stap(); text.settext(""+teller1); public static void main(string args[]) { new opgave(new VerhoogMet()); // new opgave(new VerhoogMet2()); We gaan nu een object van de klasse VerhoogMet gaan inpluggen in de klasse opgave. Een object van de klasse VerhoogMet wordt daarvoor doorgegeven aan de constructor van de klasse opgave. In de main routine van de klasse opgave voeren we één van de volgende instructies uit: new opgave(new VerhoogMet()); new opgave(new VerhoogMet2()); Visuele Gebruikers Omgevingen: Swing 44 44/395

45 (We moeten het programma daarvoor twee maal compileren, éénmaal met de eerste instructie en éénmaal met de tweede mogelijkheid). Bij de eerste mogelijkheid zal gewerkt worden met een object van de klasse VerhoogMet en bij de tweede mogelijkheid zal gewerkt worden met een object van de klasse VerhoogMet2. Hierdoor is de klasse opgave flexible daar er in die klasse gewerkt wordt met een referentie verhoog en we niet op voorhand weten tot welke klasse het object zal behoren: VerhoogMet of VerhoogMet2 of nog een andere klasse die overerft van VerhoogMet. Hierdoor kunnen we het gedrag van de klasse opgave wijzigen, gewoon door over te erven van de klasse VerhoogMet en een object van die klasse door te geven aan de constructor van opgave. In de klasse opgave wordt met verhoog.stap en verhoog.boodch gewerkt, zonder concreet te weten met welke versie van stap of boodch gewerkt wordt. import javax.swing.*; import java.awt.*; public class VerhoogMet { public int stap() {return 1; String boodch() { return "verhoog met "; class VerhoogMet2 extends VerhoogMet {int j=1; public int stap() {return j++; String boodch() { return "verhoog anders "; Visuele Gebruikers Omgevingen: Swing 45 45/395

46 Ander voorbeeld Template Strategy patroon Een eenvoudige toepassing: twee tekst velden: al wat je in de eerste intypt, wordt tijdens het typen gecopieerd naar het tweede tekstveld. import java.awt.*; import java.awt.event.*; import javax.swing.*; class Imp extends JFrame { JTextField woord1,woord2; Container c; public Imp() {super("template"); c=getcontentpane(); c.setlayout(new FlowLayout()); woord1=new JTextField(); woord1.setpreferredsize(new Dimension(150,30)); woord2=new JTextField(); woord2.setpreferredsize(new Dimension(150,30)); c.add(woord1);c.add(woord2); setdefaultcloseoperation(jframe.exit_on_close); woord1.addkeylistener(new KeyAdapter() { public void keyreleased(keyevent e) {woord2.settext(woord1.gettext()); ); setsize(350,150); setvisible(true); public class Basic {public static void main(string args[]) {new Imp(); De keylistener zorgt ervoor dat telkens men een letter intypt in het eerste veldje, heel dit veldje gecopieerd wordt naar het tweede veldje. Wilt men hier meerdere versies van maken, dan moet men functies toevoegen. Wilt men bijvoorbeeld de actie woord2.settext(woord1.gettext()) in een andere versie kunnen vervangen, dan moet men dit in een functie steken. In de volgende versie wordt hiervoor de functie actie genomen. Visuele Gebruikers Omgevingen: Swing 46 46/395

47 Template patroon In de eerste versie wordt al hetgeen men intypt gewoon gecopieerd naar het tweede veldje. In de tweede versie wordt al hetgeen men intypt in hoofdletters gecopieerd naar het tweede veldje. In de derde versie wordt al hetgeen men intypt in omgekeerde volgorde gecopieerd naar het tweede veldje.(terwijl men typt). Visuele Gebruikers Omgevingen: Swing 47 47/395

48 import java.awt.*; import java.awt.event.*; import javax.swing.*; class TemplateImp extends JFrame { JTextField woord1,woord2; Container c; public TemplateImp() {super("template"); c=getcontentpane(); c.setlayout(new FlowLayout()); woord1=new JTextField(); woord1.setpreferredsize(new Dimension(150,30)); woord2=new JTextField(); woord2.setpreferredsize(new Dimension(150,30)); c.add(woord1);c.add(woord2); setdefaultcloseoperation(jframe.exit_on_close); woord1.addkeylistener(new KeyAdapter() { public void keyreleased(keyevent e) {actie(woord1,woord2); ); setsize(350,150); setvisible(true); public void actie(jtextfield woord1,jtextfield woord2) {String tekst1=woord1.gettext(); String tekst2=transform(tekst1); woord2.settext(tekst2); public String transform(string t){return t; Visuele Gebruikers Omgevingen: Swing 48 48/395

49 class TemplateImp2 extends TemplateImp { public String transform(string t){return t.touppercase(); class TemplateImp3 extends TemplateImp { public String transform(string t) {StringBuffer sb=new StringBuffer(t); StringBuffer reverse=sb.reverse(); return reverse.tostring(); public class Template {public static void main(string args[]) {new TemplateImp(); new TemplateImp2(); new TemplateImp3(); De truk bestaat erin om voor hetgeen men wil wijzigen functies te definieren en hiervoor andere versies te voorzien in de afgeleide klassen. Bij het template patroon moet er gemeenschappelijke code staan in de basis klasse. In dit geval is dit de volgende code: woord1.addkeylistener(new KeyAdapter() { public void keyreleased(keyevent e) {actie(woord1,woord2); ); De exacte betekenis hiervan ligt niet meer vast omdat het op voorhand niet meer vaststaat welke versie van de functie actie zal opgeroepen worden. (In de voorbeeld werd geopteerd om niet heel de routine actie in de afgeleide klassen te herdefinieren, maar wel de deelfunctie transform. Ook de gemeenschappelijke code: public void actie(jtextfield woord1,jtextfield woord2) {String tekst1=woord1.gettext(); String tekst2=transform(tekst1); woord2.settext(tekst2); heeft geen vaste betekenis meer omdat er opvoorhand niet vaststaat welke versie van de routine transform zal worden opgeroepen. Eigenlijk staat er this.transform(tekst1). Hiermee wordt aangegeven dat tijdens de uitvoering van dit bevel zal gekeken worden waarheen de referentie this wijst. Er worden drie objecten aangemaakt: new TemplateImp(); new TemplateImp2(); new TemplateImp3(); Visuele Gebruikers Omgevingen: Swing 49 49/395

50 Bij new TemplateImp2() zal tijdens de uitvoering van this.transform(tekst1) de versie van transform nemen die hoort bij de klasse TemplateImp2. Zo ook voor de andere objecten. Dit is flexibiliteit. Voor het template patroon is het absoluut noodzakelijk dat er gemeenschappelijke code in de basisklasse staat die zal worden gewijzigd door het opgeven van meerdere versies van een functie die in deze gemeenschappelijke code zal worden opgeroepen. Door overerving verandert men het gedrag van de basis klasse. Strategy patroon De uitvoer van het programma is hetzelfde als bij het template patroon. import java.awt.*; import java.awt.event.*; import javax.swing.*; class GebruiktStrategy extends JFrame { DoIets reftoactie; JTextField woord1,woord2; Container c; public GebruiktStrategy(DoIets reftoactieparameter) {super("strategy"); this.reftoactie=reftoactieparameter; c=getcontentpane(); c.setlayout(new FlowLayout()); woord1=new JTextField(); Visuele Gebruikers Omgevingen: Swing 50 50/395

51 woord1.setpreferredsize(new Dimension(150,30)); woord2=new JTextField(); woord2.setpreferredsize(new Dimension(150,30)); c.add(woord1);c.add(woord2); setdefaultcloseoperation(jframe.exit_on_close); woord1.addkeylistener(new KeyAdapter() { public void keyreleased(keyevent e) {reftoactie.actie(woord1,woord2); ); setsize(350,150); setvisible(true); interface DoIets { public void actie(jtextfield woord1,jtextfield woord2); public String transform(string t); class StrategyImp implements DoIets { public void actie(jtextfield woord1,jtextfield woord2) {String tekst1=woord1.gettext(); String tekst2=transform(tekst1); woord2.settext(tekst2); public String transform(string t){return t; class StrategyImp2 extends StrategyImp { public String transform(string t){return t.touppercase(); class StrategyImp3 extends StrategyImp { public String transform(string t) {StringBuffer sb=new StringBuffer(t); StringBuffer reverse=sb.reverse(); return reverse.tostring(); public class Strategy {public static void main(string args[]) {new GebruiktStrategy(new StrategyImp()); new GebruiktStrategy(new StrategyImp2()); new GebruiktStrategy(new StrategyImp3()); Door inpluggen verandert men het gedrag van een concreet object (niet van een klasse). Men gaat nu een referentie van een object dat een interface implementeert (DoIets) inpluggen. De klasse GebruiktStrategy weet op voorhand niet welk object die gaat zijn. Deze klasse weet Visuele Gebruikers Omgevingen: Swing 51 51/395

52 alleen dat het die interface implementeert en dus de methodes actie en transform zal hebben. In het hoofdprogramma gaan we drie verschillende objecten gaan inpluggen. Het voordeel van strategy is dat je dit inpluggen zelfs tijdens de uitvoering van het programma kunt doen. (Je verandert immers het gedrag van een concreet object, niet van een klasse). In de klasse GebruiktStrategy staat er reftoactie.actie(woord1,woord2); als bevel. Het ligt weeral niet op voorhand vast welke versie van de routine actie zal opgeroepen worden. reftoactie krijgt een concrete referentie door de constructor van de klasse GebruiktStrategy. Zo zal bij new GebruiktStrategy(new StrategyImp2()); de veranderlijke reftoactie wijzen naar het object new StrategyImp2 en zal bijgevolg de routine transform van deze klasse opgeroepen worden. Het voordeel van strategy over template ligt in de mogelijke combinaties. In dit voorbeeld werd alleen maar de actie ingeplugd. Stel dat je ook een TaalObject kunt inpluggen om zodoende franse, of engelse of nederlandse titels te laten zien in uw JFrame. Je kan dan door middel van dit inpluggen alle mogelijke combinaties van talen en acties verkrijgen door gewoon de juiste taal en juiste actie in te pluggen. Visuele Gebruikers Omgevingen: Swing 52 52/395

53 Abstract programmeren, een voorbeeld Alle io klassen van java werden abstract geprogrammeerd. Om dit model duidelijk te maken, zullen we eerst zelf even een abstract io-programma maken. We maken daarom een denkbeeldige abstracte klasse FlexIn. In deze abstracte klasse schrijven we een routine run. We maken deze routine run met behulp van een nog niet concrete routine (abstract dus) lees die ergens een character zal lezen. De routine run moet stoppen indien de nog niet concrete routine (abstract dus) einde true als resultaat zal geven. Alles wat gelezen wordt wordt op het scherm getoond. Als de invoer een teken bevat, moet er naar een nieuwe lijn in de uitvoer gegaan worden. De klasse Flexin is erg eenvoudig en nog niet helemaal concreet. Het leuke ervan is dat we toch de logica van de routine run kunnen neerschreven in functie van nog later in te vullen routines. import java.io.*; abstract class FlexIn { private static int x=1,y=1; public FlexIn(){//x=1;y=1; abstract public boolean einde(); abstract public char lees() throws IOException; public void run() throws IOException {char c = lees(); while(!einde()) { if(c==' ') {x=1;y++; else { Scherm.gotoxy(x,y);Scherm.schrijf(c);x++; c=lees(); De eerste klasse die gaat overerven van Flexin is Bestand. In deze klasse moeten we alleen maar concrete versies van de routines lees en einde geven. In deze klasse hoeven we ons helemaal niet meer bezig te houden met de routine run die we later in onze toepassing zullen gebruiken. Deze routine wordt overgeërfd maar zal zich bij een object van de klasse Bestand anders gaan gedragen. Een ander leuk voordeel van object georiënteerd programmeren bestaat erin dat alle details die te maken hebben met access naar het bestand mooi ingekapseld zijn in de klasse Bestand. Deze details staan niet in de algemene klasse Flexin en ook niet in de toepassing. Als er later iets wijzigt aan het werken met bestanden, moeten we de aanpassing maar op één plaats doen: in de klasse Bestand. class Bestand extends FlexIn {private FileReader fr; private BufferedReader br; private char c; public Bestand(String fil)throws IOException {super(); fr=new FileReader(fil); br=new BufferedReader(fr); public boolean einde() {return c==(char)-1; public char lees() throws IOException Visuele Gebruikers Omgevingen: Swing 53 53/395

54 { c=(char)br.read(); return c; De klasse Bestand moet nu een constructor hebben die de naam van het bestand doorkrijgt. Elk character dat we lezen gaan we in een veranderlijke c steken. Het einde van het bestand wordt aangegeven doordat het character dat overeenkomt met het getal -1 (hexadecimaal FFFF ) wordt gelezen. De tweede klasse die gaat overerven van Flexin zal ToetsenBord2 zijn. Hierin zullen we van System.in gaan lezen. Een echt einde van het lezen van een toetsenbord is er niet. Het einde van het lezen stellen we daarom per conventie vast op het lezen van het character #. class ToetsenBord2 extends FlexIn {private InputStreamReader fr; private BufferedReader br; private char c; public ToetsenBord2()throws IOException {super(); fr=new InputStreamReader(System.in); br=new BufferedReader(fr); public boolean einde() {return c=='#'; public char lees() throws IOException { c=(char)br.read(); return c; Een derde versie van Flexin zal als invoer een String nemen die we doorgeven aan de constructor van StringInput. Het einde van het lezen van zulke invoer valt natuurlijk samen met het einde van de string. Hiervoor moeten we een teller i bijhouden. Telkens als we een letter uit de string lezen, zal i verhoogd worden. Als hij gelijk wordt aan de lengte van de string (aantal), is het einde bereikt van de invoer. class StringInput extends FlexIn {private String str; private int aantal,i; private char c; public StringInput(String str) {super(); this.str=str; aantal=str.length(); i=0; public boolean einde() { Scherm.schrijf(""+i+" "+aantal+" "); return i==aantal; public char lees() throws IOException { c=str.charat(i);i++; return c; Visuele Gebruikers Omgevingen: Swing 54 54/395

55 In het test programma gaan we objecten maken van de klassen ToetsenBord2, Bestand en StringInput en hiervoor de routine run oproepen. Steeds zal de invoer overgebracht worden naar scherm (een nederlandse versie van de klasse Screen). Bij het lezen van zal er naar een nieuwe lijn overgegaan worden. public class Test {public static void main(string arg[]) throws IOException {Scherm.clearDevice(); new ToetsenBord2().run(); Scherm.schrijf("einde1"); Scherm.schrijf("start"); new Bestand("test.txt").run(); Scherm.schrijf("einde2"); new StringInput("abc def ghi").run(); Reader Writer klassen zijn abstract gemaakt /* programmeer niet meer concreet. Stel dat u 20 characters moet intypen en alle 'a' moet vervangen door 'A'. Programmeer dit dan niet voor gewone toetsenbord invoer. Programmeer het dadelijk voor een abstract 'Reader' object. (InputstreamReader, BufferedReader, FileReader, StringReader, CharArrayReader zijn allemaal Reader objecten omdat hun klasse overerft van de abstracte klasse Reader). */ Veralgemeen steeds zoveel mogelijk. U kunt dit eenvoudig doen door telkens u een klasse nodig hebt even na te gaan er een basisklasse erboven staat die ook bruikbaar is. Hierdoor is het automatisch algemener geprogrammeerd. -->Reader!!is een !!!!! +----BufferedReader FileReader CharArrayReader StringReader!! InputStreamReader Het voorbeeld hieronder is geprogrammeerd voor de abstracte klasse Reader. BufferedReader klasse is ook op deze wijze gemaakt: geprogrammeerd voor een abstract Reader object. (En dit terwijl het zelf een Reader object is. In het schema hierboven merk je dat er een pijl van BufferedReader naar Reader gaat om aan te geven dat BufferedReader geprogrammeerd is voor een abstract Reader object. import java.io.*; public class Test { public static void tellettera(reader invoer) Visuele Gebruikers Omgevingen: Swing 55 55/395

56 {try{char c=' '; c=(char)invoer.read(); for (int i=0;i<20;i++) {System.out.print(c=='a'?'A':c); c=(char)invoer.read(); catch(ioexception e){system.out.print("fout"); System.out.println(""); public static void main(string arg[]) {tellettera( new InputStreamReader(System.in)); tellettera( new BufferedReader(new InputStreamReader(System.in))); tellettera( new StringReader("stringreader")); tellettera( new BufferedReader( new StringReader("bufferedstringreader"))); try{ tellettera( new FileReader("text.txt")); tellettera( new BufferedReader(new FileReader("text.txt"))); catch(filenotfoundexception e) {System.out.println("niet gevonden"); tellettera( new CharArrayReader( new char[]{'c','h','a','r','a','r','r','a','y')); tellettera( new BufferedReader( new CharArrayReader( new char[]{'b','u','f','f','e','r','e','d', 'c','h','a','r','a','r','r','a','y'))); tellettera( new RandomReader()); tellettera( new SchermReader()); De klasse Scherm als Reader benaderen De nederlandstalige versie van de klasse Screen, Scherm kunnen we toch omtoveren tot een standaard Reader klasse. We moeten eerst eens de documentatie van de abstracte klasse Reader bekijken. Ze heeft de volgende methoden: abstract void close() Close the stream. void mark(int readaheadlimit) Mark the present position in the stream. boolean marksupported() Tell whether this stream supports the mark() operation. int read() Read a single character. int read(char[] cbuf) Read characters into an array. abstract int read(char[] cbuf, int off, int len) Read characters into a portion of an array. Visuele Gebruikers Omgevingen: Swing 56 56/395

57 boolean ready() Tell whether this stream is ready to be read. void reset() Reset the stream. long skip(long n) Skip characters. Ze is dus geprogrammeerd zoals de klasse Flexin hiervoor. Nu zijn er ook twee abstracte routines waarvoor we een concrete versie zullen moeten geven in een afgeleide klasse: close en read(char []cbuf,int off,int len). Hiervoor moeten we overerven van de abstracte Reader klasse. In onze toepassing maken we steeds gebruik van de routine read die in de klasse Reader werd geprogrammeert. (zoals de routine in het voorbeeld hiervoor bij de klasse Flexin). Deze routine read roept een routine read op met drie parameters : - een array van characters waaruit gelezen zal worden. - Een offset: De plaats in deze array waar de character moeten komen - Het aantal character dat op die plaats moet komen Dit komt bizar over. Waarom een array van characters, waarom een offset? Omdat iemand anders de abstracte klasse Reader gemaakt heeft, moeten we verder gaan op de uitleg die bij de klasse Reader te vinden is: //Read characters into a portion of an array. This method will block until some input is available, an I/O error occurs, or the end of the stream is reached. Parameters:cbuf - Destination bufferoff - Offset at which to start storing characterslen - Maximum number of characters to readreturns:the number of characters read, or -1 if the end of the stream has been reached public int read(char[] cbuf,int off,int len) throws IOException Klassieke programmeurs hebben last met het zelf geven van versies van deze routine read met drie parameters: ze zien ook niet waar deze routine wordt opgeroepen. En in de toepassing roepen ze een andere routine op: de routine read zonde parameters. Object georiënteerd programmeren is duidelijk abstracter. // adapter klasse class SchermReader extends Reader { public void close() throws IOException { public int read(char[] cbuf,int off,int len) throws IOException { //Scherm.gotoxy(1,2); Scherm.schrijf("geef "+len+" aantal character(s)"); for(int i=0;i<len;i++) {while(!toetsenbord.hit()); char c=toetsenbord.getch(); cbuf[off+i]=c; Scherm.schrijf(" gelezen char:" + c); return len; Tevens moeten we een concrete versie van de abstracte routine close voorzien. Omdat we in SchermReader gaan lezen van een nederlandse versie van de klasse Screen, kan onze concrete versie van close leeg zijn. Visuele Gebruikers Omgevingen: Swing 57 57/395

58 De abstracte uitleg van deze abstracte routine: //Close the stream. Once a stream has been closed, further read(), ready(), mark(), or reset() invocations will throw an IOException. Closing a previously-closed stream, however, has no effect. De concrete versie van de routine read(char[] cbug,int off, int len) zal eerst op scherm vragen hoeveel character de gebruiker wilt intypen. Vervolgens wordt dat aantal characters van scherm gelezen en weggeborden in cbuf[off+i]. Een eigen Reader die random characters genereert Echt bruikbaar is het niet, maar wel leuk. class RandomReader extends Reader { public void close() throws IOException { public int read(char[] cbuf,int off,int len) throws IOException { for(int i=0;i<len;i++) { double d=math.random(); char c= (char)( (int)'a'+ (int) (d*26)); cbuf[off+i]= c; return len; Visuele Gebruikers Omgevingen: Swing 58 58/395

59 Componenten JTabbedPane, JComboBox Een JComboBox is een openklapbare keuzelijst. Een JTabbedPane kan meerdere tabs bevatten. Elke tab omvat een component. Een samengestelde component wordt bekomen door een JPanel te gebruiken. Een panel is een container. Je kan direct met behulp van add componenten toevoegen aan een JPanel. Het heeft per default al een FlowLayout manager. Je kan aan een tabbed pane op twee manieren elementen toevoegen: met add wordt de nieuwe keuzemogelijkheid achteraan toegevoegd. Met behulp van insert kan je zelf bepalen waar de nieuwe tab komt. Elke tab kan ook een icoontje hebben. (niet gebruikt in dit voorbeeld). Een tip is een boodschap die verschijnt als je met de muis over de tab beweegt. import javax.swing.*; import java.awt.*; public class Swing7 extends JFrame {public Swing7() {super("swing7"); Container c=getcontentpane(); c.setlayout(new FlowLayout()); JComboBox l= new JComboBox(new String[]{"een","twee","drie"); c.add(l); JPanel p =new JPanel(); p.add(new JLabel("ha")); p.add(new JButton("druk")); p.add(new JSlider()); //c.add(p); JTabbedPane t=new JTabbedPane(); t.setsize(300,300); t.inserttab("1",null/*icoontje*/,new JLabel("11"),"tip111",0); t.addtab("een",p); t.addtab("twee",new JLabel("label2")); Visuele Gebruikers Omgevingen: Swing 59 59/395

60 c.add(t); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {new Swing7(); JList Een eenvoudige JList is te initializeren met behulp van een array van objecten (vb een array van Strings. Een gewone JList kan, naast een array van objecten, ook met behulp van een Vector geïnitialiseerd worden. Eens deze JList gemaakt is, kan men er geen elementen meer aan toevoegen of weglaten. Wil je een JList waar je later nog elementen kunt toevoegen of weglaten, dan moet je de JList initialiseren met een DefaultListModel object. De klasse DefaultListModel heeft methodes om achteraan toe te voegen (addelement), op een juiste plaats toe te voegen (insertelementat(object, index) ), weg te laten (removeelement, removeelementat), te veranderen (setelementat ). In het voorbeeld hieronder verbinden we twee verschillende JList s met hetzelfde DefaultListModel. Telkens we veranderingen aanbrengen aan het default list model, veranderen beide JList s. De Swing klassen trachten de data te scheiden van de visualisatie en verwerking. Als je in het tekstveldje iets intypt en vervolgens op voegtoe klikt, dan wordt de ingevulde tekst toegevoegd aan de DefaultListModel. Hierdoor worden beide JList s aangepast. De delete knop, om uit het DefaultListModel iets weg te laten, werkt alleen maar als je eerst uit de linkse JList iets selecteert. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing7B extends JFrame {JTextField text = new JTextField(8); String[] data = {"one", "two", "three", "four"; JList fixedlist = new JList(data);// niet wijzigbare list DefaultListModel listmodel= new DefaultListModel();// we kunnen // elementen toegevoegen, weglaten uit DefaultListModel JList list1 = new JList(listModel); Visuele Gebruikers Omgevingen: Swing 60 60/395

61 JList list2 = new JList(listModel); JButton but = new JButton("voeg toe"); JButton but2 = new JButton("laat weg"); public Swing7B() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(fixedlist);c.add(list1);c.add(list2); c.add(text);c.add(but);c.add(but2); fixedlist.getselectionmodel().setselectionmode(listselectionmodel.multiple_interval_selection); but.setaction(new Aktie()); but2.setaction(new DeleteAktie()); list1.setpreferredsize(new Dimension(70,200)); list2.setpreferredsize(new Dimension(70,200)); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); class Aktie extends AbstractAction {public Aktie(){super("V O E G toe"); public void actionperformed(actionevent e) {String ingetypt=text.gettext(); listmodel.addelement(ingetypt); class DeleteAktie extends AbstractAction {public DeleteAktie(){super("Delete selected item"); public void actionperformed(actionevent e) {String ingetypt=text.gettext(); listmodel.remove(list1.getselectedindex()); public static void main(string args[]) {new Swing7B(); Je kan steeds aan een JList bepalen welk element geselecteerd is met getselectedindex en met getselectedvalue. Je kan ook programmatorisch iets selecteren met setselectedindex. Selecting more than one element in a list In dit programma kan je uit een list box meer dan één element selecteren (ctrl ingedrukt houden tijdens selectie). De geselecteerde elementen worden getoond wanneer de show selected button wordt ingedrukt. Deze button kan wel door middel van de andere button geactiveerde of gedesactiveerd worden. import javax.swing.*; import java.awt.*; import java.awt.event.*; Visuele Gebruikers Omgevingen: Swing 61 61/395

62 public class Swing7C extends JFrame { String[] data = {"one", "two", "three", "four","five"; JList fixedlist = new JList(data);// niet wijzigbare list JButton but = new JButton("bla bla"); JButton but2 = new JButton("bla bla"); public Swing7C() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(fixedlist); c.add(but);c.add(but2); but.setaction(new Aktie()); but2.setaction(new Aktie2()); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); class Aktie extends AbstractAction {public Aktie(){super("Show selected"); public void actionperformed(actionevent e) {Object [] gekozen = fixedlist.getselectedvalues(); for(int i=0;i<gekozen.length;i++) {JOptionPane.showMessageDialog(null,gekozen[i].toString()); class Aktie2 extends AbstractAction {public Aktie2(){super("activate/desactivate"); public void actionperformed(actionevent e) {if(but.isenabled() ) but.setenabled(false); else but.setenabled(true); public static void main(string args[]) {new Swing7C(); Naast getselectedvalues kan men ook getselectedindeces gebruiken. Men krijgt dan gewoon een array van integers met de nummers van de geselecteerde elementen. JScrollPane In dit voorbeeld hebben we een JList en een JTextArea. Beiden worden doorgegeven aan de constructor van een JScrollPane. Hierdoor kunnen we scrollen door de JList en de JTextArea. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing7D extends JFrame {Object [] elementen= new Object[8]; Visuele Gebruikers Omgevingen: Swing 62 62/395

63 JList fixedlist; public Swing7D() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); elementen[0]="een"; elementen[1]="twee"; elementen[2]="drie"; elementen[3]="vier"; elementen[4]="vijf"; elementen[5]="zes"; elementen[6]="zeven"; elementen[7]="acht"; fixedlist=new JList(elementen); JScrollPane scroller=new JScrollPane(fixedList); c.add(scroller); scroller.setpreferredsize(new Dimension(40,120)); JTextArea ta=new JTextArea(100,100); JScrollPane scroller2=new JScrollPane(ta); c.add(scroller2); scroller2.setpreferredsize(new Dimension(150,120)); this.setlocation(100,100);this.setsize(120,100); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {new Swing7D(); In plaats van een array van objecten hadden we ook een array van Strings kunnen gebruiken (zoals in vorig voorbeeld). Een array van Strings is een array van objecten. (elke String is een object). JScrollBar, JToolBar public class Swing8 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JScrollBar l= new JScrollBar(); c.add(l); JToolBar jt=new JToolBar(); jt.add(new JButton("een")); jt.add(new JButton("twee")); c.add(jt); Visuele Gebruikers Omgevingen: Swing 63 63/395

64 Toolbar verplaatsen, Scrollbar horizontaal. Een JToolBar komt maar echt tot zijn recht wanneer het wordt toegevoegd aan een BorderLayout. In het centrum van de BorderLayout wordt het object dat door de toolbar kan worden gemanipuleerd, toegevoegd. Omdat de andere zijkanten van de borderlayout leeg gelaten werden, kan je de toolbar gemakkelijk naar één van de andere zijkanten verslepen. In dit voorbeeld kiezen we met behulp van de toolbar de hoeveelheid rood, groen en blauw. Wanneer de button van de toolbar wordt ingedrukt, verandert de achtergrondkleur van de label die zich in het centrum bevindt. Een toolbar kan ook uit de frame gesleept worden. Het vormt dan een volwaardig frame: import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing8B extends JFrame {JScrollBar scrol1= new JScrollBar(JScrollBar.HORIZONTAL); JScrollBar scrol2= new JScrollBar(JScrollBar.HORIZONTAL); JScrollBar scrol3= new JScrollBar(JScrollBar.HORIZONTAL); JLabel lab = new JLabel("gewone tekst"); JButton but = new JButton("bla bla"); JToolBar tool=new JToolBar(); public Swing8B() {Container c=getcontentpane(); c.setlayout(new BorderLayout());// is default voor JFrame scrol1.setmaximum(255); scrol2.setmaximum(255); scrol3.setmaximum(255); tool.add(scrol1);tool.add(scrol2);tool.add(scrol3); c.add(lab,borderlayout.center);tool.add(but); but.setaction(new Aktie()); c.add(tool,borderlayout.north); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); class Aktie extends AbstractAction Visuele Gebruikers Omgevingen: Swing 64 64/395

65 {public Aktie(){super("verander kleur"); public void actionperformed(actionevent e) {int rood=scrol1.getvalue(); int groen=scrol2.getvalue(); int blauw=scrol3.getvalue(); lab.setbackground(new Color(rood,groen,blauw)); lab.setopaque(true);// ondoorzichtig lab.settext("rgb: "+rood+" "+groen+" "+blauw); public static void main(string args[]) {new Swing8B(); De scrollbars zijn door middel van een parameter van de constructor nu horizontaal. Met behulp van setmaximum wordt de maximale waarde op 255 ingesteld. Dit moet daar kleuren worden weergegeven met een getal tussen 0 en 255. Borders, setmnemonic, In dit voorbeeld gaan we borders rond componenten zetten. U kunt elke component met behulp van setborder een andere border geven. Hier werken we met drie JPanels. (Een JPanel is een Container waar u dadelijk elementen kunt toevoegen. In panel1 wordt button2 toegevoegd. Er wordt met een BevelBorder gewerkt. Button4 zit in panel3. Button3 en panel3 zitten in panel2. Panel2 heeft een blauwe LineBorder. Panel3 heeft een EtchedBorder. JButtons, JMenuItems, JRadioButtons en JCheckBoxen kan je verbinden met sneltoetsen. Met behulp van setmnemonics kan je een alt-toets combinatie verbinden met de component. Indien in ons voorbeeld alt-1 wordt ingedrukt, dan wordt button1 ingedrukt. Alt-2: button2 en alt-3 : button3, alt-4: button4. Elke component kan met toets-combinaties verbonden worden met acties. In dit voorbeeld wordt panel1 met toets a verbonden met een AbstractAction. Per component worden drie inputmappen bijgehouden met toetscombinaties. Visuele Gebruikers Omgevingen: Swing 65 65/395

66 Om het onderscheid tussen deze drie mappen te begrijpen, moet je het begrip focus kennen. Voor Buttons zien we duidelijk wanneer deze de focus hebben: een klein rechthoekje verschijnt rond de tekst (zie figuur hier net boven: het rechthoekje rond de ok ). We kunnen eenvoudig focus geven aan componenten waar we op kunnen klikken. Een component met focus is dus het laatst geselecteerd component. We kunnen een component ook de focus geven door middel van component.requestfocus(); Er worden drie inputmappen per component bijgehouden. Hiermee kunt u precies aangeven wanneer een toets met een component wordt verbonden. Bij WHEN_FOCUSED zal de toets alleen maar effect hebben als de component de focus heeft. In ons voorbeeld wordt panel2 verbonden met toets b. Normaal kan een panel geen focus krijgen, zodat de toets nooit een effect zou hebben. Daarom heb ik expliciet de focus aan panel 2 gegeven met panel2.requestfocus(). Het meest algemene is WHEN_IN_FOCUSED_WINDOW. Wanneer de component zich ergens in een focused window zit (vb het hoofdframe) is de toets actief. In ons voorbeeld wordt panel1 verbonden met toets a. Deze is dus steeds actief. De derde mogelijkheid is WHEN_ANCESTOR_OF_FOCUSED_COMPONENT. In ons voorbeeld zit button4 in panel3. Panel 3 is verbonden met toets c. Alleen maar als we eerst op button4 klikken (krijgt dan de focus), is panel3 de ancestor van een focused component (button4) en heeft de toets effect. De eerste inputmap is actief WHEN_IN_FOCUSED_WINDOW. De tweede inputmap van een component : WHEN_FOCUSED De derde inputmap van elke component: WHEN_ANCESTOR_OF_FOCUSED_COMPONENT import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; public class Swing8C extends JFrame {JPanel panel1 = new JPanel(); JPanel panel2 = new JPanel(); JPanel panel3 = new JPanel(); JButton but1 = new JButton("but1"); JButton but2 = new JButton("but2"); JButton but3 = new JButton("but3"); JButton but4 = new JButton("but4"); public Swing8C() {Container c=getcontentpane(); panel1.setborder(new BevelBorder(BevelBorder.RAISED)); panel2.setborder(new LineBorder(Color.blue)); panel3.setborder(new EtchedBorder(Color.yellow,Color.red)); c.setlayout(new FlowLayout()); c.add(but1);c.add(panel1);c.add(panel2); panel1.add(but2); panel2.add(but3);panel2.add(panel3); panel3.add(but4); panel1.setpreferredsize(new Dimension(300,100)); panel2.setpreferredsize(new Dimension(300,100)); panel3.setpreferredsize(new Dimension(150,80)); but1.setmnemonic(keyevent.vk_1);// geldt ook voor MenuItem, Radiobutton, checkbox but2.setmnemonic(keyevent.vk_2);// alt-2 but3.setmnemonic(keyevent.vk_3);// alt-3 but4.setmnemonic(keyevent.vk_4);// alt-4 but1.setaction(new Aktie("BUT1")); but2.setaction(new Aktie("BUT2")); but3.setaction(new Aktie("BUT3")); but4.setaction(new Aktie("BUT4")); KeyStroke ks=keystroke.getkeystroke('a'); InputMap inputmap1=panel1.getinputmap(jcomponent.when_in_focused_window); inputmap1.put(ks,"panel1actie"); Visuele Gebruikers Omgevingen: Swing 66 66/395

67 panel1.getactionmap().put("panel1actie",new Aktie("**panel1**")); KeyStroke ks2=keystroke.getkeystroke('b'); panel2.getinputmap(jcomponent.when_focused).put(ks2,"panel2actie"); panel2.getactionmap().put("panel2actie",new Aktie("**panel2**")); KeyStroke ks3=keystroke.getkeystroke('c'); panel3.getinputmap(jcomponent.when_ancestor_of_focused_component).put(ks3,"panel3actie"); panel3.getactionmap().put("panel3actie",new Aktie("**panel3**")); this.setlocation(100,100); this.setsize(250,120); this.setvisible(true); this.setdefaultcloseoperation(jframe.exit_on_close); class Aktie extends AbstractAction {public Aktie(String s){super(s); public void actionperformed(actionevent e) {JOptionPane.showMessageDialog(null,e.getActionCommand()); panel2.requestfocus(); public static void main(string args[]) {new Swing8C(); Scrollen door een heel frame import javax.swing.*; import java.awt.*; import java.awt.event.*; class Swing_scrollpane extends JFrame { public Swing_scrollpane() {JLabel lab = new JLabel(new ImageIcon("Tekening.jpg")); JPanel panel=new JPanel(); panel.setlayout(new BorderLayout()); panel.add(lab,borderlayout.center); panel.add(new JLabel("een tekening"),borderlayout.north); panel.add(new JButton("test"),BorderLayout.SOUTH); JScrollPane pan= new JScrollPane(panel); setcontentpane(pan); setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) { Swing_scrollpane mainframe = new Swing_scrollpane(); Visuele Gebruikers Omgevingen: Swing 67 67/395

68 mainframe.setsize(400, 400); mainframe.settitle("swing_scrollpane"); mainframe.setvisible(true); Scrollen door een heel frame gebeurd door een JScrollPane als contentpane op te geven. Omdat een JScrollPane maar één component meekrijgt in zijn constructor, moeten we meerdere elementen eerst grouperen in een JPanel. In bovenstaand voorbeeld hebben we een JLabel, een JLabel met tekening (een ImageIcon) en een JButton eerst samengevoegd in een JPanel. Tekeningen worden het eenvoudigst met een ImageIcon getoond. Een ImageIcon kan meegegeven worden met de constructor van een JLabel en kan vervolgens gewoon als label getoond worden. Geneste JMenu s Elke JMenu kan JMenu s en JMenuItem s bevatten. Hierdoor kan je menu s nesten. Het voorbeeldje zal een menu presenteren waarbij het menu tijdens de uitvoer groter en groter wordt. Steeds als je op maak bij klikt, zal er een optie hoeveel nog bijkomen. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing12 extends JApplet {private JMenu jm2; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JMenuBar j = new JMenuBar(); JMenu jm = new JMenu("een"); jm2= new JMenu("twee"); JMenu jm3= new JMenu("drie"); j.add(jm);j.add(jm3); jm.add(new JMenuItem("ha")); jm.add(jm2); jm2.add(new JMenuItem("boe")); jm2.add(new JButton("druk op mij")); jm2.add(new AbstractAction("maak bij") {public void actionperformed(actionevent e) {jm2.add(new JMenuItem("hoeveel nog")); ); c.add(j); Visuele Gebruikers Omgevingen: Swing 68 68/395

69 Een geneste JMenu als JMenuBar Nu zal het menu in de JMenuBar toegevoegd worden. Het voorbeeld heeft een optie maak bij die bij klikken, zichzelf als optie zal toevoegen. Dit komt doordat we rechtstreeks een object a (dat overerft van AbstractAction) toevoegen aan een JMenu. Bij klikken op a, wordt a toegevoegd aan het menu. Normaal ga je een menu s toevoegen aan de JMenuBar (die automatisch de contentpane zal verkleinen). public class Swing13 extends JApplet {private JMenu jm2; p rivate AbstractAction a; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JMenuBar j = new JMenuBar(); JMenu jm = new JMenu("een"); jm2= new JMenu("twee"); JMenu jm3= new JMenu("drie"); j.add(jm);j.add(jm3); jm.add(new JMenuItem("ha")); jm.add(jm2); jm2.add(new JSlider()); JProgressBar p =new JProgressBar(0,100); p.setvalue(60); jm2.add( p ); JPanel jp =new JPanel(); jp.add( new JButton("a")); jp.add( new JButton("b")); jm2.add( jp ); jm2.add(new JButton("druk op mij")); a=new AbstractAction("maak bij") {public void actionperformed(actionevent e) {jm2.add( a ); ; jm2.add(a); setjmenubar(j); Visuele Gebruikers Omgevingen: Swing 69 69/395

70 JSplitPane public class Swing14 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JSplitPane l= new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JSlider(), new JButton("druk") ); c.add(l); l.setdividerlocation(0.4); Visuele Gebruikers Omgevingen: Swing 70 70/395

71 Een progressbar gekoppeld met een timer public class Swing11 extends JApplet implements ActionListener {private int waarde=0; private JProgressBar p; pu blic void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); p= new JProgressBar(0,100); c.add(p); p.setvalue( 0); Timer tim=new Ti mer(100,this); tim.start(); public void actionperformed(actionevent e) { waarde++; p.setvalue(waarde); Een Timer is een handige klasse om iets op een regelmatig tijdstip te doen uitvoeren. In dit voorbeeld hebben we een timer die om de 100 milli seconden zijn listeners zal verwittigen. Langs de constructor wordt al this doorgegeven om aan te geven dat de Swing11 klasse zelf de interface ActionListener zal implementeren door een routine actionperformed op te geven. Het is die routine die de Timer zal oproepen om de 100 milliseconden. In dit voorbeeld zullen we dan de stand van de progressbar aanpassen met behulp van setvalue. Visuele Gebruikers Omgevingen: Swing 71 71/395

72 Een popup menu JpopupMenu erft over van JMenu. Je kan er dus hetzelfde mee doen, maar meer. Met behulp van show(component,x,y) kan je het popupmenu laten verschijnen op positie (x,y) ten opzichte van de component die aan show wordt doorgegeven. Je kan natuurlijk acties koppelen aan de JMenuItems. import javax.swing.*; import java.awt.*; import javax.swing.border.*; public class SwingPopup extends JFrame {JPopupMenu popup = new JPopupMenu(); JList list=new JList(new String[]{"a","b","c"); public SwingPopup() {popup.add(new JMenuItem("een")); popup.add(new JMenuItem("twee")); Container c=getcontentpane(); c.add(list); setsize(200,180); setdefaultcloseoperation(jframe.exit_on_close); setvisible(true); popup.show(list,20,20); public static void main(string args[]) {new SwingPopup(); Het popup menu zal op positie 20,20 ten opzichte van de JList getoond worden. Natuurlijk zal je normaal gezien het popup menuutje laten zien als reactie op een muisklik van de gebruiker. Hiervoor moet je met een muisluisteraar werken. JEditorPane en JTextArea JEditorPane en JTextArea lijken sterk op elkaar, maar een JEditorPane kan naast gewone tekst, ook html, rtf tonen. Je moet dan met de constructor het type meegeven: text/plain, text/html, text/rtf. Hiermee zou je een eenvoudige web-browser kunnen maken. Visuele Gebruikers Omgevingen: Swing 72 72/395

73 public class Swing15 extends JApplet { public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JEditorPane t= new JEditorPane(); t.settext("qsfqsfsqdfsdfsqfsfsqsdf"+ "qsfqsfqsfqsdfsdfssssssfs"+ "qsfqsfsqfqsffqsdsqddf"); t.setsize(300,50); JScrollPane ss=new JScrollPane(t); ss.setsize(200,50); c.add(ss); JTextArea ta =new JTextArea(15,5); JScrollPane s=new JScrollPane( ta ); c.add( s); JEditorPane voor html (Grant Palmer) Met behulp van setpage kan men de inhoud van een JEditorPane wijzigen. In het onderstaand voorbeeld wordt "file:///j:/i2_2001/help.html"; genomen (een lokale file), maar je mag evengoed een http adres opgeven van een internet pagina. Probeer maar eens een kleine webbrowser te maken. Als je gaat kijken in de API documentatie van JEditorPane, dan vindt je daar extra uitleg: Je kan namelijk een willekeurig html pagina met links openen en dedecteren wanneer de gebruiker op een link klikt. Je hebt daarvoor een hyperlinklistener nodig. <HTML> <BODY> <H1 ALIGN=CENTER>Help with JEditorPanes</H1> <BR><BR> <P>syntax: public class JEditorPane extends JTextComponent <BR><BR> A JEditorPane object is capable of displaying html and rtf format files. It is often used as a way to display on-line help information for applications. <BR><BR> A JEditorPane object needs an EditorKit object associated with it to provide the JEditorPane object with information on how to display a particular format. </BODY> </HTML> Visuele Gebruikers Omgevingen: Swing 73 73/395

74 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; public class TestJEP extends JFrame { JEditorPane jep = null; JButton helpbutton; JDialog helpdialog; public TestJEP() { helpdialog = new JDialog(this, "Help"); helpbutton = new JButton("Help"); helpbutton.addactionlistener(new HelpButtonHandler()); JPanel p = new JPanel(); p.add(helpbutton); getcontentpane().add(p, BorderLayout.SOUTH); setdefaultcloseoperation(jframe.exit_on_close); addwindowlistener(new WinClosing()); setbounds(100, 100, 400, 400); setvisible(true); // When the "Help" button is pressed, the actionperformed() method // is called and the JEditorPane() object is created and displayed. class HelpButtonHandler implements ActionListener { public void actionperformed(actionevent ae) { jep = new JEditorPane(); // The location of the HTML file will have to be changed to // correspond to its location on your system. String file = "file:///j:/i2_2001/help.html"; try { jep.setpage(file); catch (IOException ioe) { System.out.println(ioe); helpdialog.getcontentpane().add(new JScrollPane(jep)); helpdialog.setbounds(200, 200, 400, 300); helpdialog.show(); public static void main(string args[]) { TestJEP tjep = new TestJEP(); Visuele Gebruikers Omgevingen: Swing 74 74/395

75 JTree Een JTree is één van de ingewikkeldste onderwerpen van Java. We geven gewoon zeer eenvoudige vormen ervan: Hierdoor weet je wat een JTree is. public class Swing9 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JTree l= new JTree(); c.add(l); JTree l2= new JTree(new String[]{"een","twee","drie"); c.add(l2); Als je gewoon een object maakt van de klasse JTree, zal een standaard test JTree worden getoond. Je kan een JTree ook initialiseren met behulp van een array van strings. Andere mogelijkheden komen later. Een eenvoudige JTable import javax.swing.*; import javax.swing.table.*; import java.awt.*; import java.awt.event.*; public class SwingJTable extends JFrame { JTable jt; public SwingJTable() { Object[][] data = { {"Jackson", new Integer(4), "March 21", { "Zachary", new Integer(2), "May 12" ; String[] headers = { "Name", "Age", "Birthday" ; jt = new JTable(data,headers); jt.setgridcolor(color.red); jt.setrowselectionallowed(true); jt.setrowselectioninterval(1, 1); getcontentpane().add(jt, BorderLayout.CENTER); addwindowlistener(new WinClosing()); setbounds(100, 100, 300, 200); setvisible(true); Visuele Gebruikers Omgevingen: Swing 75 75/395

76 try {Thread.sleep(10000);catch(Exception e){ data[0][0]="gewijzigd"; repaint(); public static void main(string[] args) { SwingJTable tjt = new SwingJTable(); class WinClosing extends WindowAdapter { public void windowclosing(windowevent we) { System.exit(0); In bovenstaand programma initializeren we een JTable met behulp van een matrix van objecten. Voor elk van deze objecten zal de tostring functie worden opgeroepen. U kan ook elementen wijzigen. We veranderen bijvoorbeeld de inhoud van data[0][0]. Je moet dan wel een repaint doen opdat de nieuwe waarde automatisch hertekend wordt. Hetzelfde kan je ook bereiken met: jt.setvalueat("gewijzigd",0,0); In dit geval is er geen repaint nodig. JTable Je kan tabellen ook initializeren met behulp van een vector die alle rijen bevat. Elk element van deze vector (een rij) is zelf terug een vector die alle waarden in die rij bevat. Dit is een voor de hand liggende constructor. Zie hiervoor de API documentatie bij JTable. public class Swing10 extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); TableModel datamodel = new AbstractTableModel() { public int getcolumncount() { return 5; public int getrowcount() { return 10; public Object getvalueat(int row, int col) { return new Integer(row*col); ; JTable table = new JTable(dataModel); //JScrollPane scrollpane = new JScrollPane(table); c.add(table); Tabellen en ook JList kunnen niet alleen met een opsomming van concrete waarden geïnitializeerd worden, maar ook met een Abstract Model. Dit is een klasse die de waarden zal genereren. Bij een AbstractTableModel is er een routine getvalueat(row, col) die u moet opgeven en die voor een bepaalde row,col een object moet teruggeven dat dan in de tabel op die rij en kolom zal verschijnen. Elk element kan dusdanig berekend worden. Visuele Gebruikers Omgevingen: Swing 76 76/395

77 JTable (Grant Palmer) Bij lijsten konden we twee soorten lijsten maken : de eerste soort werd geïnitialiseerd met behulp van een array van strings. Dit type lijst kon tijdens de uitvoering niet meer uitgebreid worden. De tweede type lijst werkte met een DefaultListModel. Het voordeel hiervan was dat deze lijst tijdens de uitvoering van het programma nog uitgebreid kon worden. Zo ook bij tabellen. Wil je werken met tabellen waar je achteraf nog rijen aan kunt toevoegen, dan moet je werken met een DefaultTableModel. import javax.swing.*; import javax.swing.table.*; import java.awt.*; import java.awt.event.*; public class TestJTable extends JFrame implements ActionListener { JTable jt; DefaultTableModel dtm; JLabel namelabel, agelabel, birthdaylabel; JTextField nametf, agetf, birthdaytf; JButton addbutton; Visuele Gebruikers Omgevingen: Swing 77 77/395

78 public TestJTable() { Object[][] data = { { "Jackson", new Integer(4), "March 21", { "Zachary", new Integer(2), "May 12" ; String[] headers = { "Name", "Age", "Birthday" ; // A DefaultTableModel object is created and initialized with some // data and headers. dtm = new DefaultTableModel(2, 3); dtm.setdatavector(data, headers); // A JTable object is created using the DefaultTableModel object // as its data model. jt = new JTable(dtm); jt.setgridcolor(color.red); jt.setrowselectionallowed(true); jt.setrowselectioninterval(1, 1); // Some GUI components are added that are used to add rows to // the JTable. namelabel = new JLabel("Name: "); namelabel.setforeground(color.black); agelabel = new JLabel(" Age: "); agelabel.setforeground(color.black); birthdaylabel = new JLabel(" Birthday: "); birthdaylabel.setforeground(color.black); nametf = new JTextField(10); agetf = new JTextField(3); birthdaytf = new JTextField(12); addbutton = new JButton("add"); addbutton.addactionlistener(this); JPanel p = new JPanel(); p.add(namelabel); p.add(nametf); p.add(agelabel); p.add(agetf); p.add(birthdaylabel); p.add(birthdaytf); p.add(addbutton); // The JTable is placed inside a JScrollPane before it is placed // within the JFrame JPanel pc = new JPanel(); pc.add(new JScrollPane(jt, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)); getcontentpane().add(pc, BorderLayout.CENTER); getcontentpane().add(p, BorderLayout.SOUTH); addwindowlistener(new WinClosing()); setbounds(100, 100, 700, 500); setvisible(true); // whenever the "add" JButton is pressed, the actionperformed() // method is called and a new row is added to the JTable public void actionperformed(actionevent ae) { String name = nametf.gettext(); Integer age = new Integer(ageTF.getText()); String birthday = birthdaytf.gettext(); Object[] newdata = { name, age, birthday ; dtm.addrow(newdata); public static void main(string[] args) { TestJTable tjt = new TestJTable(); // The WinClosing class terminates the program when the window is closed class WinClosing extends WindowAdapter { Visuele Gebruikers Omgevingen: Swing 78 78/395

79 public void windowclosing(windowevent we) { System.exit(0); Visuele Gebruikers Omgevingen: Swing 79 79/395

80 Listeners Laat ons even een eigen component maken. We maken een tekstveldje waarop je kan klikken, zodat het de focus krijgt en geel kleurt. Je kan dan letters intypen. De letters bewegen wel constant op en neer. Hiervoor zijn verschillende listeners nodig. Je moet er met de muis op kunnen klikken om het de focus te geven: MouseListener. Je moet lettes kunnen intypen: KeyListener (of KeyAdapter). Je moet ook kunnen dedecteren wanneer een veldje de focus verliest: FocusListener. Tenslotte wordt er met eentimer gewerkt die 5 maal per seconde zijn luisteraars zal verwittigen: ActionListener. import java.awt.*; import javax.swing.*; import java.awt.event.*; class mytextfield extends JPanel {boolean focus=false; Timer tim; char [] letter=new char[100]; int aantal=0,offset; public mytextfield() {setpreferredsize(new Dimension(100,30)); grabfocus(); addkeylistener(new KeyAdapter() {public void keytyped(keyevent e) {if(aantal<100){ letter[aantal]=e.getkeychar(); aantal++; repaint(); ); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent e) {focus=true; repaint(); grabfocus(); ); addfocuslistener(new FocusAdapter() {public void focuslost(focusevent e) {focus=false;repaint(); ); tim=new Timer(200,new ActionListener() {public void actionperformed(actionevent e) {repaint(); ); tim.start(); public void paint(graphics g) {super.paint(g); if(focus) g.setcolor(color.yellow); else g.setcolor(color.white); g.fillrect(1,1,100,30); g.setcolor(color.black); g.drawrect(1,1,100,30); for(int i=0;i<aantal;i++) { offset=(int)(math.random()*10); g.drawstring(""+letter[i],i*8,10+offset); Als je met de muis op het tekstveldje klikt, gaan we de veranderlijke focus op true zetten. Door de repaint zal paint worden opgeroepen waardoor de achtergrond geel wordt. Let vooral op de grabfocus oproep. Deze is nodig omdat we overerven van een JPanel. Een JPanel kan nooit de focus krijgen. (Je kan een panel normaal niet met de muis selecteren;). Dit is geen probleem omdat we zelf muiskliks opvangen en expliciet met behulp van grabfocus de focus geven aan onze JPanel. Zonder focus worden de ingetypte letters niet opgevangen. We moeten dan nog wel dedecteren wanneer een andere JPanel de focus krijgt. Dit kunnen we doen met behulp van een FocusAdapter. We zetten dan focus op false en hertekenen. Als we letters intypen, zal onze keylistener deze letters in een array plaatsen en vervolgens hertekenen met behulp van repaint. Onze timer gaat gewoon om de 200 milliseconden hertekenen. In de paint gaan we met behulp van een random functie een offset per letter bepalen. We gaan aldus letter per letter op een andere plaats tekenen. Visuele Gebruikers Omgevingen: Swing 80 80/395

81 In de volgende toepassing gebruiken we gewoon 4 objecten van onze eigen tekstveldje. public class SwingKeyListener extends JFrame {Container c; public SwingKeyListener() {setbounds(10,10,250,180); setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(new mytextfield()); c.add(new mytextfield()); c.add(new mytextfield()); c.add(new mytextfield()); setvisible(true); public static void main(string args[]){new SwingKeyListener(); KeyListeners Om op key s te reageren hebben we de eenvoudige setmnemonic waarmee je eenvoudig een alt toets kunt verbinden met een JButton, JRadioButton, JCheckBox. Je kan ook met ActionMap s en InputMap s acties verbinden aan toetsaanslagen. Als je algemener zelf alle toetsaanslagen voor een component wilt dedecteren (je wilt bijvoorbeeld een kleine editor zelf maken), dan moet je met KeyListeners werken. KeyListener: keypressed, keytyped, keyreleased y oorspronkelijk stond er 'y', door bij het maken van schermafdruk moet ik de 'alt' toets indrukken. Maar er stond een y In volgende toepassing kan je de kleur van de panel veranderen in geel of groen door de toetsen y of g in te drukken. Deze zijn verbonden met een KeyListener die bij de applet zelf hoort. Het tekstveldje heeft ook een keylistener die op de r of b zal reageren en hierdoor de kleur van het paneeltje in rood of blauw veranderen. Het tekstveldje zal echter alleen maar key events opvangen indien het de focus heeft. (Daarom staat er in de toepassing ook een JButton druk : door hier op te klikken kunnen we expliciet de focus aan het tekstveldje ontnemen en toekennen aan de JButton). t t Als we vervolgens het tekstveldje de focus geven door er een 't' in te typen, dan krijgt de applet ook nog eens de keypressed en keyreleased van de t door. (Dit komt omdat het tekstveldje een deeltje van de applet is en klikken op het tekstveldje is ook klikken op het venster. Merk op dat keytyped event vanuit het tekstveldje niet wordt doorgegeven naar de applet omdat het tekstveldje deze zelf opvangt. Visuele Gebruikers Omgevingen: Swing 81 81/395

82 We hebben drie event s: keypressed (u drukt in), keyreleased (u laat los), keytyped (de samenvatting). Een hoofdletter A zal 5 events veroorzaken: keypressed shift, keypressed a, keyreleased shift, keyreleased a, keytyped A. Voor het JTextField vangen we alleen de keypressed op. De keytyped worden al opgevangen door het tekstveldje zelf. public class SwingH extends JApplet { private JLabel l,l2,l3,l4; private JTextField t; private JPanel p; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel(); c.add(l); l2=new JLabel(); c.add(l2); l3=new JLabel(); c.add(l3); l4=new JLabel(); c.add(l4); p=new JPanel(); p.setpreferredsize(new Dimension(50,50));c.add(p); c.add(new JButton("druk")); t=new JTextField(10); c.add(t); //twee listeners t.addkeylistener( new KeyAdapter() {public void keypressed(keyevent e) {l.settext("textfield keypressed :"+e.getkeychar()); if(e.getkeycode()==keyevent.vk_r) p.setbackground(color.red); if(e.getkeycode()==keyevent.vk_b) p.setbackground(color.blue); ); this.addkeylistener( new KeyListener() {public void keytyped(keyevent e) {l2.settext("applet keytyped :"+e.getkeychar()); public void keypressed(keyevent e) {if(e.getkeycode()==keyevent.vk_f1 ) JOptionPane.showMessageDialog(SwingH.this,"F1"); l3.settext("applet keypressed :"+e.getkeychar()); public void keyreleased(keyevent e) {l4.settext("applet keyreleased :"+e.getkeychar()); if(e.getkeychar()=='y') p.setbackground(color.yellow); if(e.getkeychar()=='g') p.setbackground(color.green); ); this.requestfocus(); Voor de applet gaan we alle keyevent opvangen. Hiervoor gaan we een KeyListener aan this verbinden. We vangen alle keytyped, keypressed, keyreleased events op. U merkt dat u zelfs onderscheid kunt maken tussen het indrukken en het loslaten van de toets. Stel dat u een tekenprogramma maakt en dat de lijn die u tekent in het rood moet verschijnen wanneer u de r ingedrukt houdt, dan is deze optie handig. Probeer dit maar eens als opdracht: een tekenprogramma waarbij u tijdens het tekenen de kleur van de lijnen met toetsen kunt kiezen, en dit zonder de muis los te laten tijdens het tekenen. De ingetypte letters kunnen we op twee wijzen opvragen: getkeycode en getkeychar. De getkeycode is algemener en hiermee kan je ook speciale toetsen zoals escape, pijltje en zo opvangen. Ben je alleen in letters geïnteresseerd, dan moet je met getkeychar werken. Visuele Gebruikers Omgevingen: Swing 82 82/395

83 ActionListener Naast setaction kan je ook met ActionListener s werken voor JButton. Hierdoor kunnen meerdere geïnteresseerde klassen zich melden als luisteraar van de button. public class Swing2 extends JApplet { String str="eerstebutton";int tel=0; JButton l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l= new JButton(str); l.addactionlistener( new ActionListener() {public void actionperformed(actionevent e) {str=str+(tel++)+" "; l.settext(str); Swing2.this.setSize(150+tel*2,70+tel*2); if(tel<5) l.doclick(); ); c.add(l); Elke maal als we klikken zal een teller verhogen en zal de grootte van het venster aangepast worden. We kunnen hiervoor met Swing2.this.setSize werken. (ti dit wijst naar de innerklasse die overerft van ActionL door 1x te klikken, wordt er automatisch 5x geklikt op de button.(programmatorisch) his is niet voldoende daar stener). Listeners voor JSlider en JList public class Swing3 extends JApplet { JLabel lab=new JLabel(); DefaultListModel v=new DefaultListModel(); JList lis=new JList(v); // MODEL CONTROL VIEW JSlider l= new JSlider(); Visuele Gebruikers Omgevingen: Swing 83 83/395

84 public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(lab); c.add(l); v.addelement(" een"); // de JList zelf bevat GEEN elementen v.addelement("twee");// maar wordt verwittigd (is observer) v.addelement("drie");// l.addchangelistener(new ChangeListener() {public void statechanged(changeevent e) { lab.settext(""+swing3.this.l.getvalue() ); ); c.add(new JScrollPane(lis)); lis.setvisiblerowcount(10); lis.addlistselectionlistener(new ListSelectionListener() {public void valuechanged(li stselectionevent e) {//JOptionPane.showMessageDialog(null,"selected"); v.addelement(""+lis.getselectedvalue()+ "_"+lis.getselectedindex() ); ); ListModel bigdata = new AbstractListModel() { public int getsize() { return 100; p ublic Object getelementat(int index) { return "Index " + index; ; JList bigdatalist = new JList(bigData); c.add(new JScrollPane(bigDataList)); Een ChangeListener zal reageren op het verschuiven van een JSlider. Een ListSelectionListener zal reageren op het selecteren uit een JList. (In bovenstaand geval zullen er steeds nieuwe waarden aan de lijst worden toegevoegd: bij selecteren uit de lijst, wordt hij langer en langer). Leuk is de bigdatalist. Hiervoor worden geen concrete waarden opgesomd. Je kan hiermee evengoed een lijst van elementen maken. Je werkt hiervoor met een AbstractListModel die een methode getelementat(index) heeft en waarmee waarden kunnen gegenereerd worden.(berekend worden). Menu samen met JInternalFrame Het komt dikwijls voor dat je met internalframes werkt en dat je met behulp van een menu acties wilt ondernemen die voor het internal frame dat de focus heeft. U weet dat u met een JDesktopPane moet werken als u internal frames wilt gebruiken. De methode getselectedframe hebben we hiervoor nodig: het bevat een referentie naar het frame dat de focus heeft. Visuele Gebruikers Omgevingen: Swing 84 84/395

85 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.beans.*; public class Swing5 extends JFrame {JMenuBar bar=new JMenuBar(); JMenu men=new JMenu("opties"); JMenuItem item=new JMenuItem("sluit actief subvenster"); JMenuItem item2=new JMenuItem("plaats venster links boven"); public Swing5() {Container c=getcontentpane(); final JDesktopPane thedesktop = new JDesktopPane(); //thedesktop.setlayout(new FlowLayout()); JInternalFrame cb= new JInternalFrame("stip mij aan",true,true,true,true); thedesktop.add(cb);cb.setvisible(true); cb.setbounds(10,10,120,40); JInternalFrame cb2= new JInternalFrame("stip mij aan2",true); //cb2 is vergrootbaar thedesktop.add(cb2);cb2.setvisible(true); cb2.setbounds(150,10,100,30); c.add(thedesktop); men.add(item);men.add(item2); bar.add(men); setjmenubar(bar); setbounds(1,1,3500,250); setvisible(true); setdefaultcloseoperation(jframe.exit_on_close); item.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {JInternalFrame frame=thedesktop.getselectedframe(); if(frame!=null) {frame.dispose(); thedesktop.repaint(); ); item2.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {JInternalFrame frame=thedesktop.getselectedframe(); frame.setlocation(10,10); thedesktop.repaint(); ); public static void main(string args[]) {new Swing5(); Visuele Gebruikers Omgevingen: Swing 85 85/395

86 JCombobox: ItemListener Als je selecteert uit een List, JList, JComboBox, zullen de geregistreerde ItemListeners verwittigd worden. public class Swing7 extends JApplet {JComboBox comb= new JComboBox(new String[]{"een","twee","drie"); JLabel lab=new JLabel(); JLabel lab2=new JLabel(); oeg toe aan combobox"); JButton but=new JButton("v public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(lab); c.add(comb); c.add(lab2); comb.additemlistener(new ItemListener() {p ublic void itemstatechanged(itemevent ee) {lab2.settext("item "+ ee.getitem()); ); JPanel p =new JPanel(); p.add(new JLabel("ha")); p.add(but); p.add(new JSlider()); but.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { lab.settext("");lab2.settext(""); comb.additem("button "+e.getactioncommand()); ); JTabbedPane t=new JTabbedPane(); t.setsize(300,300); t.inserttab("1",null,new JLabel("11"),"111",0); t.addtab("een",p); t.addtab("twee",new JLabel("label2")); c.add(t); JScrollBar en AdjustmentListener (Grant Palmer) Als je een scrollbar verschuift, worden de geregistreerde adjustmentlisteners verwittigd doordat de routines adjustmentvaluechange wordt opgeroepen. import javax.swing.*; Visuele Gebruikers Omgevingen: Swing 86 86/395

87 import java.awt.*; import java.awt.event.*; public class TestJSB extends JFrame { JScrollBar jsb; JTextField jtf; int i, maxvalue, extentvalue; public TestJSB() { maxvalue = 100; extentvalue = 5; jsb = new JScrollBar(JScrollBar.VERTICAL, 1, extentvalue, 1, maxvalue + extentvalue); jsb.setpreferredsize(new Dimension(20, 85)); jsb.addadjustmentlistener(new JScrollBarHandler()); jtf = new JTextField(3); jtf.addactionlistener(new JTextFieldHandler()); i = maxvalue jsb.getvalue(); jtf.settext("" + i); JPanel p = new JPanel(); p.add(jtf); p.add(jsb); getcontentpane().add(p); setdefaultcloseoperation(jframe.exit_on_close); setbounds(100, 100, 300, 150); setvisible(true); class JScrollBarHandler implements AdjustmentListener { public void adjustmentvaluechanged(adjustmentevent ae) { i = maxvalue jsb.getvalue(); jtf.settext("" + i); class JTextFieldHandler implements ActionListener { public void actionperformed(actionevent ae) { i = maxvalue Integer.parseInt(jtf.getText()); jsb.setvalue(i); public static void main(string args[]) { JScrollBar zelf zetten TestJSB tjsb = new TestJSB(); Elke keer we op de button klikken, zal de scrollbar 10 verder gezet worden. public class Swing8 extends JApplet Visuele Gebruikers Omgevingen: Swing 87 87/395

88 {JButton but=new JButton("verhoog Scroller"); JScrollBar scrol= new JScrollBar(); int i=0; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(scrol); scrol.setpreferredsize(new Dimension(30,200)); JToolBar jt=new JToolBar(); jt.add(but); jt.add(new JButton("twee")); but.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {scrol.setvalue(i+=10);if(i>100)i=0; ); c.add(jt); WindowListener WindowListeners kunnen gebruikt worden voor reactie op verkleinen tot icoon, terug vergroten vanuit icoon, sluiten. De interface WindowListener heeft vele routines en omdat we meestal maar geïnteresseerd zijn in één (vb WindowClosing), zal men meestal overerven van WindowAdapter. Deze klasse voorziet lege versies voor alle routines uit de interface WindowListener. import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing16 extends JFrame { static public void main(string args[]) { new mijnframe("nog een ander venster"); class mijnframe extends JFrame {DefaultListModel lm =new DefaultListModel(); public mijnframe(string titel) { super(titel); Container c=this.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new JList(lm)); this.setsize( 120,50); this.setvisible(true); addwindowlistener(new WindowListener() { public void windowactivated(windowevent e) // Invoked when the window is set to be the user's active window, // which means the window (or one of its subcomponents) will receive keyboard //events. {lm.addelement("activated"); public void windowclosed(windowevent e) // Invoked when a window has been closed as the result of Visuele Gebruikers Omgevingen: Swing 88 88/395

89 // calling dispose on the window. {JOptionPane.showMessageDialog(null,"Closed"); public void windowclosing(windowevent e) // Invoked when the user attempts to close the window // from the window's system menu. {JOptionPane.showMessageDialog(null,"Closing"); public void windowdeactivated(windowevent e) //Invoked when a window is no longer the user's active window, which means //that keyboard events will no longer be delivered //to the window or its subcomponents. {lm.addelement("deactivated"); public void windowdeiconified(windowevent e) //Invoked when a window is changed from a minimized to a normal state. {lm.addelement("deiconified"); public void windowiconified(windowevent e) //Invoked when a window is changed from a normal to a minimized state. {lm.addelement("iconified"); public void windowopened(windowevent e) //Invoked the first time a window is made visible. {lm.addelement("windowopened"); ); Visuele Gebruikers Omgevingen: Swing 89 89/395

90 Listeners: dansende letters Laat ons even een eigen component maken. We maken een tekstveldje waarop je kan klikken, zodat het de focus krijgt en geel kleurt. Je kan dan letters intypen. De letters bewegen wel constant op en neer. Hiervoor zijn verschillende listeners nodig. Je moet er met de muis op kunnen klikken om het de focus te geven: MouseListener. Je moet lettes kunnen intypen: KeyListener (of KeyAdapter). Je moet ook kunnen dedecteren wanneer een veldje de focus verliest: FocusListener. Tenslotte wordt er met eentimer gewerkt die 5 maal per seconde zijn luisteraars zal verwittigen: ActionListener. import java.awt.*; import javax.swing.*; import java.awt.event.*; class mytextfield extends JPanel {boolean focus=false; Timer tim; char [] letter=new char[100]; int aantal=0,offset; public mytextfield() {setpreferredsize(new Dimension(100,30)); grabfocus(); addkeylistener(new KeyAdapter() {public void keytyped(keyevent e) {if(aantal<100){ letter[aantal]=e.getkeychar(); aantal++; repaint(); ); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent e) {focus=true; repaint(); grabfocus(); ); addfocuslistener(new FocusAdapter() {public void focuslost(focusevent e) {focus=false;repaint(); ); tim=new Timer(200,new ActionListener() {public void actionperformed(actionevent e) {repaint(); ); tim.start(); public void paint(graphics g) {super.paint(g); if(focus) g.setcolor(color.yellow); else g.setcolor(color.white); g.fillrect(1,1,100,30); g.setcolor(color.black); g.drawrect(1,1,100,30); for(int i=0;i<aantal;i++) { offset=(int)(math.random()*10); g.drawstring(""+letter[i],i*8,10+offset); Als je met de muis op het tekstveldje klikt, gaan we de veranderlijke focus op true zetten. Door de repaint zal paint worden opgeroepen waardoor de achtergrond geel wordt. Let vooral op de grabfocus oproep. Deze is nodig omdat we overerven van een JPanel. Een JPanel kan nooit de focus krijgen. (Je kan een panel normaal niet met de muis selecteren;). Dit is geen probleem omdat we zelf muiskliks opvangen en expliciet met behulp van grabfocus de focus geven aan onze JPanel. Zonder focus worden de ingetypte letters niet opgevangen. We moeten dan nog wel dedecteren wanneer een andere JPanel de focus krijgt. Dit kunnen we doen met behulp van een FocusAdapter. We zetten dan focus op false en hertekenen. Als we letters intypen, zal onze keylistener deze letters in een array plaatsen en vervolgens hertekenen met behulp van repaint. Onze timer gaat gewoon om de 200 milliseconden hertekenen. In de paint gaan we met behulp van een random functie een offset per letter bepalen. We gaan aldus letter per letter op een andere plaats tekenen. Visuele Gebruikers Omgevingen: Swing 90 90/395

91 In de volgende toepassing gebruiken we gewoon 4 objecten van onze eigen tekstveldje. public class SwingKeyListener extends JFrame {Container c; public SwingKeyListener() {setbounds(10,10,250,180); setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(new mytextfield()); c.add(new mytextfield()); c.add(new mytextfield()); c.add(new mytextfield()); setvisible(true); public static void main(string args[]){new SwingKeyListener(); Opdracht Maak een toepassing waarin je twee rechthoeken hebt met elk vier delen. U voorziet drie type luisteraars die verwittigd worden indien in één van de vier kwadranten wordt geklikt: - een staafjes diagram dat visueel weergeeft hoeveel maal in elk kwadrant werd geklikt - een rechthoek waarin gewoon 4 getallen komen - een rechthoek waarin de procentuele verhouding van het aantal kliks per kwadrant wordt weergegeven. - U kunt er nog bij verzinnen: een pie-chart,. Van elke luisteraar moeten twee objecten worden gemaakt die elk bij een andere eventgenerator worden geregistreerd. Voorbeeld in Java zelf van het Observer patroon Visuele Gebruikers Omgevingen: Swing 91 91/395

92 In het onderstaand programma moet de gebruiker niets doen, het programma 'beweegt' zelf. Timer gaan op regelmatige tijdsintervallen de luistenaars verwittigen door de functie actionperformed op te roepen. Dit programma heeft twee timers. De eerste zal om de seconde (1000 milliseconden) zijn luistenaars verwittigen. De eerste luisteraar wordt aan de constructor van de timer doorgegeven. Dit komt omdat een timer zonder luisteraar geen zin heeft. De andere luisteraars worden toegevoegd met behulp van addactionlistener. Alle luisteraars moeten de interface ActionListener implementeren. Door de eerste timer zal om de seconde de woorden tak en tok op het scherm verschijnen. De tweede timer zal om de 5 seconden zijn luisteraars verwittigen. Hierdoor wordt er op het scherm naar een nieuwe lijn gegaan en zal er een steeds grotere rechthoek op scherm worden getekend. /* leuk idee : maak een auto klasse. De auto moet zich over het scherm verplaatsen telkens hij door een timer wordt verwittigd. */ import javax.swing.timer; import java.awt.event.*; class Luistenaar implements ActionListener {public void actionperformed(actionevent e) {Scherm.schrijf("tok"); class Luistenaar2 implements ActionListener {public void actionperformed(actionevent e) {Scherm.schrijf("tak"); class Luistenaar3 implements ActionListener {int y=2; public void actionperformed(actionevent e) {Scherm.gotoxy(1,y++); class Generator implements ActionListener {public void run() {Scherm.clearDevice(); Timer tim=new Timer(1000, new Luistenaar() ); tim.start(); tim.addactionlistener( new Luistenaar2() ); Timer tim2=new Timer(5000, new Luistenaar3() ); tim2.addactionlistener(this); Visuele Gebruikers Omgevingen: Swing 92 In dit geval geven we 'this' door als parameter naar addactionlistener. Hierdoor geven we aan dat de 92/395

93 tim2.start(); public void actionperformed(actionevent e) {Scherm.rectangle(300-x,70-x,310+x,70+x);x+=2; private int x=1; public class Test {public static void main(string []arg){new Generator().run(); Visuele Gebruikers Omgevingen: Swing 93 93/395

94 Eigen dingen maken Een eigen border maken Hiervoor moet je gewoon een interface Border implementeren. Deze interface heeft drie methoden: Insets getborderinsets(component c) Returns the insets of the border. boolean isborderopaque() Returns whether or not the border is opaque. void paintborder(component c, Graphics g, int x, int y, int width, int height) Paints the border for the specified component with the specified position and size. Onze eigen boord bestaat uit twee blauwe lijnen opgevuld met rode lijnen en een kleine tekst: firma x. Vervolgens kunnen we deze boord overal gebruiken. import javax.swing.*; import javax.swing.border.*; import java.awt.event.*; import java.awt.*; Visuele Gebruikers Omgevingen: Swing 95 94/395

95 /*interface javax.swing.border Insets getborderinsets(component c) Returns the insets of the border. boolean isborderopaque() Returns whether or not the border is opaque. void paintborder(component c, Graphics g, int x, int y, int width, int height) Paints the border for the specified component with the specified position and size. */ class EigenBorder implements Border {public Insets getborderinsets(component c) {return new Insets(4,4,4,4); public boolean isborderopaque() {return false; public void paintborder(component c, Graphics g, int x, int y, int width, int height) { Color oldcolor = g.getcolor(); g.setcolor(color.blue); g.drawrect(x+0, y+0, width-0-0-1, height-0-0-1); g.setcolor(color.red); g.drawrect(x+1, y+1, width-1-1-1, height-1-1-1); g.drawrect(x+2, y+2, width-2-2-1, height-2-2-1); g.drawrect(x+3, y+3, width-3-3-1, height-3-3-1); g.drawrect(x+4, y+4, width-4-4-1, height-4-4-1); g.drawrect(x+5, y+5, width-5-5-1, height-5-5-1); g.setcolor(color.blue); g.drawrect(x+6, y+6, width-6-6-1, height-6-6-1); g.setcolor(color.black); g.setfont(new Font("Dialog",Font.ITALIC,8)); g.drawstring("firma x",7,7); g.setcolor(oldcolor); public class Test1 extends JFrame { public void Test1() { public static void main(string args[]) { Test1 mainframe = new Test1(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(null); JButton b,b2,b3; b=new JButton("test");b2=new JButton("test2"); b3=new JButton("test3"); b.setborder(new EigenBorder()); b.setbounds(10,30,70,30); b2.setborder(new EigenBorder()); b2.setbounds(100,30,100,30); Visuele Gebruikers Omgevingen: Swing 96 95/395

96 b3.setbounds(220,30,70,50); b3.setborder(new EigenBorder()); c.add(b);c.add(b2);c.add(b3); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); c.setbackground(color.cyan); mainframe.setvisible(true); public class Test1 extends JFrame { public void Test1() { public static void main(string args[]) { Test1 mainframe = new Test1(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(null); JButton b,b2,b3; b=new JButton("test"); b2=new JButton("test2"); b3=new JButton("test3"); b.setborder(new EigenBorder()); b.setbounds(10,30,70,30); b2.setborder(new EigenBorder()); b2.setbounds(100,30,70,30); b3.setbounds(200,30,70,30); b3.setborder(new EigenBorder()); c.add(b);c.add(b2);c.add(b3); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); c.setbackground(color.cyan); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing 97 96/395

97 Een JTextField die alleen maar integers toelaat We maken een klasse JIntegerField. Als de gebruiker iets intypt dat geen integer is, zal de achtergrond oranje kleuren. Als hij het terug verbeterd, zal de achtergrond terug wit worden. Hiervoor gaan we overerven van JTextField en in de constructor een keylistener toevoegen. Telkens de gebruiker iets ingetypt heeft, gaan we terug checken of het geheel wel een integer is. Indien niet gaan we de achtergrond oranje kleuren. We voegen ook een extra functie getgetal toe om de uiteindelijke waarde als een int op tek kunnen vragen. import javax.swing.*; import java.awt.event.*; import java.awt.*; class JIntegerTextField extends JTextField {public JIntegerTextField() {super(); addkeylistener(new KeyAdapter() { public void keyreleased(keyevent e) {setbackground(color.white); String tekst=gettext(); try{int i=integer.parseint(tekst); catch(exception ee) {if(!tekst.equals("")){setbackground(color.orange); ); public int getgetal() {int i; String tekst=gettext(); try{i=integer.parseint(tekst); catch(exception ee){i=0; return i; Visuele Gebruikers Omgevingen: Swing 98 97/395

98 public class IntegerTextField extends JFrame { static JIntegerTextField b,b2,b3; public void IntegerTextField() { public static void main(string args[]) { IntegerTextField mainframe = new IntegerTextField(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(new FlowLayout()); b =new JIntegerTextField(); b.setpreferredsize(new Dimension(70,30)); b2=new JIntegerTextField(); b2.setpreferredsize(new Dimension(70,30)); b3=new JIntegerTextField(); b3.setpreferredsize(new Dimension(70,30)); c.add(b);c.add(b2); JButton b4=new JButton("som"); b4.setpreferredsize(new Dimension(70,30)); c.add(b4);c.add(b3); b4.addactionlistener(new ActionListener() {public void actionperformed(actionevent a) {b3.settext( ""+(b.getgetal()+b2.getgetal()) ); ); mainframe.setsize(400, 400); mainframe.settitle("integer textfield"); mainframe.setvisible(true); Een InputVerifier Een JComponent heeft een methode setinputverifier. Hiermee kan een klasse die overerft van de abstracte klasse InputVerifier worden ingeplugd. Men moet hiervoor maar één methode herdefinieren: verify. Deze methode verify geeft true terug indien de input goed is. De methode verify krijgt de component waarvan de inhoud moet geverifieerd worden mee als argument. Visuele Gebruikers Omgevingen: Swing 99 98/395

99 Zolang de methode verify geen true teruggeeft, blijft het veld de focus houden. (Je kan de component gewoon niet verlaten en verder gaan met een andere component. (Tab werkt niet)). De vorige oplossing om de component echt anders te kleuren, was mooier. Met een InputVerifier kan je wel iets verkeerd intypen, je kan gewoon niet meer het veld verlaten. import javax.swing.*; import java.awt.event.*; import java.awt.*; class IntegerVerifier extends InputVerifier { public boolean verify(jcomponent input) { JTextField tf = (JTextField) input; String tekst=tf.gettext(); try{int i=integer.parseint(tekst); catch(exception ee){return false; return true; public class IntegerTextField2 extends JFrame { static JTextField b,b2,b3; Visuele Gebruikers Omgevingen: Swing /395

100 public void IntegerTextField2() { public static void main(string args[]) { IntegerTextField2 mainframe = new IntegerTextField2(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(new FlowLayout()); b =new JTextField();b.setPreferredSize(new Dimension(70,30)); b2=new JTextField();b2.setPreferredSize(new Dimension(70,30)); b3=new JTextField();b3.setPreferredSize(new Dimension(70,30)); b.setinputverifier(new IntegerVerifier()); b2.setinputverifier(new IntegerVerifier()); b3.setinputverifier(new IntegerVerifier()); c.add(b);c.add(b2);c.add(b3); mainframe.setsize(400, 400); mainframe.settitle("integer textfield"); mainframe.setvisible(true); Een eigen Button import java.awt.*; import javax.swing.*; class mybutton extends JButton { public mybutton() {setpreferredsize(new Dimension(100,100)); public void paint(graphics g) {super.paint(g); g.setcolor(color.yellow); g.filloval(50,50,20,20); public class SwingJButton extends JFrame {Container c; public SwingJButton() {setbounds(10,10,250,180); setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(new mybutton()); setvisible(true); public static void main(string args[]){new SwingJButton(); Visuele Gebruikers Omgevingen: Swing /395

101 De routine paint staat in voor het tekenen van de JButton. We geven hiervan een eigen versie, maar roepen voor zekerheid eerst de basis versie super.paint op. Als je dit niet doet, dan heb je gewoon geen rechthoek, dan moet je alles tekenen, en ook zorgen dat de boord anders wordt getekend indien je de button indrukt. Eigenlijk roept de paint ook een paintcomponent op die meer instaat voor het tekenen van het binnenste van de component. Je kan ook alleen paintcomponent herdefinieren. tweede versie class mybutton extends JButton { public mybutton() {setpreferredsize(new Dimension(100,100)); protected void paintcomponent(graphics g) {super.paintcomponent(g); g.setcolor(color.yellow); g.filloval(50,50,20,20); Eigen JPanel Als je gewoon een eigen tekenvlak wilt hebben, kan je best overerven van Canvas (awt) of JPanel (Swing). Dit zijn de eenvoudigste lege Componenten. Je kan voor een JPanel een eigen paint voorzien. Weet wel dat een JPanel ook een Container is, maar probeer best niet naast het opgeven van een eigen paint ook de container te gebruiken door eigen componenten hieraan toe te voegen. Wil je toch een toepassing met een eigen tekening en ook componenten, zet dan de eigen componenten in een andere gewone JPanel. class mijncanvas extends Canvas { Image logo1; public mijncanvas() {this.setsize(40,40); logo1= Toolkit.getDefaultToolkit().getImage("globe.gif"); public void paint(graphics g) {g.drawimage(logo1,0,0,this); g.drawline(0,0,40,40); g.drawline(0,40,40,0); class mijnpanel extends JPanel {public mijnpanel(){ setpreferredsize(new Dimension(100,50)); Visuele Gebruikers Omgevingen: Swing /395

102 public void paint(graphics g) { g.setcolor(color.blue); g.fillpolygon(new int[]{10,20,30,40, 50,60, 70,80, 90,100,1, new int[] {1,50,1,50,1,50,1,50,1,50,50,11); public class Swing20 extends JApplet { public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(new mijncanvas()); c.add(new mijnpanel()); c.add(new mijncanvas()); JPanel p=new JPanel(); p.add(new JButton("druk")); p.add(new mijnpanel()); p.add(new mijncanvas()); p.add(new JSlider()); c.add(p); In een gewone toepassing ga je een Image laden met behulp van Toolkit klasse. Toolkit.getDefaultToolkit geeft de Toolkit klasse voor uw besturingssysteem terug.een polygon is een veelhoek waarvan je gewoon de coordinaten van de hoekpunten moet opgeven. Uw eigen controls gebruiken Een Button die doorzichtig is: je kan de ondergrond erdoor zien: Hiervoor gaan we gewoon overerven van de klasse JButton en in de constructor de methode setopaque(false) oproepen. Op deze manier kunnen we onze eigen buttons, met eigen kleuren gebruiken. Het voordeel om eerst een eigen type button te maken is, dat je achteraf alle buttons van heel uw toepassing kunt aanpassen van look, gewoon door die eigen klasse wat aan te passen. import java.awt.event.*; import java.awt.*; import javax.swing.*; Visuele Gebruikers Omgevingen: Swing /395

103 class DoorzichtigButton extends JButton {public DoorzichtigButton(String titel) {super(titel); setopaque(false);// doorzichtig public class Test1 extends JFrame { public void Test1() { public static void main(string args[]) { Test1 mainframe = new Test1(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(null); JButton b,b2,b3; b=new DoorzichtigButton("test");b2=new DoorzichtigButton("test2"); b3=new DoorzichtigButton("test3"); b.setbounds(10,30,70,30); b2.setbounds(100,30,70,30); b3.setbounds(200,30,70,30); c.add(b);c.add(b2);c.add(b3); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); c.setbackground(color.cyan); mainframe.setvisible(true); Onze doorzichtige button geplaatst over een tekening Je merkt dat de button echt doorzichtig zijn: de onderliggende tekening komt erdoor. Visuele Gebruikers Omgevingen: Swing /395

104 import javax.swing.*; import java.awt.event.*; import java.awt.*; class DoorzichtigButton extends JButton {public DoorzichtigButton(String titel) {super(titel); setopaque(false);// doorzichtig class TekeningPanel extends JPanel{ private Image img; MediaTracker tracker; public TekeningPanel(Image img){ tracker = new MediaTracker(this); this.img=img; tracker.addimage(img,0); try {// tekening wordt normaal alleen geladen als het echt getekend wordt. // met waitforall wachten totdat hij echt geladen is //Start downloading the images. Wait until they're loaded. tracker.waitforall(); catch (InterruptedException e) { Visuele Gebruikers Omgevingen: Swing /395

105 public void paintcomponent(graphics g){ super.paintcomponent(g); g.drawimage(img,0,0,this); g.drawstring("copywright",0,15); public class Test2 extends JFrame { public void Test2() { public static void main(string args[]) { Test2 mainframe = new Test2(); mainframe.setdefaultcloseoperation(jframe.exit_on_close); Container c=mainframe.getcontentpane(); c.setlayout(null); JButton b,b2,b3; b=new DoorzichtigButton("test");b2=new DoorzichtigButton("test2"); b3=new DoorzichtigButton("test3"); b.setbounds(10,30,70,30); b2.setbounds(100,30,70,30); b3.setbounds(200,30,70,30); c.add(b);c.add(b2);c.add(b3); Toolkit tk=toolkit.getdefaulttoolkit(); Image img=tk.getimage("water.gif"); JPanel tek1=new TekeningPanel(img); tek1.setbounds(10,10,200,200); c.add(tek1); mainframe.setsize(400, 400); mainframe.settitle("eigen_layout"); c.setbackground(color.cyan); mainframe.setvisible(true); //!!!!even wachten en alles hertekenen is nodig als we geen mediatracker //gebruiken!!!!!!!!! //try{thread.sleep(1000); catch(exception e){ //c.repaint(); We hebben een eigen klasse TekeningPanel gemaakt. Dit is een panel dat een tekening bevat. Het probleem met tekeningen tonen zit erin dat ze alleen maar van schijf geladen worden op het moment dat ze echt getekend worden. In dit voorbeeld tekenen we worden de button BOVEN de tekening getekend als alles geladen is. De eerste keer echter werden de buttons ACHTER de tekening getoond omdat de tekening nog eerst van schijf moest gehaald worden. Dit komt omdat het programma niet blijft wachten totdat de tekening geladen is: dit gebeurt in een achtergrond proces. Dit probleem werd opgevangen met een zogenaamde MediaTracker. Met het bevel tracker.waitforall stopt het programma totdat het achtergrond proces de tekening(en) echt van schijf gehaald heeft. Dan pas wordt er verder gegaan en echt getekend. tracker = new MediaTracker(this); Visuele Gebruikers Omgevingen: Swing /395

106 this.img=img; tracker.addimage(img,0); try { tracker.waitforall();// wachten geblazen catch (InterruptedException e) { JList is flexible ontworpen Een eigen ListCellRenderer voor een JList Hoe dat een lijst reageert op het selecteren van een element kan eenvoudig worden aangepast door een object van een klasse die de interface ListCellRenderer implementeert, in te pluggen. Het is een heel eenvoudige interface met maar één methode getlistcellrenderercomponent. Dit is een methode die een component (zoals een JLabel, JTextField, een JPanel,..) teruggeeft. Deze methode wordt door de JList opgeroepen voor elk element van de lijst. Voor elk van deze elementen wordt meegegeven als parameter : - de referentie naar de lijst zelf (list) - het element dat moet getekend worden (value) - de plaats die het element in de lijst inneemt (index) - of dit element al dan niet geselecteerd is (isselected) - of de lijst de focus heeft (cellhasfocus). In onze versie van ListCellRenderer zal elk geselecteerd element een zwarte achtergrond krijgen met witte letters. De andere elementen krijgen zwarte letters op een witte achtergrond. Niets houdt u tegen om deze kleur te laten afhangen van de plaats die het element in de lijst inneemt. import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; Visuele Gebruikers Omgevingen: Swing /395

107 class MyCellRenderer implements ListCellRenderer { public Component getlistcellrenderercomponent( JList list, Object value, // value to display int index, // cell index boolean isselected, // is the cell selected boolean cellhasfocus) // the list and the cell have the focus { ((Component)value).setBackground(isSelected?Color.black:Color.white); ((Component)value).setForeground(isSelected?Color.white:Color.darkGray); return (Component)value; class EigenBorder implements Border {public Insets getborderinsets(component c) {return new Insets(4,4,4,4); public boolean isborderopaque() {return false; public void paintborder(component c, Graphics g, int x, int y, int width, int height) { Color oldcolor = g.getcolor(); g.setcolor(color.blue); g.drawrect(x+0, y+0, width-0-0-1, height-0-0-1); g.setcolor(color.red); g.drawrect(x+1, y+1, width-1-1-1, height-1-1-1); g.drawrect(x+2, y+2, width-2-2-1, height-2-2-1); g.drawrect(x+3, y+3, width-3-3-1, height-3-3-1); g.drawrect(x+4, y+4, width-4-4-1, height-4-4-1); Visuele Gebruikers Omgevingen: Swing /395

108 g.drawrect(x+5, y+5, width-5-5-1, height-5-5-1); g.setcolor(color.blue); g.drawrect(x+6, y+6, width-6-6-1, height-6-6-1); g.setcolor(color.black); g.setfont(new Font("Dialog",Font.ITALIC,8)); g.drawstring("firma x",7,7); g.setcolor(oldcolor); class Test1 extends JFrame { public Test1() { setdefaultcloseoperation(jframe.exit_on_close); Container c=getcontentpane(); c.setlayout(new FlowLayout()); Object []twee=new Object[5]; twee[0]=new JButton("tof"); JButton b=new JButton("test"); b.setborder(new EigenBorder()); twee[1]=b; twee[2]=new JButton("tweede button"); twee[3]=new JTextField("vierde lijn"); twee[4]=new JTextField("vijfde lijn"); JList l=new JList(twee); l.setcellrenderer(new MyCellRenderer()); c.add(l); public static void main(string args[]) { System.out.println("Starting Test..."); Test1 mainframe = new Test1(); mainframe.setsize(400, 400); mainframe.settitle("test"); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

109 Een ListModel die de gegevens uit een file gaat lezen en terug naar wegschrijven bij beëindigen. Inhoud namen.txt: Bij een JList zijn de gegevens al gescheiden van de visuele component door middel van een interface ListModel. We gaan overerven van de klasse DefaultListModel die deze interface implementeert en hierdoor een eigen FileListModel gaan maken die in zijn constructor heel de file gaat lezen en doormiddel van het overgeërfde bevel addelement gaat toevoegen aan het ListModel. We voorzien ook een methode savelistmodeltofile die we kunnen oproepen indien het programma sluit om zo heel de inhoud van de basis klasse DefaultListModel terug naar een file te schrijven. (Hiervoor maken we gebruik van de overgeërfde methode getsize en getelementat(i) van de klasse DefaultListModel). inhoud bestand namen2.txt Visuele Gebruikers Omgevingen: Swing /395

110 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; class FileListModel extends DefaultListModel {FileReader fr; BufferedReader br; String line; FileWriter fw; BufferedWriter bw; public FileListModel(String naamfile) {try{ fr=new FileReader(naamfile); br=new BufferedReader(fr); line=br.readline(); while(line!=null) { super.addelement(line); line=br.readline(); catch(exception ee){ public void savelistmodeltofile(string naamfile) {try { fw=new FileWriter(naamfile); bw=new BufferedWriter(fw); int aantal=super.getsize(); for(int i=0;i<aantal;i++) {line=(string)super.getelementat(i); bw.write(line,0,line.length()); bw.newline(); bw.close(); catch(exception e){ class Test2 extends JFrame { static FileListModel flm; public Test2() { addwindowlistener( new WindowAdapter() {public void windowclosing(windowevent ww) {flm.savelistmodeltofile("namen2.txt"); dispose(); ); Container c=getcontentpane(); Visuele Gebruikers Omgevingen: Swing /395

111 c.setlayout(new FlowLayout()); flm = new FileListModel("namen.txt"); JList l=new JList(flm); c.add(l); flm.addelement("extra tekst"); flm.addelement("extra tekst2"); public static void main(string args[]) { System.out.println("Starting Test..."); Test2 mainframe = new Test2(); mainframe.setsize(400, 400); mainframe.settitle("test"); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

112 Een JList opgevuld vanuit een sql databank - Maak eerst een database db1.mdb - Zet hierin een tabel personen met een naam voornaam leeftijd - Je moet deze databank als odbc databank registreren. Ofwel moet je hiervoor naar het control panel gaan naar odbc gegevens. Als je dit niet vindt onder control panel, moet je gewoon onder de help maar zoeken achter odbc en je krijgt een link ernaar: Je moet hier onze databank registreren onder een logische odbc naam. We hebben dit gedaan met test. Je moet hiervoor op add klikken. Vervolgens test intypen en de databank db1.mdb hiermee verbinden. Hier zie je twee lijsten die opgevuld werden vanuit een databank. De sql query was select * from personen. In dat geval wordt de eerste kolom van het antwoord getooond. import javax.swing.*; import java.awt.*; Visuele Gebruikers Omgevingen: Swing /395

113 import java.awt.event.*; import java.io.*; import java.sql.*; class SqlListModel extends DefaultListModel {String line; Connection conn; Statement statement; ResultSet rs; public SqlListModel(String driver,string databank,string sqlquery) {try{ Class.forName(driver).newInstance(); conn = DriverManager.getConnection(databank); statement = conn.createstatement(); rs = statement.executequery(sqlquery); while (rs.next()) { super.addelement( rs.getstring(1) ); if (statement!= null) statement.close(); if (conn!= null) conn.close(); catch (Exception e) {addelement("fout"+e); We erven gewoon over van DefaultListModel en reopen de functie super.addelement op voor elke rij van de ResultSet. Hierdoor wordt elke rij van de resultset toegevoegd aan de basisklasse DefaultListModel. class JDatabaseList extends JFrame { static SqlListModel flm; public JDatabaseList() { setdefaultcloseoperation(jframe.exit_on_close); Container c=getcontentpane(); c.setlayout(new FlowLayout()); flm = new SqlListModel("sun.jdbc.odbc.JdbcOdbcDriver", "jdbc:odbc:test", "select * from personen"); JList l=new JList(flm); JList cb=new JList(flm); c.add(l);c.add(new JScrollPane(cb)); flm.addelement("extra tekst"); flm.addelement("extra tekst2"); public static void main(string args[]) { System.out.println("Starting Test..."); Visuele Gebruikers Omgevingen: Swing /395

114 JDatabaseList mainframe = new JDatabaseList(); mainframe.setsize(400, 400); mainframe.settitle("test"); mainframe.setvisible(true); Ook comboboxen verbinden met een databank Omdat we ook een klasse DefaultComboBoxModel moeten aanpassen om in te pluggen in een JComboBox en deze aanpassing zeer analoog is aan de aanpassing gedaan voor DefaultListModel, zullen we een hulp klasse maken SqlModel die we zullen gebruiken om de gemeenschappelijke code (namelijk het accessen van de databank) in te plaatsen. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.sql.*; class SqlModel { String line; Connection conn; Statement statement; ResultSet rs; String fout=null; public SqlModel(String driver,string databank,string sqlquery) {try{ Class.forName(driver).newInstance(); conn = DriverManager.getConnection(databank); statement = conn.createstatement(); rs = statement.executequery(sqlquery); Visuele Gebruikers Omgevingen: Swing /395

115 catch (Exception e) {fout=e.tostring(); public String volgendeelement() {if(fout!=null) {String copy=new String(fout); fout=null; try{if(rs.next()) return rs.getstring(1) ; catch(exception e){return e.tostring(); return null; SqlModel is een eigen klasse waarmee we de rijen van het resultaat van een sqlquery kunnen aflopen met behulp van de methode volgendeelement. We moeten hiervoor gewoon volgendeelement oproepen in een lusje totdat deze methode null teruggeeft. class SqlListModel extends DefaultListModel {SqlModel sqlmodel; public SqlListModel(String driver,string databank,string sqlquery) {sqlmodel=new SqlModel(driver,databank,sqlquery); String element=sqlmodel.volgendeelement(); while (element!=null) { super.addelement( element ); element=sqlmodel.volgendeelement(); class SqlComboBoxModel extends DefaultComboBoxModel {SqlModel sqlmodel; public SqlComboBoxModel(String driver,string databank,string sqlquery) {sqlmodel=new SqlModel(driver,databank,sqlquery); String element=sqlmodel.volgendeelement(); while (element!=null) { super.addelement( element ); element=sqlmodel.volgendeelement(); De klassen SqlListModel en SqlComboBoxModel worden hierdoor veel eenvoudiger. class JDatabaseList2 extends JFrame { static SqlListModel flm,flm2; static SqlComboBoxModel cbm; public JDatabaseList2() { setdefaultcloseoperation(jframe.exit_on_close); Container c=getcontentpane(); c.setlayout(new FlowLayout()); flm = new SqlListModel("sun.jdbc.odbc.JdbcOdbcDriver", "jdbc:odbc:test", "select naam from personen"); JList l=new JList(flm); flm2 = new SqlListModel("sun.jdbc.odbc.JdbcOdbcDriver", Visuele Gebruikers Omgevingen: Swing /395

116 "jdbc:odbc:test", "select leeftijd from personen"); JList l2=new JList(flm2); cbm = new SqlComboBoxModel("sun.jdbc.odbc.JdbcOdbcDriver", "jdbc:odbc:test", "select voornaam from personen"); JComboBox combo=new JComboBox(cbm); c.add(l); c.add(new JScrollPane(l2)); c.add(combo); flm.addelement("extra tekst"); flm.addelement("extra tekst2"); public static void main(string args[]) { System.out.println("Starting Test..."); JDatabaseList2 mainframe = new JDatabaseList2(); mainframe.setsize(400, 400); mainframe.settitle("test"); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

117 Composite Een array van Componenten Je kan alle elementen van een container aflopen door de array van Componenten (alle visuele elementen zijn componenten) af te lopen. GetComponents geeft een array van Componenten terug. Je kan deze array aflopen en voor elk element de Class vragen. Elk object heeft een Class object waarin beschreven staat hoe de klasse van het object eruit ziet. Voor een willekeurig object kan je met Class klasse= object.getclass(); het Class object opvragen. Aan dit object kan je bijvoorbeeld de naam vragen: object.getclass().getname(); Maar je kan evengoed alle methodes of alle datavelden van het object opvragen. Voor elke component kunnen we met getparent ook de container opvragen waarin hij zit. We merken dat but2 in een JPanel zit die op zijn beurt terug in een JPanel zit. Dit komt omdat de contentpane van de JFRame een JPanel is. We kunnen deze bevelen zelfs in een lus uitvoeren (totdat er geen parent meer is). Het blijkt dat de contentpane (JPanel) zelf in een JLayeredPane zit die op zijn beurt in een JRootPane zit. Waarom kan het handig zijn om alle componenten van een container af te lopen? Stel dat je alle componenten een andere kleur of grootte wilt geven (als reactie op een user event). Dan moet u gewoon al die componenten aflopen en van grootte of kleur veranderen. import javax.swing.*; import java.awt.*; import javax.swing.border.*; public class SwingComp extends JFrame {JButton but=new JButton("druk"); JLabel lab=new JLabel("tekst"); JPanel panel=new JPanel(); JButton but2=new JButton("druk2"); DefaultListModel datamodel=new DefaultListModel(); JList list=new JList(datamodel); public SwingComp() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); Visuele Gebruikers Omgevingen: Swing /395

118 c.add(but); c.add(lab); c.add(list); panel.add(but2); panel.setborder(new LineBorder(Color.blue,2)); panel.setpreferredsize(new Dimension(120,100)); c.add(panel); setsize(200,180); setdefaultcloseoperation(jframe.exit_on_close); setvisible(true); datamodel.addelement("--alle componenten van contentpane--"); Component [] comps=c.getcomponents(); for(int i=0;i<comps.length;i++) { Class klasse = comps[i].getclass(); datamodel.addelement( klasse.getname() ); datamodel.addelement("--vader en grootvader van but2--"); datamodel.addelement("vader van but2:"+but2.getparent().getclass().getname()); datamodel.addelement("grootvader van but2:"+but2.getparent().getparent().getclass().getname()); datamodel.addelement("--alle voorouders van but2--"); Component comp=but2; for(int i=0;comp!=null;i++) {datamodel.addelement("vader "+i+"van but2:"+comp.getclass().getname()); comp=comp.getparent(); public static void main(string args[]){new SwingComp(); Visuele Gebruikers Omgevingen: Swing /395

119 Dialog JFrames vanuit JApplet class mijnframe extends JFrame { public mijnframe(string titel) { super(titel); Container c=this.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new JSlider()); c.add(new JTextField(13)); this.setsize( 120,50); this.setvisible(true); public class Swing16 extends JApplet { public void init() { JFrame fr=new JFrame("ander scherm"); Container c=fr.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new JButton("druk op mij")); c.add(new JLabel("een label")); fr.setvisible(true); fr.setsize(200,100); new mijnframe("nog een ander venster"); Container cc=this.getcontentpane(); cc.add(new JLabel("we hebben nu drie vensters")); Een JFrame maken vanuit een andere JFrame In het volgend voorbeeld kunnen we vanuit het basis venster, andere vensters aanmaken. De nieuwe vensters komen steeds op een nieuwe positie op het scherm. Door middel van de knop show, hide last window kunnen we het laatst gemaakte venster verbergen en terug te voorschijn toveren. Visuele Gebruikers Omgevingen: Swing /395

120 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing_frame01 extends JFrame {private JButton but=new JButton("blabla"); private JButton but2=new JButton("blabla"); private JFrame frame; public Swing_frame01(){ Container c=getcontentpane(); c.add(but);c.add(but2);c.setlayout(new FlowLayout()); but.setaction(new Aktie("nieuw venster")); but2.setaction(new Aktie("show, hide last windows")); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); pack(); setvisible(true); public static void main(string args[]) {new Swing_frame01(); private class Aktie extends AbstractAction /* INNER KLASSE*/{ private int i=0; public Aktie(String s){super(s); public void actionperformed(actionevent e){ if(e.getsource()==but){ frame=new JFrame("extra"+i); frame.setbounds(150+i*10,150+i*10,80,50); frame.setdefaultcloseoperation(jframe.dispose_on_close); Container c2=frame.getcontentpane(); c2.add(new JLabel("extra venster: "+i++),borderlayout.center); frame.setvisible(true); if(e.getsource()==but2) { if(frame!=null) frame.setvisible(! frame.isvisible() ); In de globale veranderlijke frame houden we de referentie van het laatst gemaakt frame bij. Hierdoor kunnen we dit frame met behulp van setvisible(true/false) verbergen en terug tonen. Visuele Gebruikers Omgevingen: Swing /395

121 Communicatie tussen vensters Informatie doorgeven van kind venster naar ouder Het hoofdscherm gaat drie andere JFrames maken (ze erven over van JFrame). Elk van deze kind vensters krijgt bij zijn constructie een referentie van zijn vader mee (this). Het kindvenster zal deze referentie in een veranderlijke parent steken. Hierdoor kan het kind venster steeds communiceren met zijn vader door methode van zijn vader op te roepen (parent.voegtoeaanlijst( ) ). import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Swing_frame02 extends JFrame {private DefaultListModel listmodel=new DefaultListModel(); private JList list=new JList(listmodel); public Swing_frame02(){ Container c=getcontentpane(); c.add(new JLabel("toegevoegde elementen:")); c.add(list);c.setlayout(new FlowLayout()); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); new Swing_frame03(this); new Swing_frame03(this); new Swing_frame03(this); pack(); setvisible(true); public void voegtoeaanlijst(string s) {listmodel.addelement(s); public static void main(string args[]) {new Swing_frame02(); class Swing_frame03 extends JFrame {private JButton but=new JButton("blabla"); private JTextField text=new JTextField(20); private Swing_frame02 parent; public Swing_frame03(Swing_frame02 parent) {super("ingavescherm"); this.parent=parent; Container c=getcontentpane(); c.setlayout(new FlowLayout()); Visuele Gebruikers Omgevingen: Swing /395

122 this.setdefaultcloseoperation(jframe.dispose_on_close); c.add(but);c.add(new JLabel("tekst:")); c.add(text); but.setaction(new Aktie("voegtoe aan lijst")); this.setvisible(true); this.pack(); private class Aktie extends AbstractAction{ private int i=0; public Aktie(String s){super(s); public void actionperformed(actionevent e){ if(e.getsource()==but){ String t=text.gettext(); parent.voegtoeaanlijst(t); De publieke routine voegtoeaanlijst maakt het mogelijk om van buiten af elementen aan de lijst toe te voegen. (De lijst list wordt best private gehouden.) Communicatie tussen twee objecten van dezelfde klasse We tonen twee vensters naast elkaar. Als we tekst invullen op het eerste venster en op de button klikken, dan verschijnt deze tekst in de list van het tweede venster. En omgekeerd: tekst ingetypt in het tweede venster wordt naar het eerste venster gestuurd. Omdat we altijd één van de twee vensters moeten creëren voordat de andere wordt gecreëerd, kunnen we elkaars referentie niet meer langs de constructor doorgeven. Daarom voegen we een setbroer functie toe aan de klasse. Hiermee kunnen we na de creatie van beide objecten (van dezelfde klasse) de referenties doorgeven aan hun broer object. import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; Visuele Gebruikers Omgevingen: Swing /395

123 public class Swing_frame04 extends JFrame {private DefaultListModel listmodel=new DefaultListModel(); private JList list=new JList(listmodel); private JButton but=new JButton("blabla"); private JTextField text=new JTextField(10); private Swing_frame04 broer; public void setbroer(swing_frame04 broer) { this.broer=broer; public Swing_frame04(){ Container c=getcontentpane(); JPanel p1=new JPanel(),p2=new JPanel(); p1.add(new JLabel("boodschappen van broer:")); p1.add(list); p2.add(new JLabel("tekst voor broer:")); p2.add(text); p2.add(but); c.add(p1);c.add(p2); p1.setborder(new BevelBorder(BevelBorder.RAISED)); p2.setborder(new BevelBorder(BevelBorder.RAISED)); c.setlayout(new FlowLayout()); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); but.setaction(new AbstractAction("zend naar broer") { public void actionperformed(actionevent e) {if(e.getsource()==but) { String t=text.gettext(); broer.voegtoeaanlijst(t); ); pack(); setvisible(true); public void voegtoeaanlijst(string s) {listmodel.addelement(s); public static void main(string args[]) {Swing_frame04 fr1=new Swing_frame04(); Swing_frame04 fr2=new Swing_frame04(); fr1.setbroer(fr2); fr2.setbroer(fr1); In dit voorbeeld werken we ook met een naamloze innerklasse die overerft van AbstractAction. Omdat we toch maar éénmaal een object van deze klasse moeten maken, is het niet nodig deze klasse apart te definiëren en hier een naam voor te kiezen. Gemeenschappelijke code in basisklasse zetten We willen vorige oefening wat uitbreiden. Stel dat we twee broervensters willen hebben met als gemeenschappelijke eigenschap dat ze aan de lijst van hun broer iets willen toevoegen. Ze hebben naast gemeenschappelijke eigenschappen (zullen in de basisklasse komen) ook individuele eigenschappen: een eerste subklasse zal als extra mogelijkheid het leegmaken van de lijst van zijn broer hebben. Een tweede subklasse zal als extra mogelijkheid het opzoeken van tekst in de lijst van broer aanbieden. Visuele Gebruikers Omgevingen: Swing /395

124 De twee subklassen zullen daarom alleen de individuele verschillen programmeren zoals de extra componenten en de reactie erop. Het voordeel hiervan is dat bij noodzakelijke wijzigingen aan gemeenschappelijke delen we alleen de basisklasse moeten aanpassen. Om de lijst (listmodel) in de basisklasse gedefinieerd is en deze veranderlijke private is ( liefst ), worden we gedwongen om extra functies toe te voegen in de basisklasse: komtvoor, maakleeg, getbroer. Weersta aan de verleiding om de veranderlijke listmodel public te maken. Als je dit zou doen, kom je in de verleiding om in de afgeleide klassen dadelijk met deze veranderlijke te werken. Dit heeft als nadeel dat alle klassen tezamen één groot geheel vormen. Als je dan later de veranderlijke listmodel van naam verandert in de basisklasse, ben je verplicht om deze naamverandering door te voeren in alle afgeleide klassen. import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; public class Swing_frame05 extends JFrame {private DefaultListModel listmodel=new DefaultListModel(); private JList list=new JList(listmodel); private JButton but=new JButton("blabla"); private JTextField text=new JTextField(10); private Swing_frame05 broer; public void setbroer(swing_frame05 broer){ this.broer=broer; public Swing_frame05 getbroer() {return broer; public void maakleeg() {listmodel.clear(); public boolean komtvoor(string s) {return listmodel.contains(s); public void voegtoeaanlijst(string s) {listmodel.addelement(s); public Swing_frame05(){ Container c=getcontentpane(); JPanel p1=new JPanel(),p2=new JPanel(); p1.add(new JLabel("boodschappen van broer:")); p1.add(list); p2.add(new JLabel("tekst voor broer:")); Visuele Gebruikers Omgevingen: Swing /395

125 p2.add(text); p2.add(but); c.add(p1);c.add(p2); p1.setborder(new BevelBorder(BevelBorder.RAISED)); p2.setborder(new BevelBorder(BevelBorder.RAISED)); c.setlayout(new FlowLayout()); setlocation(100,100); setdefaultcloseoperation(jframe.exit_on_close); but.setaction(new AbstractAction("zend naar broer") {public void actionperformed(actionevent e) {if(e.getsource()==but) { String t=text.gettext(); broer.voegtoeaanlijst(t); ); pack(); setvisible(true); public static void main(string args[]) {Swing_frame05 fr1=new Swing_frame06(); Swing_frame05 fr2=new Swing_frame07(); fr1.setbroer(fr2); fr2.setbroer(fr1); class Swing_frame06 extends Swing_frame05 {JButton laatweg=new JButton(); public Swing_frame06() {super(); Container c=getcontentpane(); JPanel p=new JPanel(); p.setborder(new LineBorder(Color.blue,3)); p.add(new JLabel("maak lijst van broer leeg")); p.add(laatweg); c.add(p); pack(); laatweg.setaction(new AbstractAction("clear broer") {public void actionperformed(actionevent e) {getbroer().maakleeg(); ); class Swing_frame07 extends Swing_frame05 {JButton zoekop=new JButton(); JTextField text=new JTextField(10); public Swing_frame07() {super(); Container c=getcontentpane(); JPanel p=new JPanel(); p.setborder(new LineBorder(Color.blue,3)); p.add(new JLabel("zoek op in lijst van broer")); p.add(text); p.add(zoekop); c.add(p); pack(); zoekop.setaction(new AbstractAction("zoek bij broer") {public void actionperformed(actionevent e) {String s=text.gettext(); if( getbroer().komtvoor(s)) text.settext("gevonden bij broer"); else text.settext("niet gevonden"); ); Eenvoudige eigen JWindow class MijnVenster extends JWindow // normaal neemt men wel // een JFrame als hoofdvenster Visuele Gebruikers Omgevingen: Swing /395

126 { public MijnVenster() { Container c=this.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new JSlider()); c.add(new JTextField(13)); this.setsize( 220,180); this.setvisible(true); public class Swing17 {static public void main(string args[]) { new MijnVenster(); JWindow Bovenstaand scherm is een JWindow dat 10 seconden zal verschijnen. Het vult heel het scherm. Een van de grote verschilpunten met JFrame is het ontbreken van sluit toetsen. Thread.sleep zal het programma 10 seconden laten wachten. We moeten hiervoor wel een exception opvangen. (de thread zou door een externe oorzaak ruw wakker geschud kunnen worden). Na de 10 seconden, start de normale toepassing. Hier is dat een gewoon leeg JFrame. import javax.swing.*; import java.awt.*; import java.awt.event.*; Visuele Gebruikers Omgevingen: Swing /395

127 public class TestJW extends JFrame { public TestJW() { addwindowlistener(new WinClosing()); setbounds(100, 100, 300, 300); splashscreen(); setvisible(true); public void splashscreen() { JWindow jw = new JWindow(); Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); jw.setsize(screen.width, screen.height); JLabel pict = new JLabel( new ImageIcon("gardening.jpg"), JLabel.CENTER); JLabel lbl = new JLabel("Life on the Farm", JLabel.CENTER); lbl.setfont(new Font("Serif", Font.BOLD, 24)); lbl.setforeground(color.black); jw.getcontentpane().add(pict, BorderLayout.CENTER); jw.getcontentpane().add(lbl, BorderLayout.SOUTH); jw.setvisible(true); try { Thread.sleep(10000); catch (InterruptedException e) { jw.setvisible(false); public static void main(string args[]) { TestJW tjw = new TestJW(); class WinClosing extends WindowAdapter { public void windowclosing(windowevent we) { System.exit(0); De klasse Toolkit geeft meestal informatie over het platform waarop java draait. Met Toolkit.getDefaultToolkit() krijgen we de Toolkit van het huidige platform. Met getscreensize krijgen we de grootte van het huidige scherm. Als we onze JWindow dan even groot maken als de dimensie van het scherm, dan zal de toepassing er over voor zorgen dat het volledige scherm wordt opgevuld. Een venster met een eigen sluittoets In deze toepassing gaan we twee vensters onder elkaar tonen. Een JWindow heeft geen sluit toets, daarom moeten we zelf een button toevoegen om het venster te sluiten. Een venster sluiten kunnen we doen met de dispose methode. (Als we zouden werken met System.exit(0) zou heel de toepassing stoppen. import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.border.*; public class Swing_frame08 extends JWindow {private Container c; private JButton but; public Swing_frame08() { c=getcontentpane(); JPanel p=new JPanel(); p.settooltiptext("paneeltje"); p.setlayout(new GridLayout(0,1)); p.setbackground(color.white); Visuele Gebruikers Omgevingen: Swing /395

128 p.setopaque(true); p.setborder(new LineBorder(Color.red,2)); p.add(new JLabel(" dit venster heeft geen default sluittoetsen ")); but=new JButton("blabla"); but.setaction(new AbstractAction("sluit mij") {public void actionperformed(actionevent e) {if (e.getsource()==but) { dispose();// venster vrij geven. ); p. add(but); c.add(p); pack(); setvisible(true); public static void main(string args[]) {new Swing_frame08().setLocation(10,10); new Swing_frame08().setLocation(10,110); We maken twee objecten van dezelfde klasse en met behulp van setlocation zetten we ze op een andere plaats. JDialog JDialog zijn een soort vensters die erg lijken op JFrame, wel zijn ze ondergeschikt aan een bijhorende JFrame of JDialog. Hierdoor zullen bij het verkleinen of sluiten van de frame waarbij ze horen, de JDialog s ook mee verkleind of gesloten worden. JDialog s blijven steeds voor de frame (of andere dialog) vanwaar ze opgestart werden. Er zijn twee types van JDialogs: modaal of niet. Modale JDialogs zijn dwingend: slechts als ze gesloten zijn, kan je met andere schermen werken: ze blokkeren alles. Voor een modale JDialog moet je als laatste parameter van de constructor true meegeven. In bijhorende schermafdruk, werden de twee modale JDialogs twee en vier reeds gesloten. import java.awt.*; import javax.swing.*; public class SwingJDialog extends JFrame {JDialog diag1=new JDialog(this,"eerste",false); JDialog diag2=new JDialog(this,"tweede",true); JDialog diag3=new JDialog(this,"derde",false); JDialog diag4=new JDialog(this,"vierde",true); public SwingJDialog() {setsize(400, 400); setvisible(true); diag1.setvisible(true); diag2.setvisible(true); diag3.setvisible(true); diag4.setvisible(true); Container c=getcontentpane(); c.setlayout(new FlowLayout()); Visuele Gebruikers Omgevingen: Swing /395

129 setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) {new SwingJDialog(); Overerven van JDialog J e kan eenvoudig eigen dialoog vensters maken die opgestart worden vanuit een JFrame of een andere JDialog. Het zijn dan dialoog vensters die horen bij hun vader, samen met hen verkleind of gesloten worden en steeds in front of blijven staan. Je moet ervoor gewoon overerven van JDialog en zelf componenten toevoegen aan de contentpane van de JDialog. import java.awt.*; import javax.swing.*; import java.awt.event.*; class MijnJDialog extends JDialog {JButton but=new JButton("bla bla"); public MijnJDialog(Frame fr,string str, boolean b) {super(fr,str,b); Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(but); but.setaction(new AbstractAction("sluit mij") {public void actionperformed(actionevent e) {if (e.getsource()==but) ); setsize(100,50); { dispose();// venster vrij geven. public class SwingJDialog2 extends JFrame {JDialog diag1=new MijnJDialog(this,"eerste",false); JDialog diag2=new MijnJDialog(this,"tweede",true); JDialog diag3=new MijnJDialog(this,"derde",false); JDialog diag4=new MijnJDialog(this,"vierde",true); public SwingJDialog2() {setsize(400, 400); setvisible(true); diag1.setvisible(true); diag2.setvisible(true); diag3.setvisible(true); diag4.setvisible(true); Container c=getcontentpane(); c.setlayout(new FlowLayout()); setdefaultcloseoperation(jframe.exit_on_close); public static void main(string args[]) Visuele Gebruikers Omgevingen: Swing /395

130 {new SwingJDialog2(); awt en Swing door elkaar class MijnFrame extends JFrame // een JFrame als hoofdvenster { public MijnFrame() { super("frame"); Container c=this.getcontentpane(); c.setlayout(new FlowLayout()); c.add(new Button("windows button")); c.add(new JButton("lightweightButton")); c.add(new Scrollbar()); c.add(new JScrollBar()); List l=new List(); l.add("1");l.add("2");l.add("3"); c.add( l ); c.add(new JList(new String[]{"1","2","3")); c.add(new Checkbox("check")); c.add(new JCheckBox("check")); this.setsize( 220,180); this.setvisible(true); public class Swing18 {static public void main(string args[]) { JFrame fr=new MijnFrame(); JOptionPane.showMessageDialog(null, "U ziet deze dadelijk", "na Frame", JOptionPane.INFORMATION_MESSAGE); JDialog d=new JDialog( fr ); d.getcontentpane().add(new JLabel("dit is een dialog")); d.setvisible(true);d.setsize(150,120);d.pack(); JOptionPane.showMessageDialog(null, Visuele Gebruikers Omgevingen: Swing /395

131 "gedaan", "gedaan", JOptionPane.INFORMATION_MESSAGE); JColorChooser als een aparte dialoog Met JColorChooser.showDialog kan je eenvoudig een dialoog venster laten verschijnen om de gebruiker een kleur te laten kiezen. Er zijn drie parameters: de ouder frame/dialoog, titel van dialoog, default Color. import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class SwingJColorChooser2 extends JFrame { JLabel lab=new JLabel("gewoon tekst"); public SwingJColorChooser2() {setsize(400, 400); lab.setopaque(true);// ondoorzichtig lab.setpreferredsize(new Dimension(200,50)); Container c=getcontentpane(); c.setlayout(new FlowLayout()); setdefaultcloseoperation(jframe.exit_on_close); c.add(lab); Color newcolor = JColorChooser.showDialog( lab.setbackground(newcolor); setvisible(true); public static void main(string args[]) this,"choose label Color", Color.red); Visuele Gebruikers Omgevingen: Swing /395

132 {new SwingJColorChooser2(); JColorChooser in uw eigen frame Je kan een JColorChooser object gewoon als component toevoegen aan uw contentpane. Vervolgens kan je de gebruiker bijvoorbeeld een extra JButton laten intypen en kan je met getcolor de gekozen kleur verkrijgen. In het voorbeeld hieronder werd een ChangeListener verbonden met de JColorChooser. Hierdoor kan je een kleur kiezen en wordt er dadelijk code uitgevoerd bij elke selectie. Daardoor zal in de toepassing hieronder, onze label dadelijk de kleur krijgen die we in de JColorChooser paneel kiezen. import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class SwingJColorChooser extends JFrame {JLabel lab=new JLabel("gewoon tekst"); JColorChooser tcc = new JColorChooser(); public SwingJColorChooser() {setsize(400, 400); lab.setopaque(true);// ondoorzichtig lab.setpreferredsize(new Dimension(200,50)); Container c=getcontentpane(); c.setlayout(new FlowLayout()); setdefaultcloseoperation(jframe.exit_on_close); tcc.getselectionmodel(). addchangelistener( new ChangeListener( ) { public void statechanged(changeevent e) { Color newcolor = tcc.getcolor(); lab.setbackground(newcolor); ); c.add(lab); c.add(tcc); setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

133 public static void main(string args[]) {new SwingJColorChooser(); ### ############################################################## JFileChooser We hebben drie mogelijkheden om een object van de klasse JFileChooser te tonen: fc.showopendialog(this); fc.showsavedialog(this); fc.showdialog(this, titel ); Standaard zijn er twee dialogen voorzien voor openen en saven van files. Je kan ook een eigen titel aan uw dialoog geven met de derde mogelijkheid. Merk op dat je (zoals bij alle dialogen) een ouder venster voor de dialoog moet opgeven. Na de oproep, krijg je een return code waarop je moet testen om na te gaan of de gebruiker wel ok gekozen heeft. import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class SwingJFileChooser extends JFrame {JLabel lab=new JLabel("gewoon tekst"); public SwingJFileChooser() Visuele Gebruikers Omgevingen: Swing /395

134 {setsize(400, 400); lab.setopaque(true);// ondoorzichtig Container c=getcontentpane(); c.setlayout(new FlowLayout()); setdefaultcloseoperation(jframe.exit_on_close); c.add(lab); JFileChooser fc = new JFileChooser(); int returnval = fc.showopendialog(this); if (returnval == JFileChooser.APPROVE_OPTION) { File file = fc.getselectedfile(); String naam=file.getpath()+file.getname(); lab.settext(naam); setvisible(true); public static void main(string args[]) {new SwingJFileChooser(); Visuele Gebruikers Omgevingen: Swing /395

135 Eigen dialoogboxen maken Overerven van JDialog is dé manier om eigen dialoogboxen te maken. Wat is het onderscheid tussen eigen dialoogboxen en JFrames? JFrames zijn volwaardige vensters. JDialog vensters zijn subdialoog vensters die horen bij een JFrame of bij een andere JDialog. Daarom zal de constructor van een JDialog steeds vragen om de referentie van een ouderobject. Dit ouder-object is het object van waaruit de JDialog werd gemaakt. Elk kind-jdialog zal steeds boven het bijhorende ouder-object getoond worden. Als het ouder-object gesloten wordt, worden alle bijhorende kind-jdialogs ook automatisch gesloten. Als het ouder-object verkleind wordt tot een icoontje, worden de bijhorende kind-jdialogs ook mee verkleind tot een icoontje. Als een ouder-object terug vergroot wordt, worden de bijhorende kind-jdialogs ook terug meer vergroot (uitgaande van een icoontje). Dit ouder-object is een JFrame die een knop heeft om kind-jdialogs objecten mee aan te maken. Stel dat we drie maal op deze knop drukken. Er verschijnen dan drie extra JDialogs: In deze drie extra JDialogs kan je een naam, voornaam, leeftijd intypen. Bij het intypen van een fout voor leeftijd kleurt het veld anders. Visuele Gebruikers Omgevingen: Swing /395

136 Bij het sluiten van een kind-jdialog, zal vlug eerst de ingevulde informatie worden overgezonden naar het ouder-object. Daar wordt de ingevulde informatie in JTextArea s velden achteraan toegevoegd. De klasse PersoonDialoog heeft een extra veranderlijke papa. Hierin komt de referentie naar het ouder-object. Deze referentie kan altijd opgevraagd worden met behulp van de routine getowner. Eigendialoog papa=getowner(); Het volgende zou een fout geven: getowner().setnaam( qsdf ); Visuele Gebruikers Omgevingen: Swing /395

137 Dit komt omdat de routine getowner bij de klasse Window hoort (JDialog erft over van Window). De routine setnaam is alleen geldig voor objecten van de klasse Eigendialoog. papa.setnaam( qsdf ); is wel goed. Je kan ook expliciet de referentie downcasten naar het juiste type: ((Eigendialoog)getOwner()).setNaam( qsdf ). Hoe krijgt de routie getowner de juiste waarde? Bij de constructor van JDialog wordt de referentie van het ouder-object doorgegeven. Deze zal door de constructor van de klasse JDialog worden doorgegeven naar de constructor van zijn basisklasse Window. Deze klasse Window zal deze referentie stockeren in een globale veranderlijke van zijn klasse. De routine getowner van de klass Window zal deze referentie teruggeven. import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; class PersoonDialoog extends JDialog { Eigendialoog papa; String naam; String voornaam; int leeftijd; Container c; JIntegerTextField jleeftijd=new JIntegerTextField(); JTextField jnaam=new JTextField(); JTextField jvoornaam=new JTextField(); public PersoonDialoog(Frame frame) {super(frame,"persoonsgegevens"); papa = ((Eigendialoog)getOwner()); c=getcontentpane(); c.setlayout(new GridLayout(4,2)); c.add(new JLabel("geef leeftijd")); c.add(jleeftijd); jnaam.setpreferredsize( new Dimension(150,30)); jvoornaam.setpreferredsize(new Dimension(150,30)); c.add(new JLabel("geef naam"));c.add(jnaam); c.add(new JLabel("geef voornaam"));c.add(jvoornaam); JButton but=new JButton("sluit"); but.addactionlistener( new ActionListener() {public void actionperformed(actionevent ae) { papa.setnaam(jnaam.gettext()); papa.setvoornaam(jvoornaam.gettext()); papa.setleeftijd(jleeftijd.gettext()); PersoonDialoog.this.dispose(); ); Visuele Gebruikers Omgevingen: Swing /395

138 c.add(but); setsize(300,250); setvisible(true); PersoonDialoog.this.dispose(); Met behulp van dispose() wordt de dialog gesloten en wordt het object uit het geheugen gehaald. this.dispose() zou een fout geven omdat dit bevel in een naamloos actionlistener-object wordt gegeven. this zou in dit geval wijzen naar dit naamloos actionlistener-object. Willen we de this van de omsluitende PersoonDialoog klasse, dan moeten we dit expliciet opgeven. public class Eigendialoog extends JFrame { Container c; JTextArea naam=new JTextArea(50,4), voornaam=new JTextArea(50,4), leeftijd=new JTextArea(50,4); public void setnaam(string naam){this.naam.append(naam+'\n'); public void setvoornaam(string voornaam) {this.voornaam.append(voornaam+'\n'); public void setleeftijd(string leeftijd){this.leeftijd.append(leeftijd+'\n'); public Eigendialoog() { setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); JButton but=new JButton("maak nieuw dialoog venster"); c.setlayout(new GridLayout(4,2)); c.add(new JLabel("namen"));c.add(naam); c.add(new JLabel("voornamen"));c.add(voornaam); c.add(new JLabel("leeftijden"));c.add(leeftijd); naam.setborder(new LineBorder(Color.red)); voornaam.setborder(new LineBorder(Color.red)); leeftijd.setborder(new LineBorder(Color.red)); but.addactionlistener( new ActionListener() {public void actionperformed(actionevent ae) {new PersoonDialoog(Eigendialoog.this); ); c.add(but); public static void main(string args[]) { System.out.println("Starting Eigendialoog..."); Eigendialoog mainframe = new Eigendialoog(); mainframe.setsize(400, 400); mainframe.settitle("eigendialoog"); mainframe.setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

139 new PersoonDialoog(Eigendialoog.this); Elke keer dat er op de button wordt geklikt moet er een nieuw PersoonDialoog object worden aangemaakt. Hierbij moet de referentie van het ouder-object worden doorgegeven aan de constuctor. Weeral is dit niet gewoon this, maar Eigendialoog.this. Eigendialoog is de omsluitende klasse van het naamloos actionlistenerobject. (this zou hier wijzen naar dit naamloos innerklasse.) class JIntegerTextField extends JTextField {public JIntegerTextField() {super(); addkeylistener(new KeyAdapter() {public void keyreleased(keyevent e) {setbackground(color.white); String tekst=gettext(); try{int i=integer.parseint(tekst); catch(exception ee) {if(!tekst.equals("")){setbackground(color.orange);); public int getgetal() {int i;string tekst=gettext(); try{i=integer.parseint(tekst); catch(exception ee){i=0; return i; public Dimension getpreferredsize() {return new Dimension(70,30); Visuele Gebruikers Omgevingen: Swing /395

140 Communicatie tussen twee klassen Eens de communicatie tussen twee klassen gedefinieerd, zal deze communicatie gelden voor alle objecten van deze twee klassen. In bovenstaand schermafdruk zijn twee Bewegenddeel -objecten aanwezig. Je kan ze met je muis verslepen. Als je ergens rechts klikt op het scherm, komen er twee objecten bij van de klassen Kind en Ouder. Het Ouder object is iets groter dan het Kind object. Omdat ze terzelfdertijd worden gekreëerd, krijgen ze hetzelfde nummer. Ze horen immers bij elkaar. Ze zullen steeds met elkaar kunnen communiceren. Dit geldt voor alle paren van objecten van de klassen Ouder en Kind. Ze erven over van de klasse Bewegenddeel en kunnen dus allemaal versleept worden. In bovenstaande schermafdruk zijn drie Kind-Ouder paren bijgekreëerd. Als je echter op een Ouder object klikt, zal dit object geel gekleurd worden. Door middel van communicatie met zijn bijhorend Kind object, zal dit ook geel gekleurd worden. Omgekeerd: klikken op een Kind object zal zowel het Kind object als het bijhorend Ouder object groen doen kleuren. Hierdoor zijn bijelkaar horende objecten eenvoudiger terug te vinden. Visuele Gebruikers Omgevingen: Swing /395

141 import javax.swing.border.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; class Bewegenddeel extends JPanel { int x,y,px,py; int posx,posy; int nummer; public Bewegenddeel(int x,int y,int nummer) {this.x=x;this.y=y;this.nummer=nummer; setlocation(x,y); setsize(30,70); setbackground(color.blue); setborder(new EtchedBorder()); addmousemotionlistener (new MouseMotionAdapter() {public void mousedragged(mouseevent e) { Graphics g=getgraphics(); posx=e.getx(); posy=e.gety(); Bewegenddeel.this.moveBy(posx - px, posy -py); px = posx; py = posy;); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) { px=e.getx(); py=e.gety(); ); public void moveby(int x,int y) {Point p=getlocation(); setlocation((int)(p.getx()+x),(int)(p.gety()+y)); public void paintcomponent(graphics g) {super.paintcomponent(g); g.drawstring(""+nummer,10,20); public Color getkleur(){return Color.blue; Visuele Gebruikers Omgevingen: Swing /395

142 class Ouder extends Bewegenddeel {Kind kind; public Ouder(int x,int y,int nummer) {super(x,y,nummer); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) {setbackground(color.yellow); kind.setbackground(color.yellow);); public void setkind(kind kind){this.kind=kind; class Kind extends Bewegenddeel {Ouder ouder; public Kind(int x,int y,int nummer) {super(x,y,nummer); setsize(20,40); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) {setbackground(color.green); ouder.setbackground(color.green);); public void setouder(ouder ouder){this.ouder=ouder; public int getbreedte(){return 20; public int gethoogte(){return 50; class TekenPaneel extends JPanel { int aantal=1; int posx,posy; int px, py; public TekenPaneel() {setlayout(null); add(new Bewegenddeel(10,10,0));add(new Bewegenddeel(60,100,0)); setdoublebuffered(false); setopaque(false); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) { if(e.ismetadown())//rechtsgeklikt {px=e.getx(); py=e.gety(); Ouder ouder=new Ouder(px,py,aantal); Kind kind=new Kind(px+35,py+10,aantal++); ouder.setkind(kind); kind.setouder(ouder); add(ouder);add(kind); repaint(); ); Visuele Gebruikers Omgevingen: Swing /395

143 public class Communicatietussentweeklassen extends JFrame { Container c; TekenPaneel tekenpaneel; public Communicatietussentweeklassen() { setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); tekenpaneel=new TekenPaneel(); c.add(borderlayout.center,tekenpaneel); setsize(400,400); setvisible(true); public static void main(string args[]) { Communicatietussentweeklassen mainframe = new Communicatietussentweeklassen(); mainframe.setsize(400, 400); mainframe.settitle("communicatietussentweeklassen"); mainframe.setvisible(true); Ouder ouder=new Ouder(px,py,aantal); Kind kind=new Kind(px+35,py+10,aantal++); ouder.setkind(kind); kind.setouder(ouder); Visuele Gebruikers Omgevingen: Swing /395

144 Telkens een Ouder-Kind paar wordt aangemaakt, zal met behulp van de methoden setkind en setouder de referenties van elkaar worden doorgegeven. Hoe goed verslepen in Swing goedenavond meneer janssens, u vroeg mij de code voor een verplaatsbaar component door te sturen, de code werkt als volgt, boven een paneel waar de componenten die niet gewijzigd kunnen worden door de gebruiker (zoals bvb. labels, fotos, lijsten waar niets aan verandert moet worden, etc.) op staan plaatst u een tweede doorzichtig paneel. op dat doorzichtig paneel plaatst u de componenten waar u als gebruiker wel mee moet werken (invoerschermen en dergelijke). het doorzichtige paneel "onderschept" de muisbewegingen en muisbewerkingen en zo kan geregistreerd worden waar er geklikt is. ik neem aan dat de code enkel werkt met panelen waar de layout "null" van toepassing is. (het idee is weliswaar genomen van de "setglasspanel"- methode van java :P) ik heb geen idee hoe je gewoon op een paneel muisbewerkingen kunt controleren als er op een component van dat paneel geklikt wordt (dus er wordt geklikt op een knop, maar ik zou willen weten waar op het paneel dat exact geklikt is) dus het is niet mogelijk om belangrijke invoerschermen op die manier te verplaatsen. (bij labels wordt raar genoeg wel de klik of muisbeweging/bewerking als een muisbewerking op het paneel aanzien, daarom is het mogelijk om zonder dat doorzichtig paneel te werken voor enkel labels) ik heb een aantal vlugge aanpassingen in mijn oefening gemaakt zodat ALLE componenten die niet door de gebruiker gewijzigd worden (labels, foto, lijst) verplaatsbaar zijn. hier is de code die van belang is: jtabbedpane1.setbounds(0, 0, 430, 320); jtabbedpane1.addtab("lingman", jpanel1); jtabbedpane1.addtab("spelregels", desktop); jtabbedpane1.addtab("cheats", jpanel3); jpanel1.setlayout(null); //maken van een doorzichtige panel emptypanel.setlayout(null); emptypanel.setopaque(false); emptypanel.setvisible(true); emptypanel.setbounds(0, 0,jTabbedPane1.getWidth(), jtabbedpane1.getheight()); //we voegen het doorzichtige paneel toe aan het huidige paneel, //als eerste, zodat het bovenop de andere componenten //ligt jpanel1.add(emptypanel); jpanel1.add(picture); jpanel1.add(raadwoord); jpanel1.add(lijst); jpanel1.add(letterlabel); jpanel1.add(woordlabel); Visuele Gebruikers Omgevingen: Swing /395

145 worden het //alle items waar veranderingen of invoer aan moeten gebracht //worden toegevoegd aan het doorzichte panel (als deze onder //paneel zitten raken we er niet meer aan) emptypanel.add(woord); emptypanel.add(raden); emptypanel.add(vastelijst); emptypanel.add(start); emptypanel.add(letter); //we voegen een MouseMotionListener toe aan het lege paneel //bij mousedragged gaat, als er een component in zit, //het verplaatsbare component verschoven worden adhv het verschil //tov de muiscoördinaten (nieuwe locatie toekennen) emptypanel.addmousemotionlistener(new MouseMotionListener() { public void mousedragged(mouseevent e) { if (dragcomponent!= null) {dragcomponent.setlocation(e.getx()+picx, e.gety()+picy); public void mousemoved(mouseevent e) { ); "null" //we voegen bij het lege paneel een mouselistener toe, //deze registreert, als er op een component geklikt wordt //het verschil tussen de huidige coördinaten //en plaats het object in de "te verplaatsten component" //als de muis gerleased wordt wordt de component terug op //geplaatsd. emptypanel.addmouselistener( new MouseListener() { MouseEvent b; public void mouseclicked(mouseevent e) { public void mouseentered(mouseevent e) { public void mouseexited(mouseevent e) { toevoegen zetten public void mousepressed(mouseevent e) { //als je een nieuwe 'verplaatsbare' component wilt //moet je gewoon hier setdraggable(componentnaam) //(en hem toevoegen op het doorzichtige paneel) b=e; setdraggable(picture); setdraggable(letterlabel); setdraggable(lijst); setdraggable(raadwoord); setdraggable(woordlabel); Visuele Gebruikers Omgevingen: Swing /395

146 public void mousereleased(mouseevent e) { dragcomponent=null; public void setdraggable(component com) { //als er geklikt is binnen de grenzen van de component //dan wordt het verschil met de muis tov de bovenrand van de //component berekend. if (b.getx()>=com.getx() && b.getx() < (com.getx()+com.getwidth()) && b.gety() >= com.gety() && b.gety() <= com.gety()+com.getheight()) { picx = com.getx() - b.getx(); picy = com.gety() - b.gety(); dragcomponent = com; ); (als de opmaak in de mail onduidelijk zou gemaakt worden staat dezelfde code ook nog 'ns in een apart txt-file) Als u het misschien iets te onduidelijk zou vinden, of nog iets wil vragen, stuur dan maar gerust een mailtje terug. Met Vriendelijke Groeten, David Van den Bergh Visuele Gebruikers Omgevingen: Swing /395

147 Images Als je gewoon een eigen tekenvlak wilt hebben, kan je best overerven van Canvas (awt) of JPanel (Swing). Dit zijn de eenvoudigste lege Componenten. Je kan voor een JPanel een eigen paint voorzien. Weet wel dat een JPanel ook een Container is, maar probeer best niet naast het opgeven van een eigen paint ook de container te gebruiken door eigen componenten hieraan toe te voegen. Wil je toch een toepassing met een eigen tekening en ook componenten, zet dan de eigen componenten in een andere gewone JPanel. class mijncanvas extends Canvas { Image logo1; public mijncanvas() {this.setsize( 40,40); logo1= Toolkit.getDefaultToolkit().getImage("globe.gif"); public void paint(graphics g) {g.drawimage(logo1,0,0,this); g.drawline(0,0,40,40); g.drawline(0,40,40,0); class mijnpanel extends JPanel {public mijnpanel(){ setpreferredsize(new Dimension(100,50)); public void paint(graphics g) { g.setcolor(color.blue) ; g.fillpolygon(new int[]{10,20,30,40, 50,60, 70,80, 90,100,1, new int[] {1,50,1, 50,1,50,1,50,1,50,50,11); public class Swing20 extends JApplet { public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(new mijncanvas()); c.add(new mijnpanel()); c.add(new mijncanvas()); JPanel p=new JPanel(); p.add(new JButton("druk") ); p.add(new mijnpanel()); p.add(new mijncanvas()); p.add(new JSlider()); c.add(p); In een gewone toepassing ga je een Image laden met behulp van Toolkit klasse. Toolkit.getDefaultToolkit geeft de Toolkit klasse voor uw besturingssysteem terug.een polygon is een veelhoek waarvan je gewoon de coordinaten van de hoekpunten moet opgeven. Visuele Gebruikers Omgevingen: Swing /395

148 ImageIcon import java.awt.*; import javax.swing.*; import java.net.*; public class SwingImageIcon extends JFrame {Container c; JLabel lab,lab2; ImageIcon icon=new ImageIcon("globe2.gif","eerste label"); ImageIcon icon2; public SwingImageIcon() {setbounds(10,10,250,180); setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); c.setlayout(new FlowLayout()); lab=new JLabel(icon); // algemene wijze : werkt ook met jar, zoekt classpath, jar's... URL iconurl = ClassLoader.getSystemResource("globe.gif"); if (iconurl!= null) { icon2 = new ImageIcon(iconURL,"tweede label"); lab2=new JLabel(icon2); c.add(lab);c.add(lab2); setvisible(true); public static void main(string args[]) {new SwingImageIcon() ; ImageIcon is simpelder dan Image. Het is eigenlijk bedoeld voor kleine icoontjes. Het verschil met Image is dat uw toepassing steeds wacht totdat de ImageIcon volledig in het geheugen geladen is, voordat verder wordt gegaan. Je kan ook een tekst meegeven met de constructor van ImageIcon. Dit is voor accessibility (blinden): de tekst zal getoond worden in plaats van de tekening. ClassLoader.getSystemResource zal de tekening zoeken volgens classpath. (Als je bepaalde packages nodig hebt, worden deze gezocht volgens uw classpath. Zie later maken van packages. DIT KAN OOK EEN JAR FIL E ZIJN, dit is een soort ZIP file, waarmee u uw toepassing over internet kunt versturen.) Image import java.awt.*; import javax.swing.*; import java.net.*; Visuele Gebruikers Omgevingen: Swing /395

149 class Paneel extends JPanel {ImageIcon icon=new ImageIcon("globe2.gif","eerste label"); public Paneel(){setPreferredSize(new Dimension(100,100)); public void paint(graphics g) {Image im=icon.getimage(); g.drawimage(im,0,0,100,100,this); class Paneel2 extends JPanel {Image im=toolkit.getdefaulttoolkit().getimage("globe2.gif"); public Paneel2(){setPreferredSize(new Dimension(100,100)); public void paint(g raphics g){ g.drawimage(im,0,0,100,100,this); public class SwingImageIcon2 extends JFrame {Container c; public SwingImageIcon2() {setbounds(10,10,250,180); setdefaultcloseoperation(jframe.exit_on_close); c=getcontentpane(); c.setlayout(new FlowLayout()); Paneel paneel=new Paneel(); Paneel2 paneel2=new Paneel2(); c.add(paneel);c.add(paneel2); setvisible(true); public static void main(string args[]) {new SwingImageIcon2(); Je kan eenvoudig een ImageIcon omzetten naar een Image door de methode getimage op te roepen voor de ImageIcon. Vervolgens kan je deze image gebruiken in drawimage. Communicatie tussen Luisteraar en verwittigaar class Luisteraar implements ActionListener Visuele Gebruikers Omgevingen: Swing /395

150 { private JLabel ll; public Luisteraar(JLabel par){ll=par; public void actionperformed(actionevent e) { ll.settext(e.getactioncommand() ); public class SwingB extends JApplet {public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); JLabel l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); t.addactionlistener(new Luisteraar(l)); Stel dat de luisteraar een label van de verwittigaar moet wijzigen? Omdat de luisteraar een andere klasse is, kan hij niet dadelijk aan de label van de de verwittigaar (hier SwingB). We moeten hiervoor dan een referentie van de label doorgeven aan de constructor van de luisteraar. Deze referentie wordt dan gestockeerd in de veranderlijk ll. Hiermee kunnen we dan de tekst van de label veranderen. Merk op dat e.getactioncommand ook de tekst van het ingevulde JTextField bevat. (Je kan deze tekst natuurlijk ook nog altijd ophalen uit het tekstveldje.) Je hebt geen problemen met communicatie tussen luisteraar en verwittigaar indien de verwittigaar zelf een luisteraar is. Hiervoor moet je SwingC de interface ActionListener zelf laten implementeren. De klasse SwingC zal hiervoor dan zelf de routine actionperformed moeten opgeven. This zal dan moeten worden doorgegeven aan addactionlistener. ZELFDE UITVOER als vorig programma: public class SwingC extends JApplet implements ActionListener { private JLabel l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); t.addactionlistener(this); public void actionperformed(actionevent e) { l.settext(e.getactioncommand() ); NOGMAALS ZELFDE UITVOER: Ten slotte kunnen we ook werken met een innerklasse om het probleem van communicatie op te lossen. Een al dan niet naamloze innerklasse kan rechtstreeks aan de informatie van zijn omliggende klasse. Dit gebeurt in het volgende voorbeeld. public class SwingD extends JApplet { private JLabel l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); t.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { l.settext(e.getactioncommand() ); Visuele Gebruikers Omgevingen: Swing /395

151 ); De naamloze innerklasse kan rechtstreeks de label l benaderen. Muis luisteraars Muis bewegingen worden opgevangen door MouseMotionListeners. Gewone muiskliks worden opgevangen door MouseListeners. Zie Grant Palmer om na te gaan welke methodes deze luisteraars moeten hebben. Grant Palmer bevat ook uitleg over de informatie die MouseEvent bevat. Zoek eens op. Nu. public class SwingE extends JApplet implements MouseMotionListener { private JLabel l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); this.addmousemotionlistener(this); public void mousedragged(mouseevent e) { l.settext("beweegt en ingedrukt: "+ e.getx()+" "+e.gety() + (e.isaltdown()?" Alt ":"") + (e.isshiftdown()?" Shift ":"") + (e.iscontroldown()?" Control ":"") + " when : "+ e.getwhen() ); public void mousemoved(mouseevent e) { l.settext("beweegt: " +e.getx()+" "+ e.gety() + (e.isaltdown()?" Alt ":"") + (e.isshiftdown()?" Shift ":"") + (e.iscontroldown()?" Control ":"") + " when : "+ e.getwhen() ); Visuele Gebruikers Omgevingen: Swing /395

152 Als je met de muis over de applet beweegt, wordt steeds de positie van de muis getoond, samen met nog andere informatie: is shift, control, alt toets ingedrukt of niet. mousedragged zal worden opgeroepen als de muis is ingedrukt en beweegt. GENEREERT ONGEVEER DEZELFDE UITVOER (alleen bij ingedrukt muisbewegingen publi c class SwingF extends JApplet { private JLabel l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); this.addmousemotionlistener(new MouseMotionListener() {public void mousedragged(mouseevent e) { l.settext("beweegt en ingedrukt: " t + e.getx()+" "+e.gety() + (e.isaltdown()?" Alt ":"") + (e.isshiftdown()?" Shift ":"") + (e.iscontroldown()?" Control ":"") + " when : "+ e.getwhen()); public void mousemoved(mouseevent e) { // ook al wil je niets doen b ij deze event, toch moet // je deze lege routine geven om de listener te // implem enteren. In dit geval kan je beter adapter // gebruiken ); Analoog aan vorige oplossing, maar nu met Adapter: public class SwingG extends JApplet { private JLabel l; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); l=new JLabel("vul in en druk enter"); c.add(l); JTextField t=new JTextField(10); c.add(t); this.addmousemotionlistener(new MouseMoti onadapter() {public void mousedragged(mouseevent e) { l.settext("beweegt en ingedrukt: " + e.getx()+" "+e.gety() + (e.isaltdown()?" Alt ":"") + (e.isshiftdown()?" Shift ":"") + (e.iscontroldown()?" Control ":"") + " when : "+ e.getwhen()); //overbodig:public void mousemoved(mouseevent e) // deze routine staat in de klasse MouseMotionAdapter // deze implementeer de Listene r interface met lege // routines, zodat wij dat niet meer moeten doen. ); Een adapter klasse is een lege luisteraar: De interface wordt geïmplementeerd met lege versies van de gevraagde routines. Indien een interface (zoals WindowListener) meerdere routines verplicht te implementeren en je bent maar geïnteresseerd in één routine, dan kan je beter overerven van de bijhorende adapter. Je hoeft dan alleen maar Visuele Gebruikers Omgevingen: Swing /395

153 uw versie van de routine waarin je geïnteresseerd bent, te implementeren. Voor de andere routines erf je de lege versies over. Ee n eenvoudig tekenprogramma De eenvoudigste wijze om een tekenprogramma te maken, is een JPanel nemen en hieraan een MouseMotionListener verbinden. Hiermee vang je de muisbewegingen op. Telkens je een muisbeweging dedecteert, kan je met getgraphics() het tekenbord opvragen en hierop tekenen. Wil je bijvoorbeeld met de muis vloeiend e lijnen tekenen, dan moet je steeds werken met een vorige positie. Hiermee kan je dan steeds een lijn tekenen van de vorige positie naar de huidige positie. Er is wel een probleem met dit tekenprogramma: als je iets getekend hebt en je verandert de grootte van uw venster, dan wordt paint terug opgeroepen, die terug een lege rechthoek zal tekenen. Uw tekening is weg. Dit komt omdat de paint automatisch wordt opgeroepen. We hebben ook een MouseAdapter nodig om de coordinaten van het begin punt van de bewegende lijn te registreren. class MijnPanel extends JPanel {int x=0,y=0,oldx,oldy; Vector v=new Vector(); public MijnPanel() { setbackground(color.yellow); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent e) {x=e.getx();y=e.gety(); ); Visuele Gebruikers Omgevingen: Swing /395

154 addmousemotionlistener( new MouseMotionAdapter() { public void mousedragged(mouseevent e) {oldx=x;oldy=y;x=e.getx();y=e.gety(); Graphics g=getgraphics(); g.drawline(oldx,oldy,x,y); ); public void paintcomponent(graphics g) { super.paintcomponent(g); g.drawrect(10,10,250,200); public class Swing4 extends JApplet { JCheckBox cb= new JCheckBox("stip mij aan"); JRadioButton cb2=new JRadioButton("kies mij"); JPanel ca=new MijnPanel(); public void init() {Container c=getcontentpane(); c.setlayout(new BorderLayout()); c.add(cb,borderlayout.south); c.add(cb2,borderlayout.north); c.add(ca,borderlayout.center); setvisible(true); Je merkt dat de JPanel waarop getekend wordt, slechts één van de componenten is die in de container van de applet zitten. Gebruik een aparte component om op te tekenen. Probeer niet aan de JPanel waarop je tekent ook nog eens componenten toe te voegen. == ================================= De volgorde van oproepen is vrij ingewikkeld: JComponent.update->JComponent.paint->JComponent.paintComponent->ComponentUI.update ->ComponentUI.paint->JComponent.paintBorder->JComponent.paintChildren update gaat eerst het veld leegmaken en vervolgens paint oproepen. (Wil je bijvoorbeeld dat u veld niet eerst leeg gemaakt wordt, dan moet je een eigen versie van update voorzien die gewoon paint oproept (zonder eerst het veld le eg te maken). De tekening herstellen bij paint We gaan nu in een Vector de coordinaten van alle punten van de vloeiende lijnen bijhouden. Als je nu uw venster verkleint en terug vergroot, staat de tekening er terug. Toch zijn er enkele extra lijnen. Dit komt omdat we in de vector gewoon alle coordinaten achter elkaar steken zonder aan te geven welke punten begin of eindpunten zijn. Bij het hertekenen worden in onze versie alle punten verbonden met de vorige punten. Ook als je de muis even oplicht en verplaatst. Probeer dit zelf te verhelpen. Visuele Gebruikers Omgevingen: Swing /395

155 Na vergroten en verkleinen staat er een extra lijn. Hoe komt dit? Wat kan je er aan doen? class MijnPanel extends JPanel {int x=0,y=0,oldx,oldy; Vector v=new Vector(); public MijnPanel() { setbackground(color.yellow); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent e) {x=e.getx();y=e.gety();v.addelement(new Point(x,y)); ); addmousemotionlistener( new MouseMotionAdapter() { public void mousedragged(mouseevent e) {oldx=x;oldy=y;x=e.getx();y=e.gety(); Graphics g=getgraphics(); g.drawline(oldx,oldy,x,y); v.addelement(new Point(x,y)); ); public void paintcomponent(graphics g) { super.paintcomponent(g); g.drawrect(10,10,250,200); int x,y,oldx,oldy; Enumeration e=v.elements(); if(e.hasmoreelements()) { Point p= (Point)e.nextElement(); oldx=(int)p.getx();oldy=(int)p.gety(); while(e.hasmoreelements()) { p= (Point)e.nextElement(); x=(int)p.getx();y=(int)p.gety(); g.drawline(oldx,oldy,x,y); oldx=x;oldy=y; public class Swing4 extends JApplet Visuele Gebruikers Omgevingen: Swing /395

156 { JCheckBox cb= new JCheckBox("stip mij aan"); JRadioButton cb2=new JRadioButton("kies mij"); JPanel ca=new MijnPanel(); public void init() {Container c=getcontentpane(); c.setlayout(new BorderLayout()); c.add(cb,borderlayout.south); c.ad d(cb2,borderlayout.north); c. ad d(ca,borderlayout.center); setvisible(true); Om de vector af te lopen werk je met Enumeration e=v.elements(); hasmoreelements en nextelement kunnen gebruikt worden om alle elementen van e af te lopen. Wel moet je het bekomen object casten naar Point. Componenten samen met een tekenveld Omdat de drie radiobutton is een groupbox staan, wordt bij aanklikken de listener 2x opgeroepen: 1x om de oude keuze te 'on-selecten' en 1x om de nieuwe keuze te selecteren tekst verandert alternerend in 'selected' ' not selected' Als je een tekenveld samen met andere componenten wilt hebben, moet je dit tekenveld gewoon als een component behandelen. Je mag zeker geen componenten op het tekenveld zelf zetten. We zullen in dit voorbeeld ook met luisteraars voor JRadioButton s en JCheckButton s werken. We hebben al vroeger gezien dat je met setaction kunt werken. Maar je kunt ook met ItemListeners werken. Deze zullen de routine itemstatechanged moeten definieren. De naam van deze luisteraar is anders dan bij JButton omdat zowel JRadioButton als JCheckButton twee toestanden hebben. Een JButton heeft maar één toestand. Het voordeel om te werken met het algemenere itemlistener is dat er meer dan één luisteraar per component kunnen zijn in tegenstelling tot setaction. Elke keer dat we een radiobutton selecteren, verandert de tekst van de radiobutton door het nummer van selectie toe te voegen achter de vorige waarde. class MijnPanel extends JPanel { int x=0,y=0,oldx,oldy; public MijnPanel() { setbackground(color.yellow); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent e) {x=e.getx();y=e.gety(); ); addmousemotionlistener( new MouseMotionAdapter() { public void mousedragged(mouseevent e) {oldx=x;oldy=y;x=e.getx();y=e.gety(); getgraphics().drawline(oldx,oldy,x,y); Visuele Gebruikers Omgevingen: Swing /395

157 ); public void paintcomponent(graphics g) { super.paintcomponent(g); g.drawrect(10,10,250,200); class Handler implements ItemListener {int i=0; public void itemstatechanged(itemevent e) { JRadioButton but = ((JRadioButton) e.getsource()); String str=but.gettext(); // JOptionPane.showMessageDialog(null,str); but.settext(str+" "+(i++)); public class Swing4C extends JApplet { JCheckBox cb= new JCheckBox("stip mij aan"); JRadioButton cb2=new JRadioButton("kies mij"); JRadioButton cb3=new JRadioButton("kies mij2"); JRadioButton cb4=new JRadioButton("kies mij3"); JPanel panel=new JPanel(); ButtonGroup group =new ButtonGroup(); JPanel c a=new MijnPanel(); public void init() { Container c=getcontentpane(); c.setlayout(new BorderLayout()); c.add(cb,borderlayout.south); panel.add(cb2);panel.add(cb3);p anel.add( cb4); group.add(cb2);group.add(cb3);group.add(cb4); Hand ler h=new Handler(); cb2.additemlistener(h);cb3.additemlistener(h); cb4.additemlistener(h); c.add(panel,borderlayout.north); c.add(ca,borderlayout.center); cb.additemlistener( new ItemListener() {public void itemstatechanged(itemevent e) {if(e.getstatechange()==itemevent.selected) cb.settext("selected");else cb.settext("not selected"); ); setvisible(true); We werken ook met een ButtonGroup. De drie radiobuttons worden toegevoegd aan deze buttongroup. Hierdoor worden bij selectie van één radiobutton gewoon de anderen ge-unselecteerd. Zonder dat je dit moet programmeren. Bij de checkbox gaan we in zijn luisteraar nagaan wat de toestand is met getstatechange. Visuele Gebruikers Omgevingen: Swing /395

158 Properties Hashtable import java.util.*; public class TestHashtable { public static void main(string args[]) { Hashtable ht = new Hashtable(); ht.put("jackson Palmer", new Integer(1051)); ht.put("lisa Reid", new Integer(5678)); ht. put("cheryl Spada", new Integer(2345)); System.out.println("size of Hashtable is " + ht.size()); String str = "Jackson Palmer"; if (ht.containskey(str)) { System.out.println(str + "'s account number is " + ht.get(str)); size of Hashtable is 3 Jackson Palmer's account number is 1051 Een hashtable dient om keys te associëren met waarden. In dit voorbeeld wordt Jackson Palmer verbonden met het Object new Integer(1051). Het kan verbonden worden met gelijk welk Object. Later kan het geassociëerde object teruggevonden worden met get. Indien er geen is, wordt null teruggegeven. U kan ook nagaan of de waarde wel voorkomt met containskey. Er kan dus snel worden teruggezocht in een hashtable. File met properties Uitvoer van het programma: size of Hashtable is 6 titel.slot.einde's account number is bye De waarden kunnen nu op schijf worden bijgehouden. Nu kunnen alleen maar strings worden geassocieerd met de sleutels. inhoud in.properties: #test #Thu Feb 28 14:24:34 CET 2002 titel.slot.einde=bye titel.vraag=hoe gaat het? titel.naam=goede morgen import java.util.*; import java.io.*; public class TestProperties { public static void main(string args[]) { Properties ht = new Properties(); Visuele Gebruikers Omgevingen: Swing /395

159 try { FileInputStream input; input=new FileInputStream("in.properties"); ht.load(input); catch(ioexception ex){ ex.printstacktrace(); ht.setproperty(" titel.naam", "Goede morgen"); ht.setproperty("titel.vraag", "Hoe gaat het?"); ht.setproperty("titel.slot.einde", "bye"); System.out.println("size of Hashtable is " + ht.size()); String str = "titel.slot.einde"; if (ht.containskey(str)) { System.out.println(str + "'s account number is " + ht.getproperty(str)); try { FileOutputStream output; output=new FileOutputStream(" in.properties"); ht.store(output,"test"); catch(ioexception ex){ ex.printstacktrace(); Property erft over van Hashtable. Toch werk je best met setproperty in plaats van met put. Dit omdat je eigenlijk alleen maar strings mag associeren met een sleutel. (Put zou alle Objecten toelaten als waarde en dit zou tijdens de uitvoering een fout geven.) Je kunt een Property eerst laden van schijf met de methode load en een FileInputStream. Op het einde kan je de waarden ook saven met store en een FileOutputStream. ResourceBundle Er is een eenvoudigere wijze om property files te lezen: beschouw ze als ResourceBundle. Het onderscheid met Property is dat je ze alleen maar kunt lezen. Als je nu een naam van een file opgeeft, moet je properties als achtervoegsel weglaten. Goede morgen Hoe gaat het? bye Exception in thread "main" java.util.missingresourceexception: Can't find resour ce for bundle java.util.propertyresourcebundle, key bestaat niet at java.util.resourcebundle.getobject(resourcebundle.java:382) at java.util.resourcebundle.getstring(resourcebundle.java:354) at TestResourceBoundle.main(TestResourceBoundle.java:12) import java.util.*; import java.io.*; public class TestResourceBoundle { public static void print(string s){system.out.println(s); public static void main(string args[]) { ResourceBundle rb = ResourceBundle.getBundle("in"); print(rb.getstring("titel.naam")); print(rb.getstring("titel.vraag")); print(rb.getstring("titel.slot. einde")); print(rb.getstring("bestaat niet")); Visuele Gebruikers Omgevingen: Swing /395

160 Eenvoudigere wijze om eigen events te maken. In vorig voorbeeld werd duidelijk dat er toch heel wat klassen moesten gemaakt worden voor een eigen event: GokEvent, GokListener, programmeren van een Vector. Daarom werd de event propertychange voorzien. Hiermee kunt u alle luisteraars voorzien als een eigenschap veranderd. U mag de naam van deze eigenschap zelf kiezen. U mag bijvoorbeeld een eigenschap stemming of gemoedstoestand nemen. PropertyChange event import java.beans.*; public class X {java.beans.propertychangesupport pcs; private String gemoed,oldgemoed; public X(){pcs=new PropertyChangeSupport(this); gemoed= goed gezind ; public void addpropertylistener(propertychangelistener pcl) { pcs.addpropertychangelistener(pcl); public void removepropertylistener(propertychangelistener pcl) { pcs.removepropertychangelistener(pcl); public void setgemoed(string newgemoed) {oldgemoed=gemoed; gemoed=newgemoed; pcs.firepropertychange( gemoed,oldgemoed,newgemoed); // doe iets met het nieuw gemoed naargelang de toepassing Roept de routine propertychange op class Luisteraar implements PropertyChangeListener van elke luisteraar. { public void propertychange(propertychangeevent e) {// doe iets met e.getoldvalue(), e.getnewvalue(), e.getpropertyname() We hebben een property gemoed. Als deze verandert, zullen alle luisteraars verwittigd worden doordat hun routine propertychange zal worden opgeroepen. We gaan nu zelf geen Vector gebruiken. Omdat propertychange events veel voorkomen, hebben ze een klasse PropertyChangeSupport voorzien die deze Vector voor ons bijhoudt. De routine firepropertychange van die klasse zal heel de interne vector aflopen, een object van de klasse PropertyChangeEvent maken en dit object doorgeven aan de routine propertychange van elke luisteraar. Visuele Gebruikers Omgevingen: Swing /395

161 Toepassingen los koppelen met propertychange Venster1: drie properties Toepassing1 implements PropertyChangeListener PropertyChange(PropertyChangeEvent e) {e.getoldvalue() e.getnewvalue() e.getpropertyname(); addpropertychangelistener removepropertychangelistener bij verandering van properties: firepropertychange Toepassing2 implements PropertyChangeListener PropertyChange(PropertyChangeEvent e) {e.getoldvalue() e.getnewvalue() e.getpropertyname(); U kunt bijvoorbeeld een apart venster in uw toepassing hebben om bepaalde eigenschappen in te geven. U hebt twee toepassingen die gebruik maken van deze eigenschappen. De twee toepassingen worden als PropertyChangeListener geregistreerd bij het eigenschappen-venster. Telkens deze eigenschappen zich wijzigen zullen de twee toepassingen hiervan verwittigd worden. Uw toepassing is hierdoor modulairder opgebouwd. Visuele Gebruikers Omgevingen: Swing /395

162 VetoableChangeListener import java.beans.*; public class X {java.beans.propertychangesupport pcs; java.beans.vetoablechangesupport vcs; private String gemoed,oldgemoed; pub lic X(){pcs=new PropertyChangeSupport(this); vcs=new VetoableChangeSupport(this); gemoed= goed gezind ; public void addvetoablechangelistener(vetoablechangelistener pcl) { vcs.addvetoablechangelistener(pcl); public void removevetoablechangelistener(vetoablechangelistener pcl) { vcs.removevetoablechangelistener(pcl); public void addpropertylistener(propertychangelistener pcl) { pcs.addpropertychangelistener(pcl); public void removepropertylistener(propertychangelistener pcl) { pcs.removepropertychangelistener(pcl); public void setgemoed(string newgemoed) { oldgemoed=gemoed; try {vcs.firevetoablechange( gemoed,oldgemoed,newgemoed); gemoed=newgemoed; pcs.firepropertychange( gemoed,oldgemoed,newgemoed); catch(propertyvetoexception e){ // doe iets met het nieuw gemoed naargelang de toepassing class Luisteraar implements VetoableChangeListener { public void vetoablechange(propertychangeevent e) throws PropertyVetoException {// doe iets met e.getoldvalue(), e.getnewvalue(), e.getpropertyname() bij niet akkoord gaan met de voorgestelde wijziging: throw new PropertyVetoException( slecht gemoed,e); Roept de routine vetoablechange op van elke luisteraar. Visuele Gebruikers Omgevingen: Swing /395

163 VetoableChangeListeners worden verwittigt indien iemand probeert een property te wijzigen. Ze worden verwittigd doordat hun routine vetoablechange wordt opgeroepen. Deze routine kan echter een exceptie werpen. Wat gebeurt er indien er een exceptie wordt geworpen? De routine die de luisteraar verwittigde zal dan onderbroken worden. Hoe dit begrijpen? De kern zit hem in de try-catch blok: try {vcs.firevetoablechange( gemoed,oldgemoed,newgemoed); gemoed=newgemoed; pcs.firepropertychange( gemoed,oldgemoed,newgemoed); catch(propertyvetoexception e){ vcs.firevetoablechange zal iedere luisteraar verwittigen door de routine vetoablechange op te roepen. Indien deze echter een exceptie werpt, zal er uit het try-catch blok worden gesprongen en zullen de overblijvende instructies ( gemoed=newgemoed; pcs.firepropertychange( gemoed,oldgemoed,newgemoed);) niet meer uitgevoerd worden. Pas indien geen enkele luisteraar van het type VetoableChangeListener een exceptie geworpen heeft, zal gemoed worden gewijzigd en zullen alle geregistreerde PropertyChangeListeners verwittigd worden dat de property daadwerkelijk gewijzigd werd. Visuele Gebruikers Omgevingen: Swing /395

164 Voorbeeld van PropertyChangeListener Er zijn properties (background, foreground, font) die bij wijziging andere (luisteraars) automatisch verwittigen. Sommige properties (eigenschappen die kunnen veranderd worden met set en opgevraagd worden met get ) verwittigen al hun luisteraars indien ze veranderen. De foreground, background color en font zijn zulke properties. De geregistreerde PropertyChangeListeners zullen verwittigd worden als één van bovengenoemde properties verandert. Een property XXX zal steeds een getxxx functie hebben om zijn waarde op te vragen en meestal een setxxx functie om zijn waarde te veranderen. Telkens de property XXX verandert (doordat iemand setxxx heeft opgeroepen) zullen de luisteraars (PropertyChangeListeners) verwittigd worden. Dit is zo automatisch voor de properties foreground, background en font. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.beans.*; // BackGround, foreground en Font zijn bound properties. // luisteraars worden verwittigd indien er een wijziging // optreedt. class EigenschapLuisteraar implements PropertyChangeListener {public void propertychange(propertychangeevent e) Visuele Gebruikers Omgevingen: Swing /395

165 {JOptionPane.showMessageDialog(null, "algemene luisteraar "+ e.getpropertyname()+"\n old: "+e.getoldvalue() +" new: " +e.getnewvalue() ); public class Swing21 extends JApplet {JTextField field=new JTextField(10); JButton but1=new JButton("achtergrond groen"), but2=new JButton("achtergrond geel"), but3=new JButton("letters rood"), but4=new JButton("letters blauw"); public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); c.add(field); c.add(but1); c.add(but2); c.add(but3); c.add(but4); but1.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {field.setbackground(color.green);); but2.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {field.setbackground(color.yellow);); but3.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {field.setforeground(color.red);); but4.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {field.setforeground(color.blue);); field.addpropertychangelistener(new EigenschapLuisteraar()); Visuele Gebruikers Omgevingen: Swing /395

166 Exceptions public class Exception01 {static public void main(string args[]) {routine1(); static public void routine1() {routine2(); static public void routine2() {routine3(); static public void routine3() {int i=0; int j=0; int k; k=i/j; java.lang.arithmeticexception: / by zero at Exception01.routine3(Exception01.java:19) at Exception01.routine2(Exception01.java:13) at Exception01.routine1(Exception01.java:10) at Exception01.main(Exception01.java:7) Exception in thread "main" Process Exit... public class Exception01 {static public void main(string args[]) {routine1(); static public void routine1() {routine2(); static public void routine2() {routine3(); static public void routine3() {int i=0; int j=0; int k=999; try{ k =i/j; catch(exception a) {System.out.println("fout:"+a.getMessage()); fout:/ by zero public class Exception01 {static public void main(string args[]) {routine1(); static public void routine1() {routine2(); static public void routine2() {try{routine3(); catch(exception a) {System.out.println("fout:"+a.getMessage()); System.out.println("de fout trad op in : "); a.printstacktrace(); System.out.println("dit wordt nog uitgevoerd"); System.out.println("en dit ook"); static public void routine3() {int i=0; int j=0; int k=999; k=i/j; fout:/ by zero de fout trad op in : java.lang.arithmeticexception: / by zero at Exception01.routine3(Exception01.java:26) at Exception01.routine2(Exception01.java:13) at Exception01.routine1(Exception01.java:10) at Exception01.main(Exception01.java:7) Visuele Gebruikers Omgevingen: Swing /395

167 dit wordt nog uitgevoerd en dit ook public class Exception01 {s tatic public void main(string args[]) {try{routine1(); catch(exception a) {System.out.println("fout:"+a.getMessage()); System.out.println("de fout trad op in : ") ; a.printstacktrace(); System.out.println("dit wordt nog uitgevoerd"); System.out.println("en dit ook"); st atic public void routine1() {routine2(); System.out.println("dit wordt NIET uitgevoerd"); static public void routine2() {routine3(); System.out.println("dit wordt NIET uitgevoerd"); static public void routine3() {int i=0; int j=0; int k=999; k=i/j; System.out.println("dit wordt NIET uitgevoerd"); fout:/ by zero de fout trad op in : java.lang.arithmeticexception: / by zero at Exception01.routine3(Exception01.java:28) at Exception01.routine2(Exception01.java:21) at Exception01.routine1(Exception01.java:17) at Exception01.main(Exception01.java:7) dit wordt nog uitgevoerd en dit ook class MijnException extends Exception {public MijnException() {super("we delen door een te klein getal: we verliezen precisie"); public class Exception01 {static public void main(string args[]) {try{routine1(); System.out.println("dit wordt NIET uitgevoerd"); catch(mijnexception a) {System.out.println("fout:"+a.getMessage()); System.out.println("dit wordt nog uitgevoerd"); System.out.println("en dit ook"); static public void routine1() throws MijnException {routine2(); System.out.println("dit wordt NIET uitgevoerd"); static public void routine2() throws MijnException {routine3(); System.out.println("dit wordt NIET uitgevoerd"); static public void routine3() throws MijnException {int i=0; int j=0; int k=999; if( j<0.0001) throw new MijnException(); else k=i/j; System.out.println("dit wordt NIET uitgevoerd"); Visuele Gebruikers Omgevingen: Swing /395

168 fout:we delen door een te klein getal: we verliezen precisie dit wordt nog uitgevoerd en dit ook import javax.swing.*; import java.awt.event.*; import java.awt.*; import java.text.*; class MijnException extends Exception {public MijnException(){super("deler is te groot"); class delen extends JFrame {public delen( ) {super("we d elen 5 door een door ons te kiezen getal"); Container c= getcontentpane(); final JLabel lab=new JLabel("geef deler"); final JTextField field=new JTextField(5); final JTextField result=new JTextField(10); final DecimalFormat precision3=new DecimalFormat("0.000"); result.seteditable(false); c.setlayout(new FlowLayout()); c.add(lab);c.add( field);c.add(result); field.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {try{ int getal=integer.parseint(field.gettext()); result.settext(precision3.format(deel(5,getal)) ); catch(numberformatexception nfe) {JOptionPane.showMessageDialog(null,"geef getal"); catch(mijnexception me) {JOptionPane.showMessageDialog(null,me.getMessage()); catch(exception ee){system.out.println(ee.getmessage()); finally // wordt steeds uitgevoerd {// resources vrijgeven die in de try werden gebruikt ); setvisible(true); pack(); public int deel(int a,int b) throws MijnException {if( b>10) throw new MijnException(); else return a/b; public class Exception02 { public static void main(string args[]) {new delen(); Gewoon drie tellers W e hebben een klasse Teller die drie button heeft om de teller te verhogen, verlagen en terug op nul te zetten. Van deze klasse maken we drie objecten. Visuele Gebruikers Omgevingen: Swing /395

169 class Teller extends JPanel {JButton but1=new JButton("+"), but2=new JButton("-"), but3=new JButton("0"); JLabel lab=new JLabel("0"); int waarde=0; public Teller() {setlayout(new FlowLayout()); add(but1);add(but2);add(but3);add(lab); but1.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {waarde++;displaywaarde();); but2.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {waarde--;displaywaarde();); but3.addactionlistener(new ActionListener() {public void actionperformed(actionevent e ) {waarde=0;displaywaarde();); public void displaywaarde() {lab.settext(""+waarde); public class Swing22 extends JApplet {Teller teller1,teller2,teller3; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); teller1=new Teller();c.add(teller1); teller2=new Teller();c.add(teller2); Teller();c.add(teller3); teller3=new Drie tellers, maar nu met propertychangelisteners (zelf gemaakt). In dit voorbeeldje hebben we drie tellers met elk een bijhorende Staaf die visueel de grootte van de teller weergeeft. Telkens een teller van waarde verandert, zal zijn luisteraar verwittigd worden en hertekend worden. Dit voorbeeldje heeft drie tellers en elke teller heeft een property waarde teller. Eigenlijk zouden we ook functies setwaardeteller en getwaardeteller moeten hebben voor een complete property. Maar die hebben we even weggelaten. Elke keer dat een teller van waarde verandert, zal het zijn luisteraars verwittigen. Hiervoor houden we een array van luisteraars bij. Maar in plaats van zelf deze array te programmeren maken we gebruikt van de klasse PropertyChangeSupport. Deze klasse beheert een array van PropertyChangeListeners. De klasse PropertyChangeSupport heeft een methode Visuele Gebruikers Omgevingen: Swing /395

170 firepropertychange die voor alle luisteraar in de verborgen array de methode propertychange zal oproepen. Ook heeft deze klasse de methodes addpropertychangelistener en removepropertychangelistener. Hiermee kunnen we aan de klasse Teller ook methodes addpropertychangelistener en removepropertychangelistener toevoegen. (We moeten toch nieuwe luisteraar kunnen toevoegen. Het enige dat je moet onthouden van PropertyChangeSupport is : het omvat een interne array van objecten die de interface PropertyChangeListener implementeren. Dit houdt in dat deze objecten een routine propertychange moeten hebben. Met behulp van de routine firepropertychange, zal heel de interne array worden afgelopen en zal voor elk object in de array de routine propertychange worden opgeroepen. class Staaf extends JPanel implements PropertyChangeListener {int hoogtestaaf=0; public Staaf() {setpreferredsize(new Dimension(150,30)); public void paint(graphics g) { g.clearrect(0,0,150,30); g.setcolor(color.blue); g.fillrect(0,0,hoogtestaaf*5,30); public void propertychange(propertychangeevent e) {if(e.getpropertyname().equals("waarde teller")) {hoogtestaaf=((integer)e.getnewvalue()).intvalue() ; repaint(); class Teller extends JPanel {JButton but1=new JButton("+"), but2=new JButton("-"), but3=new JButton("0"); JLabel lab=new JLabel("0"); int waarde=0; PropertyChangeSupport luisteraars= new PropertyChangeSupport(this); public void addpropertychangelistener(propertychangelistener l) {luisteraars.addpropertychangelistener(l); pu blic void removepropertychangelistener(propertychangelistener l) Visuele Gebruikers Omgevingen: Swing /395

171 {luisteraars.removepropertychangelistener(l); public Teller() {setlayout(new FlowLayout()); add(but1);add(but2);add(but3);add(lab); but1.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {luisteraars.firepropertychange("waarde teller", new Integer(waarde), new Integer(waarde+1)); waarde++; displaywaarde();); but2.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {luisteraars.firepropertychange("waarde teller", new Integer(waarde), new Integer(waarde-1)); waarde--;displaywaarde();); but3.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {luisteraars.firepropertychange("waarde teller", new Integer(waarde), new Integer(0)); waarde=0;displaywaarde();); public void displaywaarde() {lab.settext(""+waarde); public class Swing23 extends JApplet {Teller teller1,teller2,teller3; Staaf staaf1,staaf2,staaf3; public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); teller1=new Teller();c.add(teller1); teller2=new Teller();c.add(teller2); teller3=new Teller();c.add(teller3); staaf1=new Staaf();c.add(staaf1); staaf2=new Staaf();c.add(staaf2); staaf3=new Staaf();c.add(staaf3); teller1.addpropertychangelistener(staaf1); teller2.addpropertychangelistener(staaf2); teller3.addpropertychangelistener(staaf3); De klasse Staaf heeft een routine propertychange en implementeert hierdoor de interface PropertyChangeListener. Objecten van de klasse Staaf kunnen aldus met de routine addpropertychangelistener als luisteraar geregistreerd worden bij de klasse Teller. Wie heeft er een veto voor een verandering waarde? van een In dit voorbeeldje gaan we een stap verder. We hebben een veldje waarin een waarde kan ingevuld worden. Meerdere luisteraars zullen verwittigd worden indien u de waarde verandert. Het verschil met het vorig voorbeeld is dat we nu werken met VetoableChangeListener i.p.v. PropertyChangeListener VetoableChangeSupport i.p.v. PropertyChangeSupport vetoablechange i.p.v. propertychange firevetoablechange i.p.v. firepropertychange Het woordje property werd vervangen door vetoable (het stellen van een veto). Weeral kunnen meerdere luisteraar geregistreerd worden. Ze worden verwittigd indien een property van waarde verandert, maar kunnen nu een veto stellen door het werpen van een exception. Visuele Gebruikers Omgevingen: Swing /395

172 Elke luisteraar moet nu een routine vetoablechange implementeren. Deze routine moet de exception PropertyVetoException kunnen werpen indien de nieuwe waarde van de property niet goed is. Het gevolg hiervan is dat de routine firevetoablechange (die voor alle geregistreerde objecten de routine vetoablechange zal oproepen), kan onderbroken worden door een exceptie. U moet daarom (verplicht) rond de oproep van de routine firevetoablechange een try catch zetten om de exceptie op te vangen. De oproep firevetoablechange ziet er typisch als volgt uit. try{ luisteraars.firevetoablechange("waarde teller", new Integer( waarde), new Integer(nieuwewaarde)); // de volgende instructies worden alleen maar uitgevoerd // indien niemand een veto stelt. waarde=nieuwewaarde; field.settext(""+waarde); catch(propertyvetoexception e) {field.settext("veto!!"); De luisteraars worden verwittigd door firevetoablechange die voor elke luisteraar de routine vetoablechange zal oproepen. Bij een veto worden de instructies achter de oproep van firevetoablechange niet meer uitgevoerd. Deze instructies gaan juist de nieuwe waarde toekennen aan de veranderlijke die hoort bij de property. (hier is dat waarde). Bij een veto zal de veranderlijke waarde dus geen nieuwe waarde krijgen en zal (door de catch) de tekst veto in het veldje verschijnen. Bovenaan staat een veldje TELLER. De waarde van de teller kan gewijzigd worden met behulp van setteller. De klasse TELLER heeft echter een routine addvetoablechangelistener. De routine setteller zal alle VetoableChangeListeners verwittigen. Onderaan de toepassing hebben we een veldje geef nieuwe waarde van teller. Hier kunt u een nieuwe waarde intypen. We hebben drie visuele luisteraars: objecten van een klasse VetoValueController. U kunt hier gewoon een waarde intypen. Deze waarde (die u steeds kunt veranderen) zullen niet aanvaard worden.) We hebben ook een niet visuele luisteraar: een object die nooit de waarde 13 (brengt ongeluk) zal toelaten. van de klasse Keurder import javax.swing.*; Visuele Gebruikers Omgevingen: Swing /395

173 import java.awt.*; import java.awt.event.*; import java.beans.*; class VetoValueController extends JPanel implements VetoableChangeListener {int VetoValue=0; JLabel lab=new JLabel("Geblokkeerde waarde:"); JTextField field=new JTextField(5); JTextField inputfield=new JTextField(5); public VetoValueController() { add(lab);add(field);add(inputfield); field.seteditable(false); setpreferredsize(new Dimension(150,50)); inputfield.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { String ingetypt = e.getactioncommand(); VetoValue=Integer.parseInt(ingetypt); field.settext(ingetypt); ); public void vetoablechange(propertychangeevent e) throws PropertyVetoException {if(e.getpropertyname().equals("waarde teller")) {int hulp = ((Integer)e.getNewValue()).intValue() ; if(hulp==vetovalue) throw new PropertyVetoException("neen",e); class Keurder implements VetoableChangeListener { public void vetoablechange(propertychangeevent e) throws PropertyVetoException {if(e.getpropertyname().equals("waarde teller")) {int hulp=((integer)e.getnewvalue()).intvalue() ; if( hulp ==13)//ongeluk? throw new PropertyVetoException("13 brengt ongeluk",e); class Teller extends JPanel {JTextField field=new JTextField(10); JLabel lab=new JLabel("TELLER :"); int waarde=0; VetoableChangeSupport luisteraars= new VetoableChangeSupport(this); public void addvetoablechangelistener(vetoablechangelistener l) {luisteraars.addvetoablechangelistener(l); public void removevetoablechangelistener(vetoablechangelistener l) {luisteraars.removevetoablechangelistener(l); public Teller() {setlayout(new FlowLayout()); add(lab);add(field);field.seteditable(false); public void setteller(int nieuwewaarde) { // met firevetoablechange worden alle luisteraars verwittigd. // ze mogen een veto stellen, indien dit zo is worden de // laatste bevelen van de try-catch clausule niet uitgevoerd. try{ luisteraars.firevetoablechange("waarde teller", new Integer(waarde), new Integer(nieuwewaarde)); // de volgende instructies worden alleen maar uitgevoerd // indien niemand een veto stelt. waarde=nieuwewaarde; field.settext(""+waarde); Visuele Gebruikers Omgevingen: Swing /395

174 catch(propertyvetoexception e) {field.settext("veto!!"); public class Swing24 extends JApplet {Teller teller1; VetoValueController controller1,controller2,controller3; JLabel boodschap=new JLabel(); JLabel l=new JLabel("geef nieuwe waarde teller: "); JTextField field = new JTextField(10); JPanel pp=new JPanel(); public void init() {Container c=getcontentpane(); c.setlayout(new FlowLayout()); teller1=new Teller();c.add(teller1); controller1=new VetoValueController();c.add(controller1); controller2=new VetoValueController();c.add(controller2); controller3=new VetoValueController();c.add(controller3); teller1.addvetoablechangelistener(controller1); teller1.addvetoablechangelistener(controller2); teller1.addvetoablechangelistener(controller3); pp.add(l);pp.add(field);c.add(pp); c.add( boodschap); // zou het volgende lukken? teller1.setteller(-1); // slechts vanaf nu zal er op 13 worden getest bij teller1 teller1.addvetoablechangelistener(new Keurder()); field.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { String ingetypt = e.getactioncommand(); int waarde=integer.parseint(ingetypt); // zou het lukken? teller1.setteller(waarde);// dit kan al dan niet lukken ); Visuele Gebruikers Omgevingen: Swing /395

175 Threads import java.awt.*; import javax.swing.*; import java.awt.event.*; class MijnFrame extends JFrame {int rij=15; public MijnFrame() {super(); setbounds(5,5,400,500); setvisible(true); //wachten noodzakelijk anders wordt // scherm opgebouwd terwijl threads // lopen en verdwijnen enkele lijnen try {Thread.sleep(3000); // we wachten 3 seconden catch(interruptedexception e) {System.out.println("thread"+ "interrupted"); new Thread( new Runnable( ) {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,50,rij+=15); ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i+ +) getgraphics().drawstring(""+i,150,rij+=15); ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,250,rij+=15); ).start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e) {System.exit(0); ); public class Thr01 {public static void main(string arg[]) {new MijnFrame(); Er worden drie nieuwe threads opgestart. De code in elke thread wordt gelijktijdig met de andere threads uitgevoerd. Een thread stelt een nieuw proces voor Stel dat u drie blokken code hebt: - Blok code A - Blok code B Visuele Gebruikers Omgevingen: Swing /395

176 - Blok code C Stel dat u, nadat blok code A werd uitgevoerd, de uitvoering van blok code B en C gelijktijdig moet gebeuren, dan moet u de code van blok B in een thread steken. - Blok code A - Thread t=new thread(new B() ); - t.start(); - Blok code C public class B implements Runnable {public void run() { code blok B De interface Runnable is erg eenvoudig: Public interface Runnable { public void run(); De code die door het apart proces moet uitgevoerd worden staat dus in een routine run. Dat is de enige voorwaarde. Aan de constructor van de klasse Thread moet steeds een Runnable object doorgegeven worden. Als de routine start wordt uitgevoerd, zal een nieuw proces worden opgestart en zal de routine run, van het object dat doorgegeven werd aan de constructor, uitgevoerd worden. Nadat het proces werd opgestart, gaat de uitvoering van het normale programma dadelijk door. Er wordt niet gewacht. Hierdoor wordt de code van blok B en C gelijktijdig uitgevoerd. Uitvoer vorig programma Er verschijnen drie kolommen naast elkaar op het scherm. Elke kolom komt overeen met een thread. De eerste thread zal steeds in kolom 50 schrijven, de tweede thread steeds in kolom 150 en de derde kolom steeds in kolom 250. In vorig programma werden geen nieuwe klassen gebruikt die de interface Runnable implementeren, maar werden naamloze innerklassen gebruikt. Deze innerklassen kunnen wel aan de gemeenschappelijke veranderlijken van de omliggende klasse aan. Zo kan de veranderlijke rij benaderd worden vanuit de drie processen. Telkens een proces 15 optelt bij de veranderlijke rij, zal het volgend getal lager worden geschreven. Belangrijk: Als processen gelijktijdig lopen weet u niet welke bevelen eerst zullen worden uitgevoerd. Neem nu het bevel getgraphics().drawstring(""+i,250,rij+=15); Dit zijn eigenlijk twee bevelen: rij+=15;//bevel A getgraphics().drawstring(""+i,250,rij+=15);// bevel B Tussen de uitvoering van de eerste instructie en de uitvoering van de tweede instructie, is het mogelijk dat een andere thread ook instructies uitvoert, en bijvoorbeeld de rij eerst ook nog eens verhoogd, of eerst nog eens een getal uitschrijft in een andere kolom (van dezelfde rij). Visuele Gebruikers Omgevingen: Swing /395

177 Elke keer u bovenstaand programma uitvoert, zal de uitvoering anders zijn. Het hangt van het uitbatingssysteem af in welke concrete volgorde de processen worden uitgevoerd. Als u niet opgeeft, hebben threads prioriteit 5. U kunt deze prioriteit wijzigen. (Hiervoor moet u gaan kijken bij de klasse Thread). Het is een hele krachttoer van java om zulke systeemafhankelijke dingen als processen machineonafhankelijk te programmeren. Elk uitbatingssysteem heeft meestal een andere wijze waarop de tijd tussen verschillende processen wordt verdeeld. U moet u inbeelden dat bovenstaand programma uit drie maal 20 instructies bestaat (10x2). U schrijft deze drie sets van instructies in drie kolommen naast elkaar en vervolgens begint u bovenaan elk stapeltje naar beneden toe. U kunt afwisselend instructies van de eerste, tweede of derde stapel uitvoeren. Altijd in een andere volgorde. U kunt bijvoorbeeld eerst 5 instructies van stapel één uitvoeren en dan 2 instructies van stapel twee en dan nog eens drie instructies van stapel drie. Andere versies van hetzelfde programma Een expliciete innerklasse gebruiken class MijnFrame02 extends JFrame {int rij=15; private class EersteKolom implements Runnable {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,50,rij+=15); public MijnFrame02() {super(); setbounds(5,5,400,500); setvisible(true); Thread one=new Thread(new EersteKolom()); one.start(); new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) getgraphics().drawstring(""+i,150,rij+=15); ).start(); new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) getgraphics().drawstring(""+i,250,rij+=15); ).start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0); ); public class Thr02 { public static void main(string arg[]){new MijnFrame02(); De klasse EersteKolom is een innerklasse. Het blijft noodzakelijk dat het een innerklasse is omdat alleen een innerklasse aan de gemeenschappelijke veranderlijke rij aan kan. De hoofdklasse implementeert zelf de interface Runnable class MijnFrame03 extends JFrame implements Runnable {int rij=15; public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,50,rij+=15); Visuele Gebruikers Omgevingen: Swing /395

178 public MijnFrame03() {super(); setbounds(5,5,400,500); setvisible(true); Thread one=new Thread(this); one.start(); new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) getgraphics().drawstring(""+i,150,rij+=15) ; ).start(); new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) getgraphics().drawstring(""+i,250,rij+=15) ; ).start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0); ); Dit is een speciaal geval. De hoofdklasse implementeert zelf de interface Runnable en heeft zelf dus een routine run. Omdat we aan de bijhorende thread een referentie naar een object Runnable moeten doorgeven, moeten we hier this doorgeven aan de constructor van de klasse Thread. Weeral kan deze routine run aan de gemeenschappelijke veranderlijke rij. Visuele Gebruikers Omgevingen: Swing /395

179 yield: voorrang geven aan anderen class MijnFrame04 extends JFrame {int rij=15; public MijnFrame04() {super(); setbounds(5,5,400,500); setvisible(true); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {getgraphics().drawstring(""+i+"yield",50,rij+=15); Thread.yield(); //Causes the currently executing thread object to //temporarily pause and allow other threads to execute. ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,150,rij+=15); ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,250,rij+=15); ).start(); public class Thr04 {public static void main(string arg[]) {new MijnFrame04(); Visuele Gebruikers Omgevingen: Swing /395

180 De eerste thread zal na het uitvoeren van twee bevelen (rij+=15 en drawstring) even pauzeren en andere wachtende threads voorlaten. Zolang thread twee en drie lopen, zal thread één dus steeds maar die twee bevelen uitvoeren en anderen voorlaten. Join: thread wacht totdat een andere specifieke thread gedaan heeft Thread drie zal na de uitvoering van de helft van zijn bevelen wachten totdat thread twee gedaan heeft. Maar thread twee zal ook na de helft van de uitvoering van zijn bevelen wachten totdat thread één gedaan heeft. In bovenstaande schermafdrukken vindt u twee verschillende uitvoeringen van hetzelfde programma. In beide gevallen zal thread één steeds eerst gedaan hebben, gevolgd door thread twee. Thread drie zal steeds als laatste gedaan hebben. We zullen nu expliciete referenties naar de threads moeten bijhouden. Daarom heeft het programma drie veranderlijken van het type thread: one, two, three. one stelt de eerste thread voor, two de tweede thread, three de derde thread. Hierdoor kunnen we, na de helft van de uivoering van thread three, de instructie two.join() uitvoeren. Hierdoor stopt thread three totdat thread two gedaan heeft. Analoog zal tijdens de uitvoering van thread two de instructie one.join() thread two laten wachten totdat thread one gedaan heeft. Visuele Gebruikers Omgevingen: Swing /395

181 class MijnFrame05 extends JFrame {int rij=15; Thread one,two,three; public MijnFrame05() {super(); setbounds(5,5,400,500); setvisible(true); one=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,50,rij+=15);); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,150,rij+=15); if(i==5) {getgraphics().drawstring("join one",165,rij); try{one.join();// waits for thread one to die. getgraphics().drawstring("joined one",165,rij); catch(interruptedexception e) {System.out.println(e); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,250,rij+=15); if(i==5) {getgraphics().drawstring("join two",265,rij); try{two.join(); getgraphics().drawstring("joined two",265,rij); catch(interruptedexception e) {System.out.println(e); ); three.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr05 {public static void main(string arg[]){new MijnFrame05(); ================================================================ Drie bevelen die na elkaar, on-onderbroken worden uitgevoerd? Soms wil je er zeker van zijn dat enkele instructies in een thread on-onderbroken worden uitgevoerd. We hebben daarom in het volgende programma de drie thread tienmaal de volgende instructies laten uitvoeren: getgraphics().drawstring(""+i,50,rij+=15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); Visuele Gebruikers Omgevingen: Swing /395

182 Dit zijn vier instructies (eenmaal rij+=15 en driemaal drawstring). In een eerste versie laten we alles gelijktijdig uitvoeren. Deze vier instructies kunnen dan op een willekeurige tijdstip onderbroken worden door een andere thread die dan op dezelfde rij gaat schrijven of die vlug even de rij verhoogt. De volgende schermafdruk laat zien dat het zeer zelden gebeurt dat de vier instructies van één thread na elkaar worden uitgevoerd. class MijnFrame06 extends JFrame {int rij=15; Thread one,two,three; public MijnFrame06() {super(); setbounds(5,5,400,500); setvisible(true); one=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,50,rij+ =15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); ); one.start(); two=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,150,rij+=15); getgraphics().drawstring(""+i,160,rij); getgraphics().drawstring(""+i,170,rij); Visuele Gebruikers Omgevingen: Swing /395

183 ); two.start(); three=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,250,rij+=15); getgraphics().drawstring(""+i,260,rij); getgraphics().drawstring(""+i,270,rij); ); three.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e) {System.exit(0);); public class Thr06 {public static void main(string arg[]){new MijnFrame06(); Kunnen de drie bevelen nu niet steeds na elkaar worden uitgevoerd? En dan pas zou een andere thread aan beurt mogen komen. Blijkbaar kan een andere thread op een willekeurig tijdstip onderbroken worden door een andere thread. Hoe kunnen we dit synchroniseren? Het mechanisme dat in Java wordt gebruikt bestaat uit monitors. Elk object in java kan gemonitored worden zodat geen twee routines die aangegeven zijn met synchronized terzelfdertijd het object kunnen accessen. Van zodra een eerste thread een routine of een deel code, aangegeven met synchronized binnengaat, zal het object waarop gesynchronized wordt gemonitored worden: er wordt op toegezien dat geen enkele andere thread synchronized code voor dit zelfde object zal uitvoeren. De uitvoer hierboven zou het verbeterde resultaat moeten zijn. In het programma hieronder wordt een object geentweetegelijk gemaakt. Het is een willekeurig object van de klasse Object. (De concrete Visuele Gebruikers Omgevingen: Swing /395

184 klasse heeft geen belang hier, zolang er maar een monitor mee verbonden kan worden.) Van zodra een thread het blok synchronized(geentweetegelijk){ Binnenkomt, wordt er een ingebruik -vlag geplaatst op het object geentweetegelijk. Bij het verlaten van dit synchronized blok, wordt de vlag van geentweetegelijk terug op vrij gezet. Als een andere thread dan ook tracht van blok binnen te komen dat ook synchronizeert op hetzelfde object geentweetegelijk, dan wordt eerst nagegaan of de vlag op vrij staat. Indien niet, dan blijft deze andere thread wachten totdat de andere thread gedaan heeft. Dan pas kan hij het blok binnengaan en zullen anderen thread moeten wachten. Omdat de drie threads monitor-ren op hetzelfde object, dwingen dat steeds maar één thread tegelijkertijd de gesynchronizeerde code kan uitvoeren. class MijnFrame07 extends JFrame {int rij=15; Thread one,two,three; Object geentweetegelijk=new Object();//een willekeurig object public MijnFrame07() {super(); setbounds(5,5,400,500); setvisible(true); one=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,50,rij+=15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); ); one.start(); two=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,150,rij+=15); getgraphics().drawstring(""+i,160,rij); getgraphics().drawstring(""+i,170,rij); ); two.start(); three=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,250,rij+=15); getgraphics().drawstring(""+i,260,rij); getgraphics().drawstring(""+i,270,rij); ); three.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr07 {public static void main(string arg[]){new MijnFrame07(); Visuele Gebruikers Omgevingen: Swing /395

185 Opdracht Hoe kunnen we bovenstaande uitvoer bekomen? In thread een en drie moeten de vier instructies steeds on-onderbroken worden uitgevoerd. In thread twee moeten alle instructies on-onderbroken worden uitgevoerd. Oplossing: plaats in thread twee heel de for lus in het blok code dat synchroniseert op het object geentweetegelijk. class MijnFrame08 extends JFrame {int rij=15; Thread one,two,three; Object geentweetegelijk=new Object();//een willekeurig object public MijnFrame08() {super(); setbounds(5,5,400,500); setvisible(true); one=new Thread( new Runnable() {public void run(){for( int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,50,rij+=15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); ); one.start(); Visuele Gebruikers Omgevingen: Swing /395

186 two=new Thread( new Runnable() {public void run(){ synchronized(geentweetegelijk) {for(int i=1;i<10;i++) {getgraphics().drawstring(""+i,150,rij+=15); getgraphics().drawstring(""+i,160,rij); getgraphics().drawstring(""+i,170,rij); ); two.start(); three=new Thread( new Runnable() {public void run(){for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,250,rij+=15); getgraphics().drawstring(""+i,260,rij); getgraphics().drawstring(""+i,270,rij); ); three.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr08 {public static void main(string arg[]){new MijnFrame08(); Synchronizeren op twee verschillende objecten Eerst geven we weeral een versie waarbij helemaal niet gesynchronizeerd wordt. Veronderstel twee bankrekeningen. Voor elk van de twee bankrekeningen zijn er twee personen die tezelfdertijd op dezelfde rekening een frank willen storten en dadelijk terug afhalen. Het programma zal vier threads hebben. We gaan aldus vier kolommen getallen zien. Het getal dat we zien in de twee eerste kolommen stelt de waarde van de bankrekening één voor. Het getal dat we zien in de twee laatste kolommen stelt de waarde van de bankrekening twee voor. Thread one en two stellen twee personen voor die gelijktijdig tienmaal de volgende bewerking willen doen op bankrekening één: 1 frank storten en dadelijk 1 frank afhalen. Als dit storten en afhalen ononderbroken zou zijn, krijgen we als uitvoer voor de eerste twee thread steeds : 1 0 of De bankrekening begint bij 0, eerst één frank storten geeft 1, dadelijk 1 frank afhalen geeft terug 0. Als het storten en afhalen on-onderbroken gebeurt krijgen we steeds 1 0 op dezelfde rij van uitvoeren. Visuele Gebruikers Omgevingen: Swing /395

187 Bij niet-gesynchronizeerde uitvoering krijgen we het volgende: Er zijn dus vier thread aan de slag. De twee eerste thread werken met bankrekening één. We zien bijvoorbeeld: 1 eerste thread stort 1 en laat dit zien, tweede thread forceert een nieuwe rij eerste thread haalt 1 frank af en laat dit zien, tweede thread stort één frank en laat zien tweede thread haalt 1 frank af maar voordat dit getoond wordt, zal thread één 1 frank storten (raar resultaat hij haalt 1 frank af maar blijft op 1 staan) Twee andere personen (thread drie en vier) die juist hetzelfde doen, maar nu met een andere gemeenschappelijke rekening. Het eindresultaat is steeds goed (0). De tussenresultaten kunnen beter. Visuele Gebruikers Omgevingen: Swing /395

188 De bankrekening en bijhorende gegeven waarop later gesynchronizeerd zal worden, worden in classe TweeGetallen gestoken. X stelt hierbij de stand van de bankrekening voor. Rij stelt hierbij de rij waarop op het scherm moet geschreven worden voor. Waarom wordt hiervoor een aparte klasse genomen? Later zullen we twee objecten van deze klasse maken en zullen de twee eerste threads synchronizeren op het eerste object en de twee laatste threads op het tweede object (tweede bankrekening). class TweeGetallen {public int x=0,rij=15; class MijnFrame09 extends JFrame { Thread one,two,three,four; TweeGetallen GetalEen=new TweeGetallen(), GetalTwee=new TweeGetallen(); public MijnFrame09() {super(); setbounds(5,5,400,350); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalEen.x++; terug afhalen. getgraphics().drawstring(""+getaleen.x,50,getaleen.rij+=15); GetalEen.x--; getgraphics().drawstring(""+getaleen.x,65,getaleen.rij);); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalEen.x++; getgraphics().drawstring(""+getaleen.x,100,getaleen.rij+=15); GetalEen.x--; getgraphics().drawstring(""+getaleen.x,115,getaleen.rij); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalTwee.x++; getgraphics().drawstring(""+getaltwee.x,200,getaltwee.rij+=15); ); four.start( ); De eerste persoon gaat van de eerste rekening tien maal een frank storten en GetalTwee.x--; getgraphics().drawstring(""+getaltwee.x,215,getaltwee.rij); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalTwee.x++; getgraphics().drawstring(""+getaltwee.x,250,getaltwee.rij+=15); GetalTwee.x--; getgraphics().drawstring(""+getaltwee.x,265,getaltwee.rij); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); Visuele Gebruikers Omgevingen: Swing /395

189 public class Thr09 {public static void main(string arg[]){new MijnFrame09(); De eerste twee threads zullen gelijktijdig tienmaal de volgende instructies uitvoeren: GetalEen.x++; getgraphics().drawstring(""+getaleen.x,50,getaleen.rij+=15); GetalEen.x--; getgraphics().drawstring(""+getaleen.x,65,getaleen.rij); Dit zijn vijf instructies (rij+= 15 is ook een instructie). Deze vijf instructies kunnen op elk moment onderbroken worden door de andere thread die even de bankrekening gaat aanpassen of de rij gaat verhogen of even vlug gaat schrijven in zijn kolom. Thread een en twee werken met GetalEen.x als bankrekening en GetalEen.rij als schrijfrij. De twee andere threads werken met GetalTwee.x en GetalTwee.rij. Oplossing: U moet telkens twee threads synchronizeren op hun gemeenschappelijke rekening. Hieronder wordt gesynchronizeerd op GetalEen, maar synchronisatie op GetalTwee werd 'vergeten'. Het resultaat is overduidelijk. De twee eerste threads geven nu steeds mooi 1 0 als resultaat. Dit komt omdat de vijf instructies niet niet onderbroken mogen worden in een synchronized blok staan. Thread een en twee synchroniseren beiden op de veranderlijke GetalEen. Als thread een de vijf instructies begint uit te voeren, zal een monitor op het object GetalEen worden gezet. Als dan thread twee ook probeert de vijf instructies uit te voeren voordat thread gedaan heeft, zal thread twee even moeten wachten. Als thread een de vijf instructies heeft uitgevoerd, zal de vlag-monitor voor object GetalEen worden afgezet. Hierdoor kan thread even zijn vlag planten op object GetalEen en verder gaan. class TweeGetallen {public int x=0,rij=15; Visuele Gebruikers Omgevingen: Swing /395

190 class MijnFrame10 extends JFrame { Thread one,two,three,four; TweeGetallen GetalEen=new TweeGetallen(), GetalTwee=new TweeGetallen(); public MijnFrame10() {super(); setbounds(5,5,400,350); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getaleen) {GetalEen.x++; getgraphics().drawstring(""+getaleen.x,50,getaleen.rij+=15); GetalEen.x--; getgraphics().drawstring(""+getaleen.x,65,getaleen.rij); ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getaleen) {GetalEen.x++; getgraphics().drawstring(""+getaleen.x,100,getaleen.rij+=15); GetalEen.x--; getgraphics().drawstring(""+getaleen.x,115,getaleen.rij); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalTwee.x++; getgraphics().drawstring(""+getaltwee.x,200,getaltwee.rij+=15); GetalTwee.x--; getgraphics().drawstring(""+getaltwee.x,215,getaltwee.rij); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {GetalTwee.x++; getgraphics().drawstring(""+getaltwee.x,250,getaltwee.rij+=15); GetalTwee.x--; getgraphics().drawstring(""+getaltwee.x,265,getaltwee.rij); ); four.start(); public class Thr10 {public static void main(string arg[]){new MijnFrame10(); Synchronizeren op alle rekeningen Voor twee concrete rekeningen kunt u nog expliciet de synchronizeren op die twee rekeningen. Visuele Gebruikers Omgevingen: Swing /395

191 Stel dat u echter voor alle rekening-objecten wilt zorgen dat de vijf instructies steeds on-onderbroken zullen worden uitgevoerd. Hoe zorgen we ervoor dat u bij meerdere rekeningen nooit vergeet om een te synchrronizeren op een rekening? Antwoord: plaats de te synchronizeren code bij de data waarop u synchroniseert. De uitvoer zal het volgende zijn en zonder aanpassingen geldt deze uitvoeren voor 100 bankrekeningen, zonder dat u op concrete objecten moet synchronizeren. De vijf instructies werden nu in de klasse TweeGetallen gestoken in een routine stortenhaalaf. Als code staat er eigenlijk: this.x++; gg.drawstring(""+this.x,kol, this.rij+=15); this.x--; gg.drawstring(""+ this.x,kol+15, this.rij); We gaan dus algemeen gaan synchroniseren op het object this. Dit stelt tijdens de uitvoering wel een concreet object voor. Het enige dat u nog moet doen is de routine stortenhaalaf synchronized declareren. Als dan een thread voor een bepaald object tracht deze routine stortenhaalaf uit te voeren, zal een vlag worden gezet op dat object (waar this naar wijst). Als een tweede thread dan tracht om ook die routine uit te voeren voor hetzelfde object, dan moet deze thread wachten totdat de eerste thread gedaan heeft met de uitvoering van de routine en de vlag wegneemt voor dat object. class TweeGetallen {private int x=0,rij=15; public synchronized void stortenhaalaf(graphics gg,int kol) {x++; gg.drawstring(""+x,kol,rij+=15); x--; gg.drawstring(""+x,kol+15,rij); Visuele Gebruikers Omgevingen: Swing /395

192 class MijnFrame11 extends JFrame { Thread one,two,three,four; TweeGetallen GetalEen=new TweeGetallen(), GetalTwee=new TweeGetallen(); public MijnFrame11() {super(); setbounds(5,5,400,350); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) GetalEen.stortenhaalaf(getGraphics(),50); ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) GetalEen.stortenhaalaf(getGraphics(),100); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) GetalTwee.stortenhaalaf(getGraphics(),200); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) GetalTwee.stortenhaalaf(getGraphics(),250); ); four.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0); ); public class Thr11{public static void main(string arg[ ]){new MijnFrame11(); Telkens je een synchronized routine oproept, zal heel het object worden gemonitored zodat geen enkele andere thread met dat object langs een gesynchronizeerde routine kan werken. Omdat het uitschrijven op scherm nu in de klasse TweeGetallen gebeurt, moeten we de waarde van getgraphics en ook de kolom waar moet geschreven worden doorgeven aan de oproep. De ene thread wacht totdat de andere thread een teken geeft Visuele Gebruikers Omgevingen: Swing /395

193 We hebben hier vier threads. We hebben maar één getal dat steeds door de vier threads wordt aangepast. De getallen die we in de schermafdruk hierboven zien, zijn waarden van dezelfde veranderlijke (stelt bijvoorbeeld een bankrekening voor). Twee threads (aangeduid op schermafdruk met ++) willen steeds 1 frank op een rekening zetten. Twee andere threads (aangeduid op schermafdruk met --) willen steeds 1 frank van een rekening halen. We weten echter niet welke thread eerst zal beginnen. Het zou kunnen dat de threads die geld afhalen meer in het begin aan bod komen, zodat de rekening negatief wordt. Stel dat dit niet zou mogen? Hoe kunnen we er nu voor zorgen dat de rekening nooit negatief wordt? Dit kan alleen maar indien de threads die geld van de rekening willen halen, zullen moeten wachten totdat de rekening positief is. Er zijn verschillende mogelijkheden om dit te bekomen. Versie waarbij er geen voorwaarde wordt afgedwongen Hierbij kan de waarde van de rekening dus negatief worden (zie afdruk hierboven). class Getallen {public int x=0,rij=15; class MijnFrame12 extends JFrame Visuele Gebruikers Omgevingen: Swing /395

194 {Thread one,two,three,four; Getallen Getal=new Getallen(); public MijnFrame12() {super(); setbounds(5,5,400,600); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x--; getgraphics().drawstring("--( "+Getal.x+" )--", 50,Getal.rij+=15); ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x++; getgraphics().drawstring("++( "+Getal.x+" )++", 100,Getal.rij+=15); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x--; getgraphics().drawstring("--( "+Getal.x+" ) - ",200,Getal.rij+=15); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x++; getgraphics().drawstring("++( "+Getal.x+" )++", 250,Getal.rij+=15); ); four.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0); ); public class Thr12 {public static void main(string arg[]){new MijnFrame12(); Versie waarbij een thread gaat slapen totdat conditie vervuld is De eenvoudigste wijze om te wachten is de threads die geld willen afhalen een tijdje te laten slapen totdat het bedrag positief is. Dit pollen is wel niet efficiënt. Het zou beter zijn dat de thread die verhoogt verwittigd, wanneer het bedrag positief geworden is. Als een thread even gaat slapen, zal er sleep op het scherm worden getoond. Indien thread een of drie als waarde voor Getal.x gelijk aan nul heeft, zal deze thread wachten totdat deze voorwaarde niet meer geldt. Dan wordt de while lus verlaten en zal Getal.x kunnen uitgevoerd worden. Visuele Gebruikers Omgevingen: Swing /395

195 class Getallen {public int x=0,rij=15; class MijnFrame13 extends JFrame {Thread one,two,three,four; Getallen Getal=new Getallen(); public MijnFrame13() {super(); setbounds(5,5,400,600); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {while(getal.x<=0) {try{getgraphics().drawstring("sleep",50,getal.rij+=15); Thread.sleep(50); catch(interruptedexception e) {System.out.println(e); Getal.x--; getgraphics().drawstring("--( "+Getal.x+" )--", 50,Getal.rij+=15); Visuele Gebruikers Omgevingen: Swing /395

196 ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x++; getgraphics().drawstring("++( "+Getal.x+" )++", 100,Getal.rij+=15); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {while(getal.x<=0) {try{getgraphics().drawstring("sleep",200,getal.rij+=15); Thread.sleep(50); catch(interruptedexception e) Getal.x--; getgraphics().drawstring("--( "+Getal.x+" )--", 200,Getal.rij+=15); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.x++; getgraphics().drawstring("++( "+Getal.x+" )++", 250,Getal.rij+=15); ); four.start(); {System.out.println(e); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr13 {public static void main(string arg[]){new MijnFrame13(); Hierbij zal het proces dus af en toe wakker worden om te kijken of het verder mag gaan. Beter zou zijn dat het proces wakker gemaakt wordt door een ander proces indien de conditie voldaan is. Visuele Gebruikers Omgevingen: Swing /395

197 Zichzelf in een wachttoestand brengen Het is veel efficiënter dat een thread zichzelf in wait zet, wanneer de conditie van een bepaald object niet goed is. Dit gebeurt wel per object waarvoor de conditie niet goed is. In dit programma wordt er gesynchronizeerd op het object Getal. Getal.wait() moet binnen dit synchronized blok uitgevoerd worden. Het bijhorend proces zal dan in een soort slaap toestand terechtkomen (en de monitor-vlag wordt tijdelijk opgeheven) en kan maar wakker gemaakt worden door een ander proces dat ook tijdens de uitvoering van een gesynchroniseerd blok (op dezelfde veranderlijke, hier Getal) de instructie Getal.notify() uitvoert. In het volgende programma, wordt er dus met een synchonized blok code gewerkt. Dit blok zal een monitor op een bepaald object zetten. wait instructies mogen alleen maar uitgevoerd worden vanuit gemonitored blok. De wait en notify instructie behoren tot de klasse Object. U kunt dus threads laten wachten op gelijk welk object totdat een andere thread de conditie voor dat bepaald object goed gezet heeft en dan een notify voor datzelfde object doet. class Getallen {public int x=0,rij=15; Visuele Gebruikers Omgevingen: Swing /395

198 class MijnFrame14 extends JFrame {Thread one,two,three,four; Getallen Getal=new Getallen(); public MijnFrame14() {super(); setbounds(5,5,400,600); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getal) {if(getal.x<= 0) {try{getgraphics().drawstring("wait",50,getal.rij+=15); ); one.start(); Getal.wait(); catch(interruptedexception e) {System.out.println(e); Getal.x--; getgraphics().drawstring("--( "+Getal.x+" )--", 50,Getal.rij+=15); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getal) {Getal.x++; if(getal.x>0) { Getal.notify(); getgraphics().drawstring("++( "+Getal.x+" )++", 100,Getal.rij+=15); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getal) {if(getal.x<=0) {try{getgraphics().drawstring("wait",200,getal.rij+=15); Getal.wait(); catch(interruptedexception e) Getal.x--; getgraphics().drawstring("--( "+Getal.x+" )--", 200,Getal.rij+=15); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(getal) {Getal.x++; if(getal.x>0) { Getal.notify(); getgraphics().drawstring("++( "+Getal.x+" )++", 250,Getal.rij+=15); ); four.start(); {System.out.println(e); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr14 {public static void main( String arg[]){new MijnFrame14(); Visuele Gebruikers Omgevingen: Swing /395

199 Threads wachten (wait) dus voor een bepaald object (hier Getal). Andere threads kunnen voor dat object de wachtende threads verwittigen dat de conditie vervuld is. Omdat we in bovenstaande klasse een synchronized blok gebruiken zullen alle instructie van dit blok on-onderbroken worden uitgevoerd. Wait en notify voor alle objecten van een klasse In het volgend programma zit nog een klein probleempje verscholen (dit zat ook al in vorige versies): meerdere threads kunnen nog terzelfdertijd de rij aanpassen. Normaal gezien worden rijen beschreven van boven naar onder. Omdat sommige threads terwijl u aan het schrijven bent, de rij aanpassen,worden rijen die meer naar onder komen soms voor rijen die meer naar boven komen gedrukt. Oplossing: verhogen van rij moet ook in een synchronizede routine komen. class Getallen {public int x=0,rij=15; public synchronized void verhoog() {x++; if(x>0)this.notify(); public synchronized void verlaag(graphics gg,int kol) {if( x<=0) {try{gg.drawstring("wait",kol, rij+=15); this.wait(); catch(interruptedexception e) {System.out.println(e); x--; public synchronized int waarde(){ return x; Weeral synchroniseren we op het algemeen object this. Zodoende geldt de synchronisatie later voor alle objecten van de klasse. Voor wait en notify krijgen we this.notify() en this.wait() voor het concrete object. class MijnFrame15 extends JFrame {Thread one,two,three,four; Getallen Getal=new Getallen(); public MijnFrame15() {super(); setbounds(5,5,400,600); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.verlaag(getGraphics(),50); getgraphics().drawstring("--( "+Getal.waarde()+" )--", 50,Getal.rij+=15); ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.verhoog(); getgraphics().drawstring("++( "+Getal.waarde()+" )++", 100,Getal.rij+=15); Visuele Gebruikers Omgevingen: Swing /395

200 ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.verlaag(getGraphics(),200); getgraphics().drawstring("--( "+Getal.waarde()+" )--", 200,Getal.rij+=15); ); three.start(); four=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) {Getal.verhoog(); getgraphics().drawstring("++( "+Getal.waarde()+" )++", 250,Getal.rij+=15); ); four.start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0);); public class Thr15 {public static void main(string arg[]){new MijnFrame15(); De klasse wordt nu ook erg overzichtelijk. Visuele Gebruikers Omgevingen: Swing /395

201 Voorbeelden Threads : meerdere tekeningen die tergelijkertijd bewegen class MijnPanel extends JPanel { Thread one; int t=10,t2=10,aanpassing=1,aanpassing2=1;boolean sw=true; Color coll;int time; public MijnPanel(int tt,color col,int timee) {t=tt;coll=col;time=timee; setvisible(true); setpreferredsize(new Dimension(100,100)); setsize(100,100); setborder(borderfactory.createraisedbevelborder()); one=new Thread(new Runnable() {public void run() {for(;;) {try{thread.sleep(time); catch(interruptedexception e){e.printstacktrace(); Graphics g=mijnpanel.this.getgraphics(); g.setxormode(coll); t+=aanpassing;t2+=aanpassing2; g.setcolor(color.green); g.fillrect(9,9,t,t2); overtekenen, dan zal het snijpunt in de // als we aan de rand komen moeten we van richting veranderen if(t>mijnpanel.this.getwidth()-20)aanpassing=-1; if(t<2)aanpassing=1; if(t2>mijnpanel.this.getheight()-20)aanpassing2=-1; if(t2<2)aanpassing2=1; ); public void start(){ one.start(); public void paintcomponent(graphics g) {super.paintcomponent(g); if(sw) g.setcolor(color.blue);else g.setcolor(color.red); sw=!sw; g.fillrect(0,0,200,400); public class Thr16 extends JFrame XOR mode is handig. Als we een lijn tekenen en daarna er terug een lijn Visuele Gebruikers Omgevingen: Swing /395

202 {MijnPanel a,b,c,d,e,f; public Thr16() {Container xc=getcontentpane(); xc.setlayout(new GridLayout(3,2)); xc.add(a= new MijnPanel(10,Color.yellow,90)); xc.add(d=new MijnPanel(30,Color.cyan,65)); xc.add(b=new MijnPanel(40,Color.black,80)); xc.add(c=new MijnPanel(60,Color.white,40)); xc.add(e=new MijnPanel(40,Color.blue,55)); xc.add(f=new MijnPanel(70,Color.red,70)); a.start();d.start();b.start();e.start();c.start();f.start(); setsize(630,320); setvisible(true); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent ee){system.exit(0);); public static void main(string args[]) {new Thr16(); Een woordje uitleg bij de XORmode. Waarvoor dient het? Stel dat de gebruiker een rechthoek tekent met de muis. Telkens hij de muis beweegt, moet de rechthoek mee vergroten en moet de oude rechthoek verdwijnen. Dit gebeurt met de XORmode. U werkt met bijvoorbeeld een witte achtergrond en tekent een zwarte lijn. Bij setxormode(color.white) en het terug tekenen van dezelfde lijn op dezelfde plaats, zal de lijn terug weg zijn. (vervangen door wit). Wat ook de achtergrond is: een tekening of zo, als u er een rechthoek op tekent, zult u deze rechthoek steeds zien (kleuren worden gexored). Tekent u nogeens dezelfde rechthoek, dan zal de oorspronkelijke tekening er onbeschadigd terug staan. Wanneer zijn threads echt nuttig? Stel dat een programma twee listboxen moet opvullen uitgaande van twee files. Normaal gebeurt het opvullen één na één : eerst de eerste file, dan pas de tweede file. In de eerste versie van het programma gebeurt dit zo. U merkt dat de tweede listbox nog leeg is, omdat de eerste listbox nog niet vol is. (Het lezen uit een file wordt hier gesimuleerd met een klasse Dokument waarmee we met getline een lijn van het document opvragen. In de routine getline wordt ervoor gezorgd dat deze routine traag is en dat er steeds een andere lijn wordt gegenereerd. import java.awt.*; import javax.swing.*; import java.awt.event.*; // zijn thread wel nuttig? Stel dat u een document moet lezen // en dat het genereren van elke lijn 1 seconde duurt. // Wel erg indien u 100 lijnen moet lezen, niet? // De classe Document simuleert dit: class Dokument {int i=0;string tekst; public Dokument(String tekstt){tekst=tekstt; public String getline() Visuele Gebruikers Omgevingen: Swing /395

203 {i++; try {Thread.sleep(300);// de gebruiker moet wachten op deze lijn catch(interruptedexception e){system.out.println(e); return "...lijn "+i+"..."+tekst; class ScrollDoorDocument extends JFrame {DefaultListModel deflistmod=new DefaultListModel(), deflistmod2=new DefaultListModel(); JList list,list2; Dokument dok,dok2; public ScrollDoorDocument() {Container c=getcontentpane(); c. setlayout(new GridLayout(1,2)); list=new JList(defListMod); list2=new JList(defListMod2); setvisible(true);setsize(500,300); c.add(new JScrollPane(list)); c.add(new JScrollPane(list2)); pack(); dok=new Dokument("eerste document"); for(int i=0;i<100;i++) deflistmod.addelement( dok.getline() ); dok2=new Dokument("tweede document"); for(int j=0;j<100;j++) deflistmod2.addelement( dok2.getline() ); // ALS HIER NOG CODE ZOU STAAN MOETEN WE LANG WACHTEN VOORDAT DIE // UITGEVOERD ZOU WORDEN. public class Thr17 {static public void main(string []args) {new ScrollDoorDocument().addWindowListener( new WindowAdapter() {public void windowclosing(windowevent ee){system.exit(0); ); Door het laden van de twee listboxen in twee aparte threads te laten gebeuren, worden de twee listboxen onafhankelijk van elkaar opgevuld. Ook moet het programma niet wachten totdat de twee listboxen opgevuld zijn om verder te gaan. (Achter de code die de twee listboxen opvuld, kan nog andere kode staan.) Bij servers wordt ook dikwijls met threads gewerkt. Stel dat meerdere gebruikers terzelfdertijd een trage bewerking van de server vragen, dan zou de tweede klant van de server moeten wachten totdat de eerste klant gedaan heeft. Bij server wordt daarom steeds een aparte thread opgestart die de transacties van één klant zal behandelen. In volgende aangepaste versie, worden de twee listboxen terzelfdertijd opgevuld. Visuele Gebruikers Omgevingen: Swing /395

204 class Dokumentje {int i=0;string tekst; public Dokumentje(String tekstt){tekst=tekstt; public String getline() {i++; try {Thread.sleep(300); catch(interruptedexception e){system.out.println(e); return "...lijn "+i+"..."+tekst; class ScrollDoorDocumentje extends JFrame {DefaultListModel deflistmod=new DefaultListModel(), deflistmod2=new DefaultListModel(); JList list,list2; Dokumentje dok,dok2; public ScrollDoorDocumentje() {Container c=getcontentpane(); c.setlayout(new GridLayout(1,2)); list=new JList(defListMod); list2=new JList(defListMod2); setvisible(true);setsize(500,300); c.add(new JScrollPane(list)); c.add(new JScrollPane(list2)); pack(); new Thread(new Runnable() {public void run() {dok=new Dokumentje("eerste document"); for(int i=0;i<100;i++) deflistmod.addelement( dok.getline() ); ).start(); new Thread(new Runnable() {public void run() {dok2=new Dokumentje("tweede document"); for(int j=0;j<100;j++) deflistmod2.addelement( dok2.getline() ); ).start(); public class Thr18 {static public void main(string []args) {new ScrollDoorDocumentje( ).addwindowlistener( new WindowAdapter() {public void windowclosing(windowevent ee){system.exit(0); ); Laatste berichten over threadsave tekenen SWING "URBAN LEGENDS" Developers who work with Swing components often hear about certain ways of doing things that they assume are the right ways to work with Swing. Like "urban legends" that purport to be accounts of actual Visuele Gebruikers Omgevingen: Swing /395

205 events but never really happened, some of these Swing techniques are incorrect. In this tip, you'll learn about a number of these Swing urban legends -- approaches that will hinder the performance of your Swing applications. In some cases, the performance reduction might only be nanoseconds, but if you want the best performance profile, even cutting a handful of nanoseconds adds up over time. Here are three Swing urban legends: Create threads for long tasks from the event dispatch thread. Use SwingUtilities for running tasks on the event dispatch thread. Synchronize methods for synchronization. Create Threads for Long Tasks From the Event Dispatch Thread Here's the situation: you want to spin off a thread from the event queue to do a long task. If your event handler needs to do a long task, you don't want to block the event thread. So you create a new thread for the long task, and call invokelater() when the task is done to handle the results on the event thread. Here's the typical usage pattern: public void actionperformed(actionevent e) { Runnable longtask = new Runnable() { public void run() { // Run task to do long stuff... // long task // Update Swing component when done Runnable awttask = new Runnable() { public void run() { // Update Swing component EventQueue.invokeLater(awtTask); ; Thread t = new Thread(longTask); t.start(); This is a common pattern: run long tasks off the event dispatch thread, then update the Swing component after the long task is done. It seems like the right thing to do, but it isn't. If you follow this pattern, you'll run across a problem that could cause your program to behave badly. When a new thread is created, it retains the thread priority of the creating thread. Because the event thread typically runs at a higher level than normal threads, threads created from the event thread inherit the higher priority. Here is a simple program, Threads, that demonstrates the thread priorities: import java.awt.*; public class Threads { public static void main(string args[]) { System.out.println("Main Thread priority: " + Thread.currentThread().getPriority()); Runnable runner = new Runnable() { public void run() { System.out.println("Event Thread priority: " + Thread.currentThread().getPriority()); ; EventQueue.invokeLater(runner); If you run Threads, you'll see that the main thread has a priority of 5, and the event thread runs at a priority 6. >> java Threads Visuele Gebruikers Omgevingen: Swing /395

206 Main Thread priority: 5 Event Thread priority: 6 The higher priority for the event thread is desirable. You want your user interfaces to be responsive. But, you don't want to extend that higher priority to non-event processing tasks. So be sure to lower the priority of user-created threads initialized from the event dispatch thread. This means: Change: Thread t = new Thread(longTask); t. start(); To: Thread t = new Thread(longTask); t.setpriority(thread.norm_priority); t.start(); Threads created with this new priority will not compete for processing time with the event dispatch thread. If there is something to run on the event dispatch thread, it will win -- not the worker thread. You might consider creating a WorkerThread class for just this purpose. That way you won't have to keep calling setpriority() for all new threads created from the event dispatch thread, or use a thread pool through the following new classes in the java.util.concurrent package: Executors.newCachedThreadPool() For a thread pool with unbound size Executors.newFixedThreadPool(int size) For a thread pool of fixed size > 1 Executors.newSingleThreadExecutor() For a thread pool of fixed size = 1 Executor was added to the standard libraries with JDK 5.0. Use SwingUtilities For Running Tasks on the Event Dispatch Thread This isn't really an urban legend, but rather an explanation of the use of the EventQueue class in the first legend. Many people are familiar with the SwingUtilities class for the use of invokelater() and invokeandwait(). Where did this EventQueue class come from? The answer is that all these methods in SwingUtilities wrap calls to the same methods of the EventQueue class in the java.awt package. In other words, there is a level of indirection of the method calls when used through SwingUtilities. There is technically nothing wrong with using the methods. It's just that you can avoid the indirection by using the EventQueue methods directly. Note that another method that wraps access to the EventQueue class is iseventdispatchthread(). This is used to check if the current task is running on the event dispatch thread. If the SwingUtilities methods are just wrapper methods, why do they exist? When the Swing components became available with JDK 1.2, Sun released a version that worked with JDK 1.1. All the Swing bits needed to be self-contained. In other words, the Swing classes couldn't use anything that was introduced to JDK 1.2. For that reason, SwingUtilities contains the methods for invokelater and invokeandwait. Since those methods simply pass along the method calls to EventQueue, you should call the EventQueue methods directly. Synchronize Methods for Synchronization Another commonly-seen practice that hinders performance involves the use of the synchronized keyword. It is common to synchronize methods to prevent simultaneous execution. In many cases, this approach is fine. When might having synchronized methods be bad and slow down performance? When the class is a subclass of an AWT or Swing component, specifically any subclass of java.awt.component. What's wrong with synchronizing methods in subclasses of Component? If you are only trying to Visuele Gebruikers Omgevingen: Swing /395

207 synchronize access to your methods, having a synchronized method means you are competing with all the other synchronized methods of Component. This causes your method to be blocked when it shouldn't, and other Component methods to be blocked when they shouldn't. In fact, this could also lead to unexpected deadlocks. Instead of synchronizing at the method level, you can create a lock variable that is shared by the methods that need to be synchronized. Here's an example: public class Foo extends JComponent { private final Object lock = new Object(); private final char[] chars; public void setmethod(string value) { synchronized(lock) { // save off value as chars chars = value.tochararray(); public String getmethod() { synchronized(lock) { // regenerate saved value from chars String savedvalue = new String(chars); return savedvalue; meer details over performantie: Visuele Gebruikers Omgevingen: Swing /395

208 Innerspection/Reflection De klasse Object, waarvan elk java object impliciet overerft heeft de methode getclass(). Deze methode geeft een object van de klasse Class. In dit object zit alle informatie van het de klasse die hoort bij het object. Je kunt vragen: - De naam van de klasse o String GetName() - welke variabelen er in de klasse zitten o Field [] getdeclaredfields Het resultaat is een array van Field objecten die informatie van de velden bevat (naam, type) o Field getdeclaredfield(string naamveld) Zoek het Field object dat als naam naamveld heeft - welke variabelen er overgeërfd worden van superklassen, samen met eigen gedefinieerde veranderlijken o Field [] getfields() - welke methoden de klasse heeft o Method [] getdeclaredmethods() Het resultaat is een array van Method objecten. Deze methoden kunnen later opgeroepen worden (en dit voor een willekeurige onbekende klasse). o Method getdeclaredmethod( String naammethode) Zoekt naar methode met een bepaalde naam - welke methoden de klasse overerfd o Method [] getmethods() - welke constructoren de klasse heeft o Constructor [] getconstructors() Geeft alle constructoren van deze klasse terug o getconstructor(class [] parameters) constructor opvragen die bepaalde types van parameters heeft. Merk op dat types van parameters worden voorgesteld door een array van Class es - welke interne klassen er werden gedefiniëerd o Class [] getdeclaredclasses() Het resultaat van deze functie is een array van Class objecten - Van welke klassen er wordt overgeërfd o GetClasses - De interfaces die de klasse implementeert o Class [] getinterfaces() - Nagaan tot welk package de klasse behoort o Package getpackage() Binnenin een willekeurig object kunnen gaan kijken, wordt innerspection of reflection genoemd. Daarnaast zijn er nog twee andere speciale methodes. static Class forname(string classname) Returns the Class object associated with the class or interface with the given string name. Visuele Gebruikers Omgevingen: Swing /395

209 forname is een beetje een misleidende naam. Het is een soort zoek methode. Het is een static methode. Ze wordt dus opgeroepen als volgt: Class c=class.forname( be.khleuven.rega.ti2.beans.test ); Deze oproep zal de klasse Test. class op schijf gaan zoeken of in de jars die in het classpath staan gaan zoeken. Indien gevonden, zal de bijhorende Class object gemaakt worden. Object newinstance () Creates a new instance of the class represented by this Class object. Hiermee kunnen objecten gemaakt worden van klassen waarvan u op voorhand (voor de uitvoering van het programma de naam niet hoeft te weten). String naamklasse; laa t gebruiker naam van klasse opgeven. Class c=cl ass. forname(naamvanklasse); Object o=c.newinstance(); o bevat na de oproep van newinstance de referentie naar een klasse waarvan de gebruiker de naam he eft opgegeven. Voorbeeld waarbij de gebruiker willekeurige klassen kan maken In de volgende interactieve toepassing kan de gebruiker onderaan het scherm de naam van een willekeurige klasse ingeven. In d e schermafdruk werd java.lang.string genomen. U merkt dat u de volledige naam van de klasse moet opgeven. Een object van die klasse zal aangemaakt worden. U kunt ook eens bijvoorbeeld javax.swing.jframe intypen. U zult merken dat er een nieuw (leeg) frame bij op uw scherm verschijnt. Het programma maakt immers een object van die klasse aan. Hierbij ziet u echt het visueel resultaat. Visuele Gebruikers Omgevingen: Swing /395

210 De toepassing heeft ook twee lijsten. De linker lijst zal de namen van alle methode van de door de gebruiker opgegeven klasse tonen. De rechter lijst zal de namen van de velden van de door de gebruiker opgegeven klasse tonen. Voor javax.swing.jframe krijgen we een heleboel methodes en velden: Het programma gaat ook automatisch alle methodes oproepen die eenvoudig op te roepen zijn: methodes die geen parameters hebben en ook een eenvoudig resultaat hebben: vb een String. Het resultaat van deze oproepen zal verschijnen in het msdos venster. import java.lang.reflect.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; class MijnFrame extends JFrame {JTextField field; JLabel lab; JList list,list2; DefaultListModel dlm=new DefaultListModel(); DefaultListModel dlm2=new DefaultListModel(); Visuele Gebruikers Omgevingen: Swing /395

211 public MijnFrame() {setvisible(true); Container c=getcontentpane(); lab=new JLabel("geef naam van een klasse om dynamisch te laden"); field=new JTextField(20); field.settext("naam klasse uit huidige dir of gewone Java klasse"); list=new JList(dlm);// methode van klasse list2=new JList(dlm2);//velden van klasse c.setlayout(new BorderLayout()); JPanel pan=new JPanel(); pan.setlayout(new GridLayout(4,1)); pan.add(lab); pan.add(new JLabel("tevens worden routines die een String teruggeven")); pan.add(new JLabel("en die geen parameters hebben opgeroepen")); c.add(pan,borderlayout.north); c.add(field,borderlayout.south); c.add(new JScrollPane(list),BorderLayout.CENTER); c.add(new JScrollPane(list2),BorderLayout.EAST); pack();setsize(300,150); field.addactionlistener(new ActionListener() {public void actionperformed(actionevent ee) {try{// we laden klasse informatie over de klasse Class klassevanschijf = Class.forName(field.getText()); // we maken een object van de gevraagde klasse Object objectvanklassevanschijf= klassevanschijf.newinstance(); //default constructor wordt gebruikt forname : hiermee kan je een willekeurige klasse van schijf halen. // welke routines heeft de klasse? Method[] reflectedmethods = klassevanschijf.getmethods(); // listem:list all methods that were not inherited from Object //(anders krijgen we er teveel) dlm.clear();// we maken lijst leeg Met newinstance maak je een nieuw object van de willekeurige klasse We lopen alle methoden van klasse af en voegen toe aan listbox for (int i = 0; i < reflectedmethods.length; i++) {if (reflectedmethods[i].getdeclaringclass()!= Class.forName("java.lang. Object")) dlm.addelement(reflectedmethods[i]); Object retval; // callem: call all methods that return Strings and take no args for (int i = 0; i < reflectedmethods.length; i++) {if ((reflectedmethods[i].getreturntype() == Class.forName("java.lang.String")) && Visuele Gebruikers Omgevingen: Swing /395

212 (reflectedmethods[i].getparametertypes().length ==0) ) {retval = // we roepen de methode eindelijk op: reflectedmethods[i].invoke(objectvanklassevanschijf, null); System.out.println("Method: " + reflectedmethods[i] + ", returned: [" + retval + "]"); if (reflectedmethods[i].getname().equals("show") &&reflectedmethods[i].getparametertypes().length == 0 ) {retval = // we roepen show op: we zien de component reflectedmethods[i].invoke(objectvanklassevanschijf, null); // we vragen alle v elden van klasse Field[] reflectedfields = klassevanschijf.getfields(); dlm2.clear();//lijst op scherm leeg maken Object fieldvalue; // getem for instance for (int i = 0; i < reflectedfields.length; i++) { fieldvalue = reflectedfields[i].get(objectvanklassevanschijf); dlm2.addelement("field[" + i + "]: " + reflectedfields[i] + ", Value = [" + fieldvalue + "]"); System.out.println( "Field[" + i + "]: " + reflectedfields[i] + ", Value = [" + fieldvalue + "]"); catch (Exception e) {System.out.println(e.getMessage()); ); class Reflect01 {static public void main(string args[]) {new MijnFrame().addWindowListener(new WindowAdapter() {public void windowclosing(windowevent e){system.exit(0); ); Visuele Gebruikers Omgevingen: Swing /395

213 Toelichting bij het programma Laden van de methodes van de klasse in lijst1 for (int i = 0; i < reflectedmethods.length; i++) {if (reflectedmethods[i].getdeclaringclass()!= Class.forName("java.lang.Object")) dlm.addelement(reflectedmethods[i]); De array reflectedmethods bevat alle Method objecten die bij de klasse horen die de gebruiker ingetypt heeft. Maar deze array bevat ook de methode die overgeërfd werden van bijvoorbeeld de klasse Object. De methodes van de klasse Object gaan we liefst niet elke keer gaan tonen. Daarom testen we of de klasse die bij de methode hoort (reflectedmethods[i].getdeclaringclass() ) wel verschillend is van de klasse Object ( Class.forName("java.lang.Object"))). We testen of twee Class objects wel verschillend zijn. Methoden zoeken die geen argumenten he resultaat een String hebben bben en die als {if ((reflectedmethods[i].getreturntype() = = Class.forName("java.lang.String")) && (reflectedmethods[i].getparametertypes().length ==0) ) getreturntype geeft een Class object terug dat het type van het resultaat van de methode voorsteld. Dit moet gelijk zijn aan het type van java.lang.string (Class.forName("java.lang.String")). GetParameterTypes() geeft een array van Class es terug van alle parameters. Als de lengte van deze array 0 is, zijn er geen parameters. Een willekeurige methode oproepen retval = reflectedmethods[i].invoke(objectvanklassevanschijf, null); Dit is een rare wijze om een methode op opgeroepen: Naamobject punt naamroutine ( parameters ) Vb. object.wijzig( piet,5); te roepen. Methodes worden normaal als volgt De naam van het object is daarbij een veranderlijke en de naam van de methode ligt vast. In ons programma wordt zowel de methode als het object waarop deze methode moet worden toegepast, voorgesteld door een veranderlijke. We kunnen maar één veranderlijke voor het punt schrijven. Voor het punt komt aldus reflectedmethods[i]. Dit object stelt de methode voor die we zullen oproepen met behulp van invoke. We hebben twee parameters: Visuele Gebruikers Omgevingen: Swing /395

214 - het object waarop de methode moet toegepast worden (hier objectvanklassevanschijf) - de array van objecten die de parameters voorstellen die meegegeven moeten worden aan de methode die opgeroepen wordt ( hier null). Waarden van veranderlijken van de willekeurige opvragen klasse fieldvalue = reflectedfields[i].get(objectvanklassevanschijf); De veranderlijke waarvan we waarde willen opvragen wordt voorgesteld door reflectedfields[i]. Het object waarvan we de veranderlijke reflectedfields[i] willen opvragen noemt, objectvanklassevanschijf. In een normaal programma vraagt u de waarde van de veranderlijke op door: Waarde = object.veranderlijke Weeral omdat zowel object als veranderlijke door een variabele worden voorgesteld in ons programma en we maar één veranderlijke voor het punt kunnen plaatsen, komen we tot de volgende schrijfwijze: fieldvalue = reflectedfields[i].get(objectvanklassevanschijf); De inhoud van de veranderlijke reflectedfields[i] van de veranderlijke objectvanklassevanschijf wordt opgevraagd. Visuele Gebruikers Omgevingen: Swing /395

215 Serialization- Persistentie Soms komt het voor u een object in zijn geheel moet wegschrijven naar schijf om het persistent te maken. Het object wordt dan persistent naar schijf geschreven. Eenvoudig is deze taak niet omdat het object ook referenties naar andere objecten kan bevatten. Die moeten dan ook gesaved worden. We laten twee verschillende aanpakken aan bod komen. De eerste aanpak bestaat erin om zelf alle data van een object naar een bestand in binair formaat te schrijven. De tweede aanpak zal eruit bestaan om het werk over te laten aan java. Door middel van innerspection kan java zelf alle data die moet gesaved worden opzoeken en wegschrijven naar schijf. Zelf saven van gehele objecten U kunt gelijk welk object zelf saven door alle velden één voor één ervan weg te schrijven naar een DataOutputStream. Deze klasse heeft methodes om integers, boolean, doubles, floats, enzovoorts ongeconverteerd (in hexadecimaal formaat) weg te schrijven. Als je de DataOutputStream verbindt met een FileOutputStream, dan worden ze uiteindelijk weggeschreven in een bestand. Het is natuurlijk zwaar om op deze wijze de inhoud van alle velden van een willekeurig object naar schrijf te schrijven. Java heeft de mogelijkheid tot innerspection: voor een willekeurig object kan gevraagd worden welke velden erin zitten en van welk type ze zijn. Hierdoor zou het mogelijk moeten zijn om een systeem te bouwen dat automatisch alle velden van een object gaat saven op schijf en daarna terug gaan herstellen. We kunnen slechts basis types zoals int,char wegschrijven naar een DataOutputStream. In het volgend programma schrijven we een getal 1111 weg. Als we een object zoals een String willen wegschrijven, moeten we alle werk zelf doen: eerst de lengte van de tekst wegschrijven ( dit is tekst.length() ) gevolgd door de tekst zelf ( dit is tekst ). Niet echt handig. Het programma gaat eerst enkele veranderlijken wegschrijven naar bestand en zal ze daarna vervolgens terug gaan lezen. import java.io.*; public class Stream01 { public static void p(string s){system.out.println(s); public static void main(string args[]) throws IOException {FileOutputStream fos=new FileOutputStream("bytes.bin"); DataOutputStream dop =new DataOutputStream(fos); dop.writeint(1111); dop.writeboolean(true); dop.writeint("dit is tekst".length() );//een string moeten we als rij Visuele Gebruikers Omgevingen: Swing /395

216 dop.writechars("dit is tekst"); //van characters + lengte saven dop.writedouble(11.11); dop.close(); FileInputStream fis=new FileInputStream("bytes.bin"); DataInputStream dip =new DataInputStream(fis); int i;boolean b;int aantal;char letters[];double fl; i=dip.readint();p( " int :"+i); b=dip.readboolean();p(" boolean :"+b); aantal=dip.readint();p(" aantal chars :"+aantal);//we lezen lengte rij letters=new char[aantal]; //we lezen rij van characters for(int j=0;j<aantal;j++) {letters[j]= dip.readchar();p("letter :"+letters[j]); fl=dip.readdouble();p("float :"+fl); dip.close(); /* int :1111 boolean :true aantal chars :12 letter :d letter :i letter :t letter : letter :i letter :s letter : letter :t letter :e letter :k letter :s letter :t float :11.11 We laten ook eens de inhoud van de file bytes.bin zien. Integers en floats worden binair wegschreven. Als we ze als tekstbestand laten zien, zullen we natuurlijk nonsens krijgen. inhoud van de file: _W_ d i t i s t e k s t@&8që _ */ Visuele Gebruikers Omgevingen: Swing /395

217 Serializatie aan java overlaten Java heeft perfect de mogelijkheid om objecten automatisch te saven. Door middel van innerspection (elk object heeft een Class object waarin alle veranderlijken, hun waarde, hun type zitten) kan java elk willekeurig object ontleden en wegschrijven. Wel is er nood aan een soepele wijze om aan te geven welke objecten wel en welke objecten niet automatisch moeten gesaved worden. Men heeft gekozen voor een eenvoudige oplossing: klassen die wel mogen gesaved worden worden aangeduid doordat ze de lege interface Serializable implementeren. Staat dus achter een klasse implements Serializable, dan zal de inhoud van de klasse (en overal waar objecten van die klasse gebruikt worden) mee gesaved worden als we naar schijf overbrengen. (java kan met behulp van de methode getinterfaces aan het Class object van elk object vragen of de interface Serializable vermeld werd voor de klasse van het object..) Serialization. Classen die de interface Serializable implementeren geven aan dat alle velden moeten gesaved worden als het object wordt weggeschreven naar een ObjectOutputStream. De methode writeobject van deze klasse ObjectOutputStream zal indien de klasse de interface Serializable implementeert, alle velden van het object aflopen en saven. Dit saven gebeurt zoveel niveau s diep als nodig. In onderstaand voorbeeld saven we een object van de klasse TweeRecords die elk twee andere objecten bevatten (Record), die terug een object bevatten (HulpRecord). (drie niveau s diep) Alles wordt automatisch goed gesaved. De interface Serializable is raar: er staat NIETS in. Het is een lege interface. Je moet dus geen enkele methode definiëren voor die interface. Waarvoor dient ze dan? Ze dient als flag, switch, markeerder om gewoon aan te geven dat alle velden van die klasse mogen gesaved worden. Saven van een object naar een file (persistent maken van het object) wordt erg eenvoudig: u schrijft het object met behulp van writeobject naar een ObjectOutputStream. Dat is alles. U schrijft meestal maar één object weg naar schijf. Dit object kan wel heel veel deelobjecten hebben, of een array van referenties naar andere objecten bevatten. Alles wordt fijn weggeschreven door gewoon het root record weg te schrijven. Terug lezen van een ObjectInputStream is een heel klein beetje ingewikkelder. Dit gebeurt met behulp van de methode readobject. Deze methode geeft natuurlijk steeds iets terug van het algemene type Object. U moet het resultaat van readobject gewoon converteren naar het type TweeRecords (het type van het object dat u weggeschreven hebt). import java.io.*; Visuele Gebruikers Omgevingen: Swing /395

218 class HulpRecord implements Serializable {int Int; String str; public HulpRecord(int i, String s) {Int=i;str=s; public void druk() {System.out.println("HulpRecord: "+Int+" "+str); class Record implements Serializable {int Int; String str; HulpRecord hulp; public Record(int i, String s) {Int =i; str=s; int j=int+1; hulp=new HulpRecord(j, str); public void druk() {System.out.println("Record: "+Int+" "+str); hulp.druk(); class TweeRecords implements Serializable {Record een,twee; public TweeRecords(Record e,record t) {een=e;twee=t; public void druk(){een.druk();twee.druk(); public class Stream02 { public static void p(string s){system.out.println(s); public static void main(string args[]) throws IOException {FileOutputStream fos=new FileOutputStream("bytes.bin"); ObjectOutputStream dop =new ObjectOutputStream(fos); Record rec,rec2; rec=new Record(100,"eerste record"); rec2=new Record(200,"tweede record"); TweeRecords tw=new TweeRecords(rec,rec2); tw.druk(); dop.writeobject(tw); dop.close(); FileInputStream fis=new FileInputStream("bytes.bin"); ObjectInputStream dip =new ObjectInputStream(fis); TweeRecords tw2; try{//terug converteren naar juiste type tw2=(tweerecords)dip.readobject(); System.out.println("van schijf:"); tw2.druk(); catch(classnotfoundexception e){system.out.println(e); dip.close(); /* Record: 100 eerste record HulpRecord: 101 eerste record Record: 200 tweede record Visuele Gebruikers Omgevingen: Swing /395

219 HulpRecord: 201 tweede record van schijf: Record: 100 eerste record HulpRecord: 101 eerste record Record: 200 tweede record HulpRecord: 201 tweede record in het bestand: wat bijgehouden). is dat allemaal? (ook types worden í _sr L _eent _LRecord;L _tweeq ~ _xpsr _Recordª ¹ ˆüdž I _IntL _hulpt LHulpRecord;L _strt _Ljava/lang/String;xp HulpRecord8ú_ ±¾À I _IntL _strq ~ _xp dsr et eerste recordq ~ sq ~ _ Èsq ~ _ Ét tweede recordq ~ */ Selectief delen van object automatisch laten saven. Sommige objecten bevatten tijdelijke tussenresultaten en moeten niet gesaved worden. Je kan op twee wijzen aangeven dat iets niet automatisch moet gesavede worden: -HulpRecord zal nu niet gesaved worden omdat het de interface Serializable niet implementeert. -Een veranderlijke waarvoor het woordje transient wordt aange geven, wordt nooit gesaved. (Hiermee kan je delen van een object selectief weglaten uit het save proces.) pas op: als je een object terug leest van schijf, moet je testen of er geen velden bijzijn die niet hersteld werden (transient velden). In onderstaand voorbeeld moet je bij het uitdrukken nagaan of hulp wel een waarde heeft Alle Swing Componenten zijn automatisch Serializable en kunnen eenvo udig gesaved worden. Je kan dus met één writeobject bevel een ingewikkelde JFrame even saven en later in zijn geheel terug herstellen === import java.io.*; class HulpRecord {int Int; String str; public HulpRecord(int i, String s) {Int=i;str=s; Visuele Gebruikers Omgevingen: Swing /395

220 public void druk(){system.out.println("hulprecord: "+Int+" "+ str); class Record implements Serializable {int Int; String str; transient HulpRecord hulp; // wordt niet gesaved door transient public Record(int i, String s) {Int =i; str=s; int j=int+1; hulp=new HulpRecord(j, str); public void druk() {System.out.println("Record: "+Int+" "+str); if(hulp!=null) hulp.druk(); class TweeRecords implements Serializable {Record een,twee; public TweeRecords(Record e,record t) {een=e;twee=t; public void druk(){een.druk();twee.druk(); public class Stream03 { public static void p(string s){system.out.println(s); public static void main(string args[]) throws IOException {FileOutputStream fos=new FileOutputStream("bytes.bin"); ObjectOutputStream dop =new ObjectOutputStream(fos); Record rec,rec2; rec=new Record(100,"eerste record"); rec2=new Record(200,"tweede record"); TweeRecords tw=new TweeRecords(rec,rec2); tw.druk(); dop.writeobject(tw); dop.close(); FileInputStream fis=new FileInputStream(" bytes.bin"); ObjectInputStream dip =new ObjectInputStream(fis); TweeRecords tw2; try{ tw2=(tweerecords)dip.readobject(); System.out.println("van schijf:"); tw2.druk(); catch(classnotfoundexception e){system.out.println(e); /* dip.close(); Visuele Gebruikers Omgevingen: Swing /395

221 Record: 100 eerste record HulpRecord: 101 eerste record Record: 200 tweede record HulpRecord: 201 tweede record v an schijf: (geen hulprecord daar dit niet werd gesaved) Record: 100 eerste record Record: 200 tweede record */ Visuele Gebruikers Omgevingen: Swing /395

222 OO-2 Een flexibel rekenmachine Als we een Rekenmachine inpluggen werken de bewerkingen +, -, *, en / voor gewone getallen. public class Rekenmachine1b {public static void main(string args[]) {new GUI(new Rekenmachine()); Als we een RekenmachineLong inpluggen, werkt de rekenmachine met getallen tot 100 cijfers. Alleen de bewerking + werd voor deze demo geimplementeerd. public class Rekenmachine1b {public static void main(string args[]) {new GUI(new RekenmachineLong()); Visuele Gebruikers Omgevingen: Swing /395

223 Als we een RekenmachineBinaryLong inpluggen, werkt de rekenmachine met binaire getallen tot 100 cijfers. Alleen de bewerking + werd voor deze demo geimplementeerd. public class Rekenmachine1b {public static void main(string args[]) {new GUI(new RekenmachineBinaryLong()); Als we een RekenmachineOctaalLong inpluggen, werkt de rekenmachine met binaire getallen tot 100 cijfers. Alleen de bewerking + werd voor deze demo geimplementeerd. public class Rekenmachine1b {public static void main(string args[]) {new GUI(new RekenmachineOctaalLong()); Om dit te bewerkstelligen hebben we de klasse JIntegerTextField wat aangepast. We zullen nu testen dat er geen niet-digits worden ingetypt. (we kunnen niet meer testen of het geheel wel een integer is.) import javax.swing.*; import java.awt.event.*; Visuele Gebruikers Omgevingen: Swing /395

224 import java.awt.*; class JIntegerTextField extends JTextField { public JIntegerTextField() {super(); addkeylistener(new KeyAdapter() {public void keyreleased(keyevent e) {if( isgetal()) setbackground(color.white); else setbackground(color.orange); ); public String getgetal() {if(isgetal()) return gettext(); return "0"; public Dimension getpreferredsize(){return new Dimension(370,30); public boolean isgetal() {String tekst=gettext(); int l=tekst.length() ; Visuele Gebruikers Omgevingen: Swing /395

225 boolean isgetal=true; // we kijken of alle tekens wel digits zijn for(int j=0;j<l;j++) { char c=tekst.charat(j); if (!Character.isDigit(c)) isgetal=false; return isgetal; // deze klasse werkt intern met een String om getallen met honderden // cijfers toe te laten class Rekenmachine {String eerstegetal,tweedegetal; char bewerking; public Rekenmachine() {eerstegetal="0";tweedegetal="0";bewerking='+'; public void setbewerking(char b){bewerking=b; public void setgetaleen(string een) {eerstegetal=een; public void setgetaltwee(string twee) {tweedegetal=twee; public String getresultaat() { int getal1=converttoint(eerstegetal); int getal2=converttoint(tweedegetal); if(bewerking=='+')return ""+(getal1+getal2); if(bewerking=='-')return ""+(getal1-getal2); if(bewerking=='/'&&getal2!=0)return ""+(getal1/getal2); if(bewerking=='*')return ""+(getal1*getal2); return "0"; private int converttoint(string tekst) {int i=0; return i; try{i=integer.parseint(tekst); catch(exception ee){return 0; //De klasse Rekenmachine zal de String converteren naar integers en vervolgens //gewoon de bewerkingen in java uitvoeren. // de klasse RekenmachineLong zal de twee String converteren naar array s van 100 // characters. Vervolgens zullen deze array s van rechts naar links worden afgelopen. // Elk character zal naar een integer worden omgezet en worden opgeteld bij het // overeenkomstig character van de andere array. Visuele Gebruikers Omgevingen: Swing /395

226 class RekenmachineLong extends Rekenmachine {char getal1[]=new char[100]; char getal2[]=new char[100]; char result[]=new char[100]; String allemaalnullen=" "+ " "+ " "+ " "; public int basis(){return 10; public String getresultaat() {// we implementeren alleen '+' initarrays(); int overdracht=0; for (int i=99;i>=0;i--) { // we converteren '0' naar 0 en...'9' naar 9 int digit1=getal1[i]-'0'; int digit2=getal2[i]-'0'; int digit3=digit1+digit2+overdracht; overdracht=digit3/basis(); result[i]=(char)((int)'0'+(digit3 - (overdracht*basis()))); return new String(result); private void initarrays() { String eerstegetal100= allemaalnullen.substring(0,100 - eerstegetal.length())+eerstegetal; String tweedegetal100= allemaalnullen.substring(0,100 - tweedegetal.length())+tweedegetal; getal1=eerstegetal100.tochararray(); getal2=tweedegetal100.tochararray(); // het enige verschil in algoritme tussen het rekenen in het tiendelig of het binair //stelsel, is de basis die ofwel 10 ofwel 2 zal zijn. class RekenmachineBinaryLong extends RekenmachineLong {public int basis(){return 2; class RekenmachineOctaalLong extends RekenmachineLong {public int basis(){return 8; De klasse GUI zal kunnen werken met elke Rekenmachine. Bij het aanmaken van een object van deze klasse GUI zal een object van de klasse Rekenmachine worden doorgegeven. class GUI extends JFrame Visuele Gebruikers Omgevingen: Swing /395

227 { class KeuzeBewerking implements ActionListener {public void actionperformed(actionevent ae) {if(ae.getsource() instanceof JButton) {JButton but=(jbutton)ae.getsource(); char bewerking=but.gettext().charat(0); if( bewerking=='+') rekenmachine.setbewerking('+'); if( bewerking=='-') rekenmachine.setbewerking('-'); if( bewerking=='/') rekenmachine.setbewerking(' /'); if( bewerking=='*') rekenmachine.setbewerking('*'); rekenmachine.setgetaleen( getal1.getgetal() ); rekenmachine.setgetaltwee( getal2.getgetal() ); if( bewerking= ='=') {String resultaat= rekenmachine.getresultaat(); getal3.settext(resultaat); JIntegerTextField getal1,getal2,getal3; JButton plus,min,maal,deel,is; Container c; Rekenmachine rekenmachine; public GUI(Rekenmachine rekenmachine) {super("rekenmachine"); this.rekenmachine=rekenmachine; c=getcontentpane(); c.setlayout(new FlowLayout()); getal1=new JIntegerTextField(); getal2=new JIntegerTextField(); plus =new JButton("+");min =new JButton("-"); deel =new JButton("/");maal =new JButton("*"); is =new JButton("="); getal3=new JIntegerTextField(); c.add(getal1);c.add(plus);c.add(min);c.add(deel);c. add(maal); c.add(getal2);c.add(is); c.add(getal3); setdefaultcloseoperation(jframe.exit_on_close); KeuzeBewerking keuzebewerking=new KeuzeBewerking(); plus.addactionlistener(keuzebewerking); min.addactionlistener(keuzebewerking); maal.addactionlistener(keuzebewerking); deel.addactionlistener(keuzebewerking); is.addactionlistener(keuzebewerking); setsize(400,250); setvisible(true); public class Rekenmachine1b { public static void main(string args[]) {new GUI(new RekenmachineOctaalLong()); Visuele Gebruikers Omgevingen: Swing /395

228 Strategy: Karel het objectje In dit programma zijn er twee met de muis versleepbare mannetjes. Je kan een mannetje selecteren door er met de muis op te klikken. Je kan het vervolgens verslepen. Het geselecteerd mannetje kan je ook een ander hoofd geven: een rond of een vierkant hoofd. In dat geval wordt er een object dat de interface Head implementeert aangemaakt en ingeplugd in de persoon p1 of p2. Visuele Gebruikers Omgevingen: Swing /395

229 import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.io.*; Visuele Gebruikers Omgevingen: Swing /395

230 /* ***************** MYPANNEL CLASS ******************** */ class MyPanel extends JPanel { int posx,posy; int px, py; Person p1=new Person(200,100); Person p2=new Person(300,100); public MyPanel() {setdoublebuffered(false); setopaque(false); addmousemotionlistener (new MouseMotionAdapter() {public void mousedragged(mouseevent e) { Graphics g=getgraphics(); posx=e.getx(); posy=e.gety(); if(p1.ismouseonobject(px, py)) {p1.clear(g); p1.moveby(posx - px, posy -py); if(p2.ismouseonobject(px, py)) {p2.clear(g); p2.moveby(posx - px, posy -py); px = posx; py = posy; paint(g);); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) {px=e.getx(); py=e.gety(); if (p1.ismouseonobject(px, py)) {p1.setselected(true); p2.setselected(false); if (p2.ismouseonobject(px, py)) {p2.setselected(true); p1.setselected(false); ); public void paint(graphics g) {p1.draw(g); p2.draw(g); public void changeheadofselectedperson(head head) {if(p1.isselected())p1.sethead(head); if(p2.isselected())p2.sethead(head); /* ******************* HEAD-INTERFACE ******************** */ interface Head { public void draw(int x,int y, Graphics g); Visuele Gebruikers Omgevingen: Swing /395

231 class SquareHead implements Head {public void draw(int x,int y, Graphics g) { g. setcolor(color.yellow); g.fillrect(x+30,y,20,20); g.setcolor(color.black); g.drawrect(x+30,y,20,20); class RoundHead implements Head {public void draw(int x,int y, Graphics g) { g.setcolor(color.yellow); g.filloval(x+30,y,20,20); g.setcolor(color.black); g.drawoval(x+30,y,20,20); /* ******************* PersonClass ******************** */ class Person { Head head; int x,y; boolean selected; public P erson (int par x, int pary) { x=parx; y=pary; selected=false; sethead(new RoundHead()); public void setselected(boolean selected){this.selected=selected; public boolean isselected(){return selected; public void sethead(head head) public void draw(graphics g) { g.setcolor(color.black); g.drawline(x+6,y+30,x+20,y+30); g.drawline(x+60,y+30,x+74,y+30); g.drawline(x+30,y+60,x+30,y+75); g.drawline(x+ 50,y+60,x+50,y+75); g.drawrect(x+45,y+75,10,5); g.drawrect(x+1,y+25,5,10); g.drawrect(x+25,y+75,10,5); g.drawrect(x+74,y+25,5,10); g.drawrect(x+20,y+20,40,40); if(head!= null) head.draw(x,y,g); {this.head=head; public void clear(graphics g) { g.setcolor(color.white); g. fillrect(x,y,80,90); public boolean ismouseonobject(int xm, int ym) {return x+20 < xm && xm < x+60 && y+20 < ym && ym < y+60; Visuele Gebruikers Omgevingen: Swing /395

232 public void moveby(int deltax, int deltay) {x += deltax; y += deltay; ; /* ******************* JFRAME-PROCEDURE ******************** */ public class KarelHetObjectje extends JFrame { MyPanel Carlpanel =new MyPanel(); JButton roundbutton =new JButton("Round"); JB utton squarebutton=new JButton("Square"); JLabel tx ttype =new JLabel("Carl's Head :"); public KarelHetObjectje() { super("carl the Object HeadPlug-in"); setsize(500,310); Container c=getcontentpane(); c.setlayout( null); c.setbackground(color.white); c.add(txttype); c.add(roundbutton); c.add(squarebutton); c.add(carlpanel); txttype.setbounds(10,10,100,30); roundbutton.setbounds(10,50,80,30); squarebutton.setbounds(10,90,80,30); Carlpanel.setBounds(100,10,380,260); Carlpanel.setBorder(new LineBorder(Color.blue)); roundbutton.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { if (e.getsource()==roundbutton) {Carlpanel.changeHeadOfSelectedPerson(new RoundHead()); repaint(); ); squarebutton.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { if (e.getsource()==squarebutton) {Carlpanel.changeHeadOfSelectedPerson(new SquareHead()); repaint();); setdefaultcloseoperation(jframe.exit_on_close); setvisible(true); /* ******************* MAIN-PROCEDURE ******************** */ public static void main( String[] args ) {new KarelHetObjectje( ); Visuele Gebruikers Omgevingen: Swing /395

233 Een tekenprogramma In dit tekenprogramma kan je de kleur waarmee wordt getekend, inpluggen. Je kan ook de dikte van de tekenpen opgeven. De JFrame maakt gebruikt van een GridBagLayout. Hiermee wordt de verschillende onderdelen van het venster proportioneel mee vergroot. Visuele Gebruikers Omgevingen: Swing /395

234 import javax.swing.*; import javax.swing.border.*; import javax.swing.joptionpane.*; import java.awt.*; import java.awt.event.*; /* ***************** DRAWPANNEL CLASS ******************** */ class MyPanel extends JPanel { int x=0, y=0, oldx, oldy; static int size=1; static ColorChooser cc; static Color bkcol=color.white; Visuele Gebruikers Omgevingen: Swing /395

235 public MyPanel(Color c) { bkcol=c; setbackground(bkcol); addmouselistener (new MouseAdapter() {public void mousepressed(mouseevent e) { oldx=x; oldy=y; x=e.getx(); y=e.gety(); Graphics g=getgraphics(); if (e.ismetadown()) g.setcolor(bkcol); else g.setcolor(cc.givecolour()); if (size >3) g.filloval(x, y, size, size); ); addmousemotionlistener (new MouseMotionAdapter() {public void mousedragged(mouseevent e) { oldx=x; oldy=y; x=e.getx(); y=e.gety(); Graphics g=getgraphics(); if (e.ismetadown()) g.setcolor(bkcol); else g.setcolor(cc.givecolour()); if (size >3) g.filloval(x, y, size, size); else g.drawline(oldx, oldy, x, y); ); public void plugincolorchooser(colorchooser c) {cc=c; public void setsize(int a) {size=a; /* ******************* COLOR-INTERFACE ******************** */ interface ColorChooser {public Color givecolour(); class blackcolor implements ColorChooser {public Color givecolour(){return Color.black; class greencolor implements ColorChooser {public Color givecolour(){return Color.green; class redcolor implements ColorChooser {public Color givecolour(){ return Color.red; class bluecolor implements ColorChooser {public Color givecolour(){ return Color.blue; Visuele Gebruikers Omgevingen: Swing /395

236 class magiccolor implements ColorChooser { static int rx=0,gx=100,bx=200; boolean rxbl=false,gxbl=false,bxbl=false; private static Color magic = new Color(rx,0,0,150); public Color givecolour() { magic = new Color((rx%255),(gx%255),(bx%255),255); if (rx>=0 &&!rxbl) rx=rx+1; if (rx<=254 && rxbl) rx=rx-2; if (rx==254) rxbl=true; if (rx==0) rxbl=false; if (gx>=0 &&!gxbl) gx=gx+2; if (gx<=254 && gxbl) gx=gx-1; if (gx==254) gxbl=true; if (gx==0) gxbl=false; if (bx>=0 &&!bxbl) bx=bx+2; if (bx<=254 && bxbl) bx=bx-2; if (bx==254) bxbl=true; if (bx==0) bxbl=false; return magic; /* ******************* DIALOG-PROCEDURE ******************** */ public class Painter extends JFrame { JComboBox kleurbox =new JComboBox(new String[] {" Black", " Green"," Red"," Blue"," Magic"); JComboBox sizebox =new JComboBox(new String[] {" Small"," Medium"," Large"," XLarge", " Super"); MyPanel DRAWPANNEL =new MyPanel(Color.white); JButton exitknop =new JButton("Exit"); JButton clearknop =new JButton("Clear"); JButton infoknop =new JButton(" Info"); JLabel txtcolor =new JLabel("Color :"); JLabel txtsize =new JLabel("Size :"); JMenuBar bar =new JMenuBar(); JMenu menu1 =new JMenu("File"); JMenu menu1it1 =new JMenu("New"); JMenu menu1it2 =new JMenu("Save"); JMenuItem menu1it11 =new JMenuItem("Black"); JMenuItem menu1it12 =new JMenuItem("White"); public Painter() { super(); setsize(450,250); settitle("java Paint"); MyPanel.pluginColorChooser(new blackcolor()); final JPanel c= new JPanel(new BorderLayout()); Visuele Gebruikers Omgevingen: Swing /395

237 c.setlayout(new GridBagLayout()); c.setborder(new EmptyBorder(new Insets(5,5,5,5))); getcontentpane().add(borderlayout.center, c); GridBagConstraints gb = new GridBagConstraints(); gb.weighty=1.0; gb.weightx=1.0; /* ******************* DRAWPANNEL ******************** */ gb.gridx=0; gb.gridy=0; gb.ipadx=260; gb.gridheight=5; gb.anchor=gridbagconstraints.west; gb.fill=gridbagconstraints.vertical; c.add(drawpannel,gb); /* ****************** TEKST-COLOR ******************* */ gb.gridx=1; gb.ipadx=5; gb.ipady= 5; gb.gridheight=1; gb.fill= GridBagConstraints.NONE; c.add(txtcolor,gb); /* ****************** TEKST-SIZE ******************** */ gb.gridy=1; gb.ipadx=5; gb.ipady= 5; gb.gridheight=1; gb.fill= GridBagConstraints.NONE; c.add(txtsize,gb); /* ******************* COLORBUTTON ********************* */ gb.gridx=2; gb.gridy=0; gb.ipadx=5; gb.ipady=5; gb.anchor=gridbagconstraints.east; gb.fill=gridbagconstraints.horizontal; c.add(kleurbox,gb); kleurbox.setmaximumrowcount(3); /* ******************* SIZEBUTTON ********************* */ gb.gridy=1; gb.ipadx=5; gb.ipady=5; c.add(sizebox,gb); sizebox.setmaximumrowcount(3); /* ******************* EXITBUTTON ********************** */ gb.gridy=2; c.add(exitknop,gb); /* ******************* CLEARBUTTON ********************** */ gb.gridy=3; c.add(clearknop,gb); /* ******************* INFOBUTTON ********************** */ gb.gridy=4; c.add(infoknop,gb); /* ***** ************** LISTENERS ********************* */ kleurbox.additemlistener (new ItemListener() {public void itemstatechanged(itemevent ee) {if (kleurbox.getselectedindex()==0) DRAWPANEL.pluginColorChooser(new blackcolor()); if (kleurbox.getselectedindex()==1) DRAWPANEL.pluginColorChooser(new greencolor()); if (kleurbox.getselectedindex()==2) DRAWPANEL.pluginColorChooser(new redcolor()); if (kleurbox.getselectedindex()==3) DRAWPANEL.pluginColorChooser(new bluecolor()); Visuele Gebruikers Omgevingen: Swing /395

238 if (kleurbox.getselectedindex()==4) DRAWPANEL.pluginColorChooser(new magiccolor()); ); exitknop.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) { if (e.getsource()==exitknop) System.exit(0);); clearknop.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {if (e.getsource()==clearknop) c.repaint();); infoknop.addactionlistener(new ActionListener() {public void actionperformed(actionevent e) {if (e.getsource()==infoknop) { JOptionPane.showMessageDialog(null, "JPaint by Mike Ptacek\npmike@skynet.be","Info", JOptionPane.INFORMATION_MESSAGE); ); sizebox.additemlistener (new ItemListener() {public void itemstatechanged(itemevent ee) {DRAWPANEL.setSize((sizebox.getSelectedIndex()+1)*3); if (sizebox.getselectedindex()==4) DRAWPANEL.setSize(35); ); setdefaultcloseoperation(jframe.exit_on_close); setvisible(true); /* ******************* MAIN-PROCEDURE ******************** */ public static void main( String[] args ) {new Painter( ); Visuele Gebruikers Omgevingen: Swing /395

239 JTree import javax.swing.*; import javax.swing.tree.*; import java.awt.*; import java.io.*; class Test1 extends JFrame {JTree tree; public Test1(){Container c=getcontentpane(); c.setlayout(new FlowLayout()); Object [] diep={new File("bestand.txt"),"drieC"; Object [] drie={"driea","drieb",diep; Object [] top={"een","twee",drie; tree=new JTree(top); c.add(new JScrollPane(tree)); setsize(300,200); setvisible(true) ; public class Tree1 { static public void main(string args[]) {new Test1(); JTree zal een boom van objecten (DefaultMutableTreeNode) maken met als afgebeelde tekst de tostring waarde van elk object. Voor een array van objecten wordt wel een erg gekke String waarde gegenereerd. Intern wordt hiervoor (in een DefaultMutableTreeNode object) als userobject heel het File object bijgehouden. Visueel wordt String "twee" Object [ ] String String Object [ ] File Al deze objecten zijn van het type DefaultMutableTreeNode en bevatten een "userobject" (String, File, Object [ ],...). Tevens bevat de klasse DefaultMutableTreeNode methodes om 'parent' en 'children' van elke node op te vragen. Alsook routines om tijdens de uitvoering knooppunten toe te voegen. Visuele Gebruikers Omgevingen: Swing /395

240 Tree2 import javax.swing.*; import javax.swing.tree.*; import java.awt.*; import java.io.*; import java.util.*; class Test2 extends JFrame {JTree tree; public Test2(){Container c=getcontentpane(); public class Tree2 We erven over van Vector, zodat we een nieuwe tostring kunnen Vector diep=new Vector() {public String tostring(){return "DIEP NIVEAU"; ; diep.addelement(new File("bestand.txt")); diep.addelement("driec"); Vector drie=new Vector() {public String tostring(){return "NIVEAU DRIE";; drie.addelement("driea"); drie.addelement("drieb"); drie.addelement(diep); Vector top=new Vector() {public String tostring(){return "top"; ; top.addelement("een"); top.addelement("twee"); top.addelement(drie); tree= new JTree(top); tree.setrootvisible(true); c.add(new JScrollPane(tree)); setsize(300,200); setvisible(true); //als we na het openen van niveau drie nog elementen // toevoegen, worden deze niet getoond. // we zullen later expliciet moeten hertekenen for(int i=0;i<10;i+ +) try{thread.sleep( 1000); drie.addelement("drie"+i); catch(interruptedexception e) {System.out.println(e); { static public void main(string args[]) {new Test2(); Visuele Gebruikers Nu hebben Omgevingen: de knooppunten Swing wel 241 een goede 240/395 naam. Dit komt dankzij overerving van Vector, zodat we een nieuwe tostring kunnen

241 Tree3 import javax.swing.*; import javax.swing.tree.*; import java.awt.*; import java.io.*; import java.util.*; import javax.swing.event.*; In plaats van alle waarden op te sommen, kan je ook gewoon een Model opgeven dat een child of een root zal genereren. Met 'getchildcount' geven we op hoeveel kinderen elk parent knooppunt heeft. Met 'isleaf' geven we aan of het een class MyTreeModel implements TreeModel { Waarden public Object getchild(object parent, int index) //Returns child of parent at index index worden //in parent's child array. dynamisch {int parint=((integer)parent).intvalue(); gegenereer if(parint==0)//root d.(wanneer return new Integer(10+index); else nodig). return new Integer(parInt*10+index); public int getchildcount(object parent) // Returns the number of children of parent. {return 10; public int getindexofchild(object parent, Object child) // Returns the index of child in parent. {return 0; public Object getroot() // Returns the root of the tree. {return new Integer(0); public boolean isleaf(object node) // Returns true if node is a leaf. {int value=((integer)node).intvalue(); if( value==0 value%10==0) return false; return true; public void removetreemodellistener(treemodellistener l) { //Removes listener previously added with addtreemodellistener(). public void addtreemodellistener(treemodellistener l){ //Adds listener for TreeModelEvent posted after tree changes. public void valueforpathchanged(treepath path, Object newvalue){ // Messaged when the user has altered the value for the item //identified by path to newvalue. class Test3 extends JFrame {JTree tree; Voorlopig niet gebruikt. public Test3(){Container c=getcontentpane(); tree=new JTree(new MyTreeModel()); tree.setrootvisible(true); c.add(new JScrollPane(tree)); setsize(300,200); setvisible(true); public class Tree3 { static public void main(string args[]) {new Test3(); Visuele Gebruikers Omgevingen: Swing /395

242 We gaan nu file gaan bekijken, maar in plaats van Tree4 (vb JFC Java in a nutshell O'Reilly) eerst alle files van c: te lezen en dan pas te tonen, gaan we slechts de files lezen als de gebruiker deze import javax.swing.*; import javax.swing. tree.*; directory opent. Hiervoor kunnen we ook import java.awt.*; TreeModel gebruiken. Nu zal getchild een File import java.io.*; object genereren. TreeModel heeft heel wat import java.util.*; routines en we moeten ze allemaal implementeren import javax.swing.event.*; zonder ons af te vragen waar ze zullen gebruikt class MyTreeModel4 implements TreeModel d { public Object getchild(object parent, int index) //Returns child of parent at index index in parent's child array. {String [] children=((file)parent).list(); if((children==null) (index>=children.length))return null; return new File((File)parent, children[index] ); public int getchildcount(object parent) We gaan na hoeveel files er // Returns the number of children of parent. in de directory staan. {String [] children=((file)parent).list(); if(children==null) return 0; return children.length; public int getindexofchild(object parent, Object child) // Returns the index of child in parent. {String [] children=( (File)parent).list(); if(children==null) return -1; String childname=((file)child).getname(); for(int i=0;i<children.length;i++) Alleen files {if(childname.equals(children[i])) return i; zijn eindknooppunten, de rest zijn return -1;// komt niet voor public Object getroot() // Returns the root of the tree. {return new File("C:\\"); public boolean isleaf(object node){return public class Tree4 { static public void main(string args[]) {new Test4(); Hiervan vertrekken we ((File)node).isFile(); public void removetreemodellistener(treemodellistener l) { //Removes listener previously added with addtreemodellistener(). public void addtreemodellistener(treemodellistener l){ //Adds listener for TreeModelEvent posted after tree changes. public void valueforpathchanged(treepath path, Object newvalue){ //Messaged when the user has altered the value for the item //identified by path to newvalue. class Test4 extends JFrame {JTree tree; public Test4(){Container c=getcontentpane(); tree=new JTree(new MyTreeModel4()); tree.setrootvisible(true); c.add(new JScrollPane(tree)); setsize(300,200); setvisible(true); directories. Visuele Gebruikers Omgevingen: Swing /395

243 Visuele Gebruikers Omgevingen: Swing /395

244 Tree5 import javax.swing.*; import javax.swing.tree.*; public class Tree5 extends JTree { public Tree5() { DefaultMutableTreeNode rootnode = new DefaultMutableTreeNode(); DefaultMutableTreeNode apollonode = new DefaultMutableTreeNode("Apollo"); rootnode.add(apollonode); DefaultMutableTreeNode skylabnode =new DefaultMutableTreeNode("Skylab"); rootnode.add(skylabnode); DefaultMutableTreeNode n =new DefaultMutableTreeNode("11");apolloNode.add(n); n.add(new DefaultMutableTreeNode("Neil Armstrong")); n.add(new DefaultMutableTreeNode("Buzz Aldrin")); n.add(new DefaultMutableTreeNode("Michael Collins")); n = new DefaultMutableTreeNode("12");apolloNode.add(n); n.add(new DefaultMutableTreeNode("Pete Conrad")); n.add(new DefaultMutableTreeNode("Alan Bean")); n.add(new DefaultMutableTreeNode("Richard Gordon")); n = new DefaultMutableTreeNode("13");apolloNode.add(n); n.add(new DefaultMutableTreeNode("James Lovell")); n.add(new DefaultMutableTreeNode("Fred Haise")); n.add(new DefaultMutableTreeNode("Jack Swigert")); n = new DefaultMutableTreeNode("14");apolloNode.add(n); n.add(new DefaultMutableTreeNode("Alan Shephard")); n.add(new DefaultMutableTreeNode("Edgar Mitchell")); n.add(new DefaultMutableTreeNode("Stuart Roosa")); n = new DefaultMutableTreeNode("15");apolloNode.add(n); n.add(new DefaultMutableTreeNode("Dave Scott")); n.add(new DefaultMutableTreeNode("Jim Irwin")); n.add(new DefaultMutableTreeNode("Al Worden")); n = new DefaultMutableTreeNode("16");apolloNode.add(n); n.add(new DefaultMutableTreeNode("John Young")); n.add(new DefaultMutableTreeNode("Charlie Duke")); n.add(new DefaultMutableTreeNode("Ken Mattingly")); n = new DefaultMutableTreeNode("17");apolloNode.add(n); n.add(new DefaultMutableTreeNode("Eugene Cernan")); n.add(new DefaultMutableTreeNode("Harrison Schmidt")); n.add(new DefaultMutableTreeNode("Ron Evans")); n = new DefaultMutableTreeNode("2"); skylabnode.add(n); n.add(new DefaultMutableTreeNode("Pete Conrad")); n.add(new DefaultMutableTreeNode("Joseph Kerwin")); n.add(new DefaultMutableTreeNode("Paul Weitz")); n = new DefaultMutableTreeNode("3"); skylabnode.add(n); Visuele Gebruikers Omgevingen: Swing /395

245 n.add(new DefaultMutableTreeNode("Alan Bean")); n.add(new DefaultMutableTreeNode("Owen Garriott")); n.add(new DefaultMutableTreeNode("Jack Lousma")); n = new DefaultMutableTreeNode("4"); skylabnode.add(n); n.add(new DefaultMutableTreeNode("Gerald Carr")); n.add(new DefaultMutableTreeNode("Edward Gibson")); n.add(new DefaultMutableTreeNode("William Pogue")); this.setmodel(new DefaultTreeModel(rootNode)); public static void main(string[] args) { JFrame f = new JFrame( "Tree5"); Tree5 t = new Tre e5(); t.putclientproperty("jtree.linestyle", "Angled"); t.expandrow(0); f.getcontentpane().add(new JScrollPane(t)); f.setsize(300, 300); f.setvisible(true); In dit voorbeeld maken we een boom van DefaultMutableTreeNode objecten. We hangen eerst alle nodes aan elkaar. Vervolgens geven de rootnode door aan een treemodel en maken w e de tree. A an een tree kunnen we alleen maar klassen die de volgende interfacen implementeren toevoegen: TreeNo de: public Enumeration children() public boolean getallowschildren() public TreeNode getchildat(int index) public int getchildcount() public int getindex(treenode child) public TreeNode getparent() public boolean isleaf() Een TreeNode hoeft niet visueel te zijn. Als u gewoon een boom van gegevens wilt bijhouden in uw programma, kunt u ook TreeNode gebruiken. DefaultMutableTreeNode implementeert deze interface. MutableTreeNode (implementeert ook TreeNode +:) public void insert(mutabletreenode child, int index) public void remove(int index) public void remove(mutabletreenode node) public void removefromparent() publi c void setparent(mutabletreenode parent) publi c void setuserobject(object userobject) Een stapje verder: u kunt dynamisch objecten toevoegen en verwijderen. Let op de setuserobject: elk MutableTreeNode object kan hierdoor een userobject DefaultMutableTreeNode implementeert deze interface. DefaultMutableTreeNode heeft zeer veel methodes om heel de boom af te lopen. Bekijk maar eens. Visuele Gebruikers Omgevingen: Swing /395

246 Tree6 import javax.swing.*; import javax.swing.tree.*; import java.util.*; public class Tree6 { public static void main(string[] args) { JFrame f = new JFrame("Tree 6:Enumerations"); DefaultMutableTreeNode rootnode = new DefaultMutableTreeNode("Root"); for (int i = 0; i < 2; i++) { DefaultMutableTreeNode a = new DefaultMutableTreeNode("" + i); for (int j = 0; j < 2; j++) { DefaultMutableTreeNode b = new DefaultMutableTreeNode("" + i + "_" + j); for (int k = 0; k < 2; k++) { b.add(new DefaultMutableTreeNode(""+i+"_"+j +"_" +k)); a.add(b); rootnode.add(a); JTree t = new JTree(rootNode); t.putclientproperty("jtree.linestyle", "Angled"); f.getcontentpane().add(new JScrollPane(t)); f.setsize(300, 300); f.setvisible(true); Geeft een iterator terug om het knooppunt af te lopen op een // Now show various enumerations speciale wijze. printenum("preorder", rootnode.preorderenumeration()); printenum("postorder", rootnode.postorderenumeration()); printenum("breadth First", rootnode.breadthfirstenumeration()); public static void printenum(string title, Enumeration e) { System.out.println("==== ===========\n" + title); while (e.hasmoreelements()) { System.out.print(e.nextElement() + " "); System.out.println("\n"); Iterator om boom af te lopen. =============== Preorder Root 0 0_0 0_0_0 0_0_1 0_1 0_1_0 0_1_1 1 1_ 0 1_0_0 1_0_1 1_1 1_1_0 1_1_1 =============== Postorder 0_0_0 0_0_1 0_0 0_1_0 0_1_1 0_1 0 1_1 1 Root =============== Breadth First 1_0_0 1_0_1 1_0 1_1_0 1_1_ 1 Root 0 1 0_0 0_1 1_0 1_1 0_0_0 0_0_1 0_1_0 0_1_1 1_0_0 1_0_1 1_1_0 1_1_1 Visuele Gebruikers Omgevingen: Swing /395

247 U merkt: DefaultMutableTreeNode heeft handige iterators om heel de boom (of een deel ervan) af te lopen. Tree6B import javax.swing.*; import javax.swing.tree.*; import java.util.*; import java.io.*; public class Tree6B { public static void main(string[] args) { JFrame f = new JFrame("Tree 6B"); DefaultMutableTreeNode rootnode = new DefaultMutableTreeNode("Root"); for (int i = 0; i < 2; i++) { DefaultMutableTreeNode a = new DefaultMutableTreeNode("" + i); for (int j = 0; j < 2; j++) { DefaultMutableTreeNode b =new DefaultMutableTreeNode(""+i+"_"+j); for (int k = 0; k < 2; k++) b.add(new DefaultMutableTreeNode(""+i+"_"+ j + "_" + k)); a.add(b); rootnode.add( a); JTree tree = new JTree(rootNode); f.getcontentpane().add(new JScrollPane(tree)); f.setsize(300, 300);f.setVisible(true); // de data staat los van de tree en kan los ervan gemanipuleerd //worden. Dit treemodel wordt automatisch opgebouwd // en bevat objecten voor alle elementen van onze tree DefaultTreeModel data = (DefaultTreeModel)tree.getModel(); bevat alle gegevens van de boom. // we voegen aan de root een File toe: DefaultMutableTreeNode root = (DefaultMutableTreeNode)data.getRoot(); data.insertnodeinto(new DefaultMutableTreeNode( new File("foert.txt")), data.reload(root); root,0); // we lopen alles eens af vertrekkend van het datamodel print(root); static void print(defaultmutabletreenode n) {// de klasse DefaultMutableTreeNode bevat alles nodig // om heel het datamodel af te lopen. (De klasse DefaultTreeModel // bevat ook routines hiervoor. Je kan dus kiezen. //Ik kies nu eens // DefaultMutableTreeNode om alles af te lopen en //niet DefaultTreeModel System.out.println(n); DefaultMutableTreeNode x=n.getnextnode(); while(x!=null) We lopen alleen de bladeren van de boom af. {System.out.println(x); x=x.getnextleaf(); Visuele Gebruikers Omgevingen: Swing /395

248 /* andere handige functies: getnextnode, getnextleaf, getnextsibling, getpreviousnode, getpreviousleaf, getprevioussibling, getroot isleaf, getchildcount, getdepth, getleafcount, getsiblingcount, getuserobject : speciaal om het omkapselde object te vragen setuserobject : om het geassocieerde object te wijzigen add(mutabletreenode) insert(mutabletreenode,plaats)*/ Root Een File object werd foert.txt 0_0_0 0_0_1 0_1_0 0_1_1 1_0_0 1_0_1 1_1_0 1_1_1 achteraf aan het datamodel toegevoegd Tree7A import javax.swing.*; Wijzigbaar! Pas wel op. U typt een String in en als het oorspronkelijke object geen String was, zal het toch vervangen worden door een String. import javax.swing.tree.*; import java.awt.*; import java.io.*; import java.util.*; import javax.swing.event.*; class Test7A extends JFrame {JTree tree; public Test7A() {Container c=getcontentpane(); Vector diep=new Vector() {public String tostring() {return "DIEP NIVEAU";; diep.addelement(new File("bestand.txt")); Visuele Gebruikers Omgevingen: Swing /395

249 diep.addelement("driec"); Vector drie=new Vector() {public String tostring() {return "NIVEAU DRIE";; drie.addelement("driea");drie.addelement("drieb"); drie.addelement(diep) ; Vector top=new Vector() {public String tostring(){return "top"; ; top.addelement("een");top.addelement("twee"); top.addelement(drie); tree=new JTree(top); tree.setrootvisible(true); c.add(new JScrollPane(tree));setSize(300, 200);setVisible(true); // JTree zal voor elk element van de vector een // DefaultMutableTreeNode // object aanmaken en dit linken aan de andere nodes. tree. seteditable(true);//!! PROBEER EENS, HET WERKT tree.addtreeselectionlistener(new TreeSelectionListener() { public void valuechanged(treeselectionevent evt) { // welk 'path' heeft de gebruiker geselecteerd? // een 'path' bestaat uit een aaneenschakeling van alle // 'voorgaande' knooppunten. // Dit zijn allemaal DefaultMutableTreeNode objecten TreePath selecttreepath=tree.getselectionpath(); System.out.println(" "); System.out.println("Selected:"+selectTreePath ); //tostring wordt opgeroepen // probeer eens meer dan één element te selecteren //(shift,ctrl) System.out.println("aantal geselecteerd:" +tree.getselectioncount()); if (tree.getselectioncount() >1) {TreePath [] alles=tree.getselectionpaths(); for(int i=0;i< alles.length;i++) System.out.println(""+i+":"+alles[i]); ); public class Tree7A { static public void main(string args[]) {new Test7A(); /* handige functies van JTree: setrootvisible(true/false) boolean isselectionempty() setselectionpaths(treepath[]) collapsepath(treepath) expandpath(treepath) TreePath getclosestpathforlocation(int x,int y) TreePath getpathforlocation(int x,int y) boolean hasbeenexpanded(treepath) boolean iscollapsed(treepath) boolean ispatheditable(treepath) boolean ispathselected(treepath) boolean isvisible(treepath) makevisible(treepath) Visuele Gebruikers Omgevingen: Swing /395

250 */ Als we deze twee elementen selecteren, wordt de volgende uitvoer Selected:[root, NIVEAU DRIE, DIEP NIVEAU, bestand.txt] aantal geselecteerd:2 0:[root, NIVEAU DRIE, DIEP NIVEAU, bestand.txt] 1:[root, NIVEAU DRIE, DIEP NIVEAU, driec] Belangrijk is dat de selectie door de gebruiker wordt weergegeven door een TreePath object. Dit is een aaneenschakeling van alle knooppunten die leiden tot het geselecteerd element. Het bevat dus meerdere objecten en deze meerdere objecten kunnen we uit het TreePath object halen. Een TreeSelectionListener gaat kunnen reageren op selecties van de gebruiker. Tree7B import javax.swing.*; import javax.swing.tree.*; import java. awt.*; import java.io.*; import java.util.*; import javax.swing.event.*; class Test7B extends JFrame {JTree tree; public Test7B() {Container c=getcontentpane(); Vector diep=new Vector() {public String tostring(){return "DIEP NIVEAU";; diep.addelement(new File("bestand.txt")); diep.addelement("driec"); Vector drie=new Vector() {public String tostring(){return "NIVEAU DRIE";; drie.addelement("driea");drie.addelement("drieb"); drie.addelement(diep); Vector top=new Vector() {public String tostring(){return "top"; ; top.addelement("een");top.addelement("twee"); top.addelement(drie); tree=new JTree(top); tree.setrootvisible(true); Visuele Gebruikers Omgevingen: Swing /395

251 c.add(new JScrollPane(tree));setSize(300,200);setVisible(true); tree.addtreeselectionlistener(new TreeSelectionListener() { public void valuechanged(treeselectionevent evt) { TreePath [] alles=tree.getselectionpaths(); for(int i=0;i< alles.length;i++) {TreePath selectedpath=alles[i]; System.out.println(" "); System.out.print(""+i+":"+selectedPath); // een TreePath bestaat uit alle knooppunten die tot het //geselecteerde onderdeel leiden: // we kunnen alle onderdelen (Objecten) eruit halen: Object [] partsselecttreepath=selectedpath.getpath(); for(int j=0;j<partsselecttreepath.length;j++) {System.out.println(); System.out.print("\tpart"+j+": " +partsselecttreepath[j]+"\t ["); if(partsselecttreepath[j] instanceof DefaultMutableTreeNode) {// er zit een DefaultMutableTreeNode rond ons node object Object uo = ((DefaultMutableTreeNode) partsselecttreepath[j]).getuserobject(); if (uo!= null) // root heeft geen userobject System.out.print(uo.getClass());//We laten System.out.print("]"); System.out.println(); System.out.println("Dadelijk laatste deel geklikt type zien +selectedpath.getlastpathcomponent() +"***(DefaultMutableTreeNode)"); ); public class Tree7B { static public void main(string args[]) {new Test7B(); TreePath:***" geselecteer d TreePath element Met getlastpathcomponent verkrijg je dadelijk het geselecteerde f l bl d l :[root, NIVEAU DRIE, DIEP NIVEAU, bestand.txt] part0: root [class java.lang.string] Geeft klasse van part1: NIVEAU DRIE [class Test7B$2] userobject van part2: DIEP NIVEAU [class Test7B$1] element van boom part3: bestand.txt [class java.io.file] weer. Meestal Dadelijk laatste deel geklikt TreePath: *** bestand.txt String, *** maar ook (DefaultMutableTreeNode) File naargelang wat 1:[root, NIVEAU DRIE, DIEP NIVEAU, driec] je er zelf instopt part0: root [class java.lang.string] Visuele Gebruikers Omgevingen: Swing /395

252 part1: NIVEAU DRIE [class Test7B$2] part2: DIEP NIVEAU [class Test7B$1] part3: driec [class java.lang.string] Dadelijk laatste deel geklikt TreePath: *** driec *** (DefaultMutableTreeNode) Alle onderdelen van het geselecteerde TreePath zijn DefaultMutableTreeNode elementen. Tree8 : icoontjes, font, kleuren aanpassen import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.awt.*; public class Tree8{ public static void main(string[] args) { JFrame f = new JFrame("Tree Selection Example"); Tree5 tree = new Tree5();//Apollo, Skylab tree.putclientproperty("jtree.linestyle", "Angled"); tree.expandrow(0) ; // Een DefaultTreeCellRenderer is een JLabel // waarvan we verschillende dingen kunnen aanpassen TreeCellRenderer cellrenderer=tree.getcellrenderer(); if (cellrenderer instanceof DefaultTreeCellRenderer) {DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer)cellrenderer; renderer.setleaficon(new ImageIcon("Leaf.gif")); renderer.setclosedicon(new ImageIcon("Closed.gif")); renderer.setopenicon(new ImageIcon("Open.gif")); renderer.setbackgroundnonselec tioncolor(color.white); renderer.setbackgroundselectioncolor(color.red); renderer.settextnonselectioncolor(color.blue); renderer.settextselectioncolor(color.orange); renderer.setfont(new Font("Monospaced",Font.ITALIC,11)); renderer.setborderselectioncolor(color.green); //tree.setbackground(color.yellow);//achtergrond tree f.getcontentpane().add(new JScrollPane(tree)); f. setsize(300, 300); f.setvisible(true); In dit voorbeeld werken we met de boom van Tree5 voorbeeld (Apollo, Skylab). Dit voorbeeld komt uit een boek van Topley. We gaan de layout gaan aanpassen. Dit doen we door het bestaande CellRenderer object aan te passen. (We kunnen ook een nieuw CellRenderer object inpluggen.) Dit Visuele Gebruikers Omgevingen: Swing /395

253 object staat in voor de presentatie van alle knooppunten en bladeren van de boom. Zoals u merkt kunt u icoontjes en kleuren aanpassen op een eenvoudige wijze. Tree9 : zelf een TreeCellR enderer klasse maken. X : GeëXpandeerd SX : Selected, expanded SL : Selected Leaf Vooraan hebben we het rij nummer gezet (gewoon het nummer van de visuele rij in de boom). Bij openen van een tak worden alle nummers van latere rijen aangepast L : Leaf import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java. awt.*; cl ass MyTreeCellRenderer implements TreeCellRenderer {public Component gettreecellrenderercompon ent (JTree tree, // de boom Object value, // meestal een DefaultMuta bletreenode boolean selected,//geselecteerd? boolean expanded,/ / opengeklapt? boolean leaf, // is het een eind punt? int row, // op welke rij komt dit component boolean hasfocus) { DefaultMutableTreeNode node = (DefaultMutableTreeNod e)value; Object obj = node.getuserobject();// hier een String, ma ar kan //gelijk wat zijn zoals een File String status=new String(""+row+" "); if(selected) status+="s"; if(expanded) status+="x"; if(leaf) status+="l"; return new JLabel(status+" "+ obj); public class Tree9{ public static void main( String[] args) { JFrame f = new JFrame( "Tree Selection Example"); Tr ee5 tree = new Tree5();//Apollo, Skylab tree.putclientproperty("jtree.linestyle", "Angled"); tree.expandrow(0); tree.setcellrenderer(new MyTreeCellRenderer()); Een nieuwe f.getcontentpane().add(new JScrollPane(tree)); CellRenderer wordt ingeplugd. Visuele Gebruikers Omgevingen: Swing /395

254 f.setsize(300, 300); f.setvisible(true); Weeral werken we met de vorige boom Tree5 (Apollo, Skylab). Een eigen TreeCellRenderer werkt eigenlijk heel eenvoudig. We moeten één routine opgeven : gettreecellrenderercomponent. Deze routine geeft een Component terug: het component dat wordt getoond. Meestal wordt hiervoor een JLabel genomen omdat je hiermee zowel tekst als het bijhorend icoontje kunt weergeven. De routine wordt opgeroepen voor elk element van het visueel deel van de boom en heeft als parameters: JTree tree, // de boom Object value, // meestal een DefaultMutableTreeNode boolean selected,//geselecteerd? boolean expanded,// opengeklapt? boolean leaf, // is het een eindpunt? int row, // op welke rij komt dit component boolean hasfocus) In 'value' zit het element van de boom waarvoor we een Component moeten genereren. Dit is meestal een DefaultMutableTreeNode object. Naargelang dit element geselecteerd of geëxpandeerd of een eindpunt is, kunnen we andere inhoud aan het te genereren component geven. In dit voorbeeld veranderen we gewoon de tekst van de JLabel die we genereren. Tree9B : crazy layout : soms icon, soms een textfield, soms Label import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.awt.*; class MyTreeCellRendererB implements TreeCellRenderer {public Component gettreecellrenderercomponent (JTree tree, // de boom Object value, // meestal een DefaultMutableTreeNode boolean selected,//geselecteerd? boolean expanded,// opengeklapt? boolean leaf, // is het een eindpunt? int row, // op welke rij komt dit component boolean hasfocus) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; Object obj = node.getuserobject();// hier een String String status=new String(""+row+" "+obj+" status+=selected?"selected ":" "; status+=expanded?"expanded ":" "; status+=leaf?"leaf ":" "; "); JLabel label =new JLabel(); label.setfont(new Font("Monospaced",Font.PLAIN,14)); label.settext(status); if(leaf && row <7) label.seticon(new ImageIcon("leaf.icon")); if(selected) {label.setfont(new Font("Monospaced",Font.ITALIC,14)); label.setborder( BorderFactory.createLineBorder(Color.green) ); Visuele Gebruikers Omgevingen: Swing /395

255 if(row>12){jbutton but=new JButton(status); return but; if(!leaf) return label; else if(!selected)return new JTextField(status); else {JTextField field=new JTextField(status); field.setborder( BorderFactory.createLineBorder(Color.red) ); return field; public class Tree9B{ public static void main(string[] args) { JFrame f = new JFrame("Tree Selection Example"); Tree5 tree = new Tree5();//Apollo, Skylab tree.putclientproperty("jtree.linestyle", "Angled"); tree.expandrow(0); tree.setcellrenderer(new MyTreeCellRendererB()); tree.seteditable(true); f.getcontentpane().add(new JScrollPane(tree)); f.setsize(300, 300); f.setvisible(true); JLabel JTextField geselecteerd : ander font (italic)+ kader Indien rij > 12 : JB tt Tree9C import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.awt.*; import java.io.*; Visuele Gebruikers Omgevingen: Swing /395

256 public class Tree9C{ public static void main(string[] args) { JFrame f = new JFrame("Tree Selection Example"); Tree5 tree = new Tree5();//Apollo, Skylab tree.putclientproperty("jtree.linestyle", "Angled"); tree.expandrow(0); // het volgende is voldoende om de waarden van de knooppunten // te wijzigen: // pas wel op: we hebben als userobject van één object een // File object genomen (zie Tree5). De String waarde hiervan // zien we in de boom. Als u deze wijzigt, typt u een String in // en zal het userobject worden gewijzigd naar een String. tree.seteditable(true); // we kunnen een JCheckBox, JComboBox of een JTextField // doorgeven aan een DefaultCellEditor. // Deze DefaultCellEditor is ook te gebruiken bij JTable's. JComboBox combo =new JComboBox(new Object[] {"Mercury","Gemini", "Apollo","Skylab", new File("Bestand2.txt")); // Als u "Bestand2.txt neemt, dan kiest u voor een 'File' object // de andere opties zijn gewoon Strings. // Denk eraan: knooppunten van een JTree kunnen uit gelijk welk // object samengesteld zijn. DefaultCellEditor editor=new DefaultCellEditor(combo); tree.setcelleditor(editor); f.getcontentpane().add(new JScrollPane(tree)); f.setsize(300, 300); f.setvisible(true); Visuele Gebruikers Omgevingen: Swing /395

257 Default hebben we als CellEditor een JTextField. We kunnen dit bijvoorbeeld vervangen door een JComboBox door deze aan de constructor van de CellEditor mee te geven. Probleem is wel dat deze CellEditor zal gebruikt worden voor alle knooppunten van de boom. U moet dus werkelijk alle waarden van eindbladen en knooppunten opgeven. Dit kan een probleem zijn. Dezelfde CellEditor kan ook wel gebruikt worden bij JTable. U moet vooral onthouden dat er een apart object is (CellEditor) dat instaat voor het veranderen van de waarde van elk element van de boom. Desnoods kunt u overerven van CellEditor of kunt u een eigen klasse maken die deze interface implementeert. Tree9D : alleen eindbladen editeerbaar maken import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.awt.*; import java.io.*; import java.util.*; import java.awt.event.*; public class Tree9D{ public static void main(string[] args) { JFrame f = new JFrame("Tree Selection Example"); final Tree5 tree = new Tree5();//Apollo, Skylab tree. putclientproperty("jtree.linestyle", "Angled"); tree.expandrow(0); tree.seteditable(true); JComboBox combo =new JComboBox( new Object[]{"Peeters","Janssens", "Amstrong","Geenen", new File("Bestand2.txt")); DefaultCellEditor editor=new DefaultCellEditor(combo) {public boolean iscelleditable(eventobject evt) {if (evt instanceof MouseEvent) {TreePath path=tree.getpathforlocation(((mouseevent)evt).getx(), ((MouseEvent)evt).getY()); DefaultMutableTreeNode o = (DefaultMutableTreeNode)path.getLastPathComponent(); return o.isleaf(); return false; ; tree.setcelleditor(editor); f.getcontentpane().add(new JScrollPane(tree)); f.setsize(300, 300); f.setvisible(true); We erven hier over van DefaultCellEditor. Let op hoe dat we toch nog aan de constructor van DefaultCellEditor het combo object doorgeven. We herdefinieren de functie iscelleditable. Deze heeft als parameter de event waarmee we het element selecteren. Wij moeten nu uitgaande van dit event bepalen of deze cell al dan niet editeerbaar is door al dan niet true terug te geven als resultaat van de functie. Hoe halen we uit de muisklik het TreePath object waarop geklikt werd? We kunnen uit het MouseEvent de X en Y positie halen van waar er geklikt werd. Dan kunnen we met de 'getpathforlocation aan de boom vragen welk TreePath bij deze coordinaten hoort. Het laatste DefaultMutableTreeNode element uit dit TreePath is de TreeNode waarop we geklikt hebben. Als dit een Leaf is, besluiten dat de gebruiker dit element mag editeren. Voor knooppunten zal altijd false teruggegeven worden. U mag hier zoveel op klikken als u wenst, er gebeurt niets. Visuele Gebruikers Omgevingen: Swing /395

258 Tree99 :stamboom Telkens u een knooppunt opent, komen er dynamisch twee knooppunten bij met als naam 'Vader van' en 'Moeder van' import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.tree.*; class IconCellRenderer extends JLabel implements TreeCellRenderer { protected boolean m_selected; public IconCellRenderer() {super(); setopaque(false); public Component gettreecellrenderercomponent(jtree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasfocus) {// we halen userobject uit value DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; IconData obj = (IconData)node.getUserObject(); settext(obj.tostring());//zetten tekst(uit userobject)in label if (expanded) seticon(obj.getexpandedicon()); else seticon(obj.geticon()); setfont(tree.getfont());// mooier setforeground(sel? Color.red : Color.blue); setbackground(sel? Color.yellow : Color.white); m_selected = sel; return this; public void paintcomponent(graphics g) {Color bcolor = getbackground(); Icon icon = geticon(); g.setcolor(bcolor); int offset = 0; if(icon!= null && gettext()!= null) offset = (icon.geticonwidth() + geticontextgap()); g.fillrect(offset,0,getwidth()-1-offset,getheight() - 1); if (m_selected) { g.setcolor(color.green); g.drawrect(offset, 0, getwidth()-1-offset, getheight()-1); super.paintcomponent(g); class IconData { protected Icon m_icon; Visuele Gebruikers Omgevingen: Swing /395

259 protected Icon m_expandedicon; protected Object m_data; public IconData(Icon icon, Object data) { m_icon = icon;m_expandedicon = null;m_data = data; public IconData(Icon icon, Icon expandedicon, Object data) { m_icon = icon;m_expandedicon = expandedicon; m_data = data; public Icon geticon() { return m_icon; public Icon getexpandedicon() { return m_expandedicon!=null? m_expandedicon : m_icon; public Object getobject() { return m_data; public String tostring() { return m_data.tostring(); public class Tree99 extends JFrame { public static ImageIcon ICON_SELF = new ImageIcon("myself.gif"); public static ImageIcon ICON_MALE = new ImageIcon("male.gif"); public static ImageIcon ICON_FEMALE =new ImageIcon("female.gif"); protected JTree m_tree; protected DefaultTreeModel m_model; protected IconCellRenderer m_renderer; protected IconCellEditor m_editor; public Tree99() {super("ancestor Tree"); setsize(500, 400); DefaultMutableTreeNode top = new DefaultMutableTreeNode( new IconData(ICON_SELF, "Myself")); addancestors(top); // we voegen steeds twee voorouders toe m_model = new DefaultTreeModel(top); m_tree = new JTree(m_model); m_tree.getselectionmodel(). setselectionmode( TreeSelectionModel.SINGLE_TREE_SELECTION); m_tree.setshowsroothandles(true); m_tree.seteditable(true); m_renderer = new IconCellRenderer(); m_tree.setcellrenderer(m_renderer); m_editor=new IconCellEditor(m_tree); m_tree.setcelleditor(m_editor); m_tree.setinvokesstopcellediting(true); m_tree.addmouselistener(new TreeExpander()); JScrollPane s = new JScrollPane(); s.getviewport().add(m_tree); getcontentpane().add(s, BorderLayout.CENTER); WindowListener wndcloser = new WindowAdapter() { public void windowclosing(windowevent e){ System.exit(0);; addwindowlistener(wndcloser); setvisible(true); public boolean addancestors(defaultmutabletreenode node) { if (node.getchildcount() > 0) return false; Object obj = node.getuserobject(); node.add(new DefaultMutableTreeNode( new IconData( ICON_MALE, "Vader van: "+obj.tostring()) )); node.add(new DefaultMutableTreeNode( new IconData( ICON_FEMALE, "Moeder van: "+obj.tostring()) )); return true; public static void main(string argv[]) { new Tree99(); class TreeExpander extends MouseAdapter { public void mouseclicked(mouseevent e) {if (e.getclickcount() == 2) // dubbel geklikt? {// we zoeken het pad waarop we geklikt hebben TreePath selpath=m_tree.getpathforlocation(e.getx(),e.gety()); if (selpath == null) return;// niet op een TreePath geklikt DefaultMutableTreeNode node = Visuele Gebruikers Omgevingen: Swing /395

260 (DefaultMutableTreeNode) (selpath.getlastpathcomponent()); if (node!=null && addancestors(node) ) {m_tree.expandpath(selpath);//klappen het geselecteerde open m_tree.repaint(); class IconCellEditor extends JLabel implements TreeCellEditor, ActionListener {protected JTree m_tree = null; protected JTextField m_editor = null; protected IconData m_item = null; protected int m_lastrow = -1; protected long m_lastclick = 0; protected Vector m_listeners = null; public IconCellEditor(JTree tree) {super(); m_tree = tree; m_listeners = new Vector(); public Component gettreecelleditorcomponent(jtree tree, Object value, boolean isselected, boolean expanded, boolean leaf, int row) {DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; IconData obj = (IconData)node.getUserObject(); IconData idata = (IconData)obj; m_item = idata; // Reserve some more space... settext(idata.tostring()+" "); // tekst van edit label seticon(idata.m_icon);//tijdens edit moet er ook een icoon zijn return this; // BELANGRIJK : de default celleditor maakt er een string van va n // vervangt het oorspronkelijk userobject door een string // dit mag niet gebeuren omdat icon wordt bijgehouden in userobject. public Object getcelleditorvalue() {if (m_item!= null && m_editor!= null) m_item.m_data = m_editor.gettext(); return m_item; // we geven hier geen String terug zoals de //standaard CellEditor public boolean iscelleditable(eventobject evt) {if (evt instanceof MouseEvent) {MouseEvent mevt = (MouseEvent)evt; if (mevt.getclickcount() == 1) {int row = m_tree.getrowforlocation(mevt.getx(), mevt.gety()); if (row!= m_lastrow) {m_lastrow = row; m_lastclick = System.currentTimeMillis(); return false; else if (System.currentTimeMillis()-m_lastClick > 1000) {m_lastrow = -1; m_lastclick = 0;prepareEditor(); mevt.consume(); return true; else return false; return false; protected void prepareeditor() {if (m_item == null) return; String str = m_item.tostring(); m_editor = new JTextField(str); m_editor.addactionlistener(this); m_editor.selectall(); m_editor.setfont(m_tree.getfont());add(m_editor); revalidate(); TreePath path = m_tree.getpathforrow(m_lastrow); m_tree.starteditingatpath(path); protected void removeeditor() Visuele Gebruikers Omgevingen: Swing /395

261 {if (m_editor!= null) {remove(m_editor); m_editor.setvisible(false); m_editor = null; m_item = null; public void dolayout() {super.dolayout(); if (m_editor!= null) {int offset = geticontextgap(); if (geticon()!= null) offset += geticon().geticonwidth(); Dimension csize = getsize(); m_editor.setbounds(offset, 0, csize.width offset,csize.height); public boolean shouldselectcell(eventobject evt) { return true; public boolean stopcellediting() {if (m_item!= null) m_item.m_data = m_editor.gettext(); ChangeEvent e = new ChangeEvent(this); for (int k=0; k<m_listeners.size(); k++) {CellEditorListener l = (CellEditorListener)m_listeners. elementat(k); l.editingstopped(e); removeeditor(); return true; public void cancelcellediting() {ChangeEvent e = new ChangeEvent(this); for (int k=0; k<m_listeners.size(); k++) {CellEditorListener l = (CellEditorListener)m_listeners. elementat(k); l.editingcanceled(e); removeeditor(); public void addcelleditorlistener(celleditorlistener l) {m_ listeners.addelement(l); public void removecelleditorlistener(celleditorlistener l) {m_listeners.removeelement(l); public void actionperformed(actionevent e) {stopcellediting(); m_tree.stopediting(); We hebben nu zowel een eigen CellRenderer als een eigen CellEditor. Waarom is de code ingewikkeld? Bij editeren, vervangen we de oorspronkelijke objecten niet door Strings. Visuele Gebruikers Omgevingen: Swing /395

262 belang Componenten = lego-blokjes om programma s te maken Een succes-verhaal: office werkt uitgebreid met componenten voor tekenen,...die ook in andere programma s gebruikt worden (componenten van word worden ook in powerpoint en excel gebruikt). Verhoogt de productiviteit van programmatie enorm. Componenten Er zijn componenten om met databanken te werken,... Voor internettoepassingen worden nu ook componenten gebruikt : java server faces, asp.net (webcontrols) Even een demo Sun levert een eenvoudig programma (BeanBox) om het concept van componenten te illustreren. Je kan ermee componenten naar een ontwerpscherm slepen en ze onderling door acties met elkaar verbinden. De toepassing werkt dadelijk 262/395 1

263 Links staan de componenten om te slepen. Rechts hun eigenschappen (properties) Het ontwerpscherm De achtergrondkleur instellen Als je op het ontwerpscherm klikt, verschijnen rechts zijn properties: background, foreground, name. We veranderen eens de achtergrond We slepen een explicit button naar ontwerpscherm We hebben hier vier properties: background, foreground kleur, label, font Op de juiste plaats zetten Je kan de componenten gewoon verslepen en vergroten Kan gewoon vergroot worden met de muis 263/395 2

264 Een tweede button We voegen een tweede button toe, en veranderen de tekst van die button We voegen juggler component toe De juggler component heeft methodes: -stopjuggling, startjuggling Het heeft properties: snelheid van jongleren De buttons hebben ook events: button push (actionperformed): het gewoon klikken op een button Een juggler Vervolgens moeten we het event klikken op de button verbinden met het oproepen van een methode van het juggler object De beanbox genereert zelf code In tegenstelling tot de meeste andere tools die met componenten werken, zal de beanbox code genereren. U verbindt met klikken op de componenten en kiezen van routines die opgeroepen worden wat er moet gebeuren. De beanbox zal de gegenereerde code compileren en dadelijk laten werken. 264/395 3

265 We kiezen eerst het event Visual Age van IBM genereert ook code. Tools van Borland en Sun genereren lege functies die zullen opgeroepen worden wanneer het event zich voordoet. De programmeur moet die lege functies opvullen. We selecteren eerst de button en vervolgens het event waarvoor we actie willen. Als we op de "Stop the Animation" button klikken (actionperformed) zullen we een methode van een ander (door ons te kiezen) object oproepen. Andere mogelijke events Versleep de rode lijn naar een ander object Je kan nu met de muis aanduiden van welk ander object er een methode zal moeten opgeroepen worden. Target the selector Line: kies het Juggler component. 265/395 4

266 Kies methode die in de gegenereerde code wordt opgeroepen. Kies stopjuggling We kiezen de methode die we zullen oproepen als we op "Stop the Animation" klikken. U moet steeds methodes zonder parameters kiezen. (met deze eenvoudige toolkit, kunnen we geen parameters opgeven.) Hoe kent de toolkit deze methodes: gewoon door introspectie: van gelijk welk object kan je zijn methodes opvragen. Code generatie Er wordt code gegenereerd om bij het event klikken op button de methode stopjuggling van het juggler object op te roepen. Dynamisch genereert de toolkit een listenerklasse (ActionListener) en zal deze toevoegen met addactionlistener aan de button. Hierdoor zal bij het klikken op de button effectief de methode stopjuggling opgeroepen worden. Analoog voor de ander button De toepassing werkt dadelijk. U kunt steeds jongleren op en af zetten, de snelheid regelen. (property aanpassen). Hoe componenten in java verpakken? Gewoon een zip file die hernoemd wordt in jar file. In een subdirectory meta-inf staat een bestand manifest.mf met daarin de naam van de klasse die de component voorstelt. 266/395 5

267 De jar bekijken Met winzip kan je de inhoud van de jar bekijken. Deze jar bevat naast de juggler klasse ook alle andere benodigde bestanden zoals gif bestanden. meta-inf/manifest.mf In het manifest.mf bestand staat de naam van de klasse die de bean vormt. De naam van deze klasse zal bij de componenten getoond worden. Er wordt aangegeven dat er slechts één klasse (Juggler.class) als bean in tools zoals beanbox mag geladen worden. De rest zijn aldus ondersteunende klassen. JBuilder 6 Properties bij jbuilder 267/395 6

268 Events bij jbuilder Tool: visual cafe tientallen javabeans, voor databanken, multimedia,... properties per component; Hier zie je voor de Button enkele gridbagcontraints. interactie tussen visuele en niet visuele component (data van JTable) GridBagLayo ut Door rechts te klikken op een willekeurig component, krijg je een popop menu, waar je 'add interaction' kan kiezen. Een gok-toepassing Eerst eens met beanbox Vanboven: een gewone knop, bij klikken worden de twee objecten eronder (Willekeurig-object) aangestuurd. 268/395 7

269 De werkende toepassing De range van de twee Willekeurig objecten wordt op 20 en 100 gezet. Ze zullen willekeurige getallen binnen deze range genereren Willekeurig1 genereert 6. Willekeurig2 genereert 98. Gokker1,2,3 wedden op Willekeurig1. Gokker 3,4,5 wedden op Willeurig2. Gokker 3 wedt dus op beide willekeurig objecten Gokker objecten Gokker objecten kunnen zich registreren als luisteraar bij één of meerdere Willekeurig objecten. Een Gokker object kan met een methode setdoel tot 100 getallen ingeven waarvan hij hoopt dat ze zullen gegenereerd worden door een willekeurig object Waarop wedt wie? Gokker 1 : 1, 2 Gokker 2: Gokker 3: 13 Gokker 4: Gokker 5: Elke keer als we op do gok klikken, genereren de twee Willekeurig objecten twee getallen. De gokkers worden dynamisch verwittigd. Als ze winnen verhoogd het getal achter hun naam 269/395 8

270 Componenten: een dynamisch systeem Het ligt op voorhand niet vast welke gokker op welk willekeurig object zal gokken. Tijdens uitvoering kan men met de beanbox nieuwe gokker-objecten aanmaken, alsook nieuwe willekeurig objecten en ze met elkaar verbinden op zelf gekozen wijzen. Het is dus een soort dynamisch lego-blokken systeem. Dezelfde componenten nu met JBuilder 6 Weeral worden dynamisch objecten gesleept op scherm en verbonden met elkaar In JBuilder : zelf blauwe code toevoegen void jbutton1_actionperformed(actionevent e) { willekeurig1.werp(); willekeurig2.werp(); void willekeurig1_gokgebeurd(gokevent e) { gokker1.gokgebeurd(e); gokker2.gokgebeurd(e); gokker3.gokgebeurd(e); void willekeurig2_gokgebeurd(gokevent e) { gokker3.gokgebeurd(e); gokker4.gokgebeurd(e); gokker5.gokgebeurd(e); Kode verklaart zichzelf Klikken op button1: werp oproepen voor willekeurig1 en 2 GokGebeurd voor willekeurig1: gokker1,2 en 3 verwittigen door gokgebeurd op te roepen 270/395 9

271 Eerste stap : een event uitdenken De klasse Willekeurig moet andere objecten verwittigen: de data wordt meegegeven in een Event object import java.awt.*; import java.util.*; public class GokEvent extends EventObject {private int gok; public GokEvent(Object Source,int gok) { super(source); this.gok=gok; public int getgok(){return gok; Verplicht overerven In deze klasse stockeren we de waarde van het gekozen willekeurig getal. Dit getal wordt met dit object doorgegeven aan alle luisteraars. Tweede stap: een verwittig contract vastleggen Welke routine zal een verwittig object oproepen van iedereen die wil verwittigd worden? Dit leggen we vast in een interface contract. Dit moet verplicht de overerven van EventListener. In de interface zet de volledig vrij te kiezen namen van routines die opgeroepen worden. Verplicht is wel dat elk van deze routines als parameter een object moet hebben dat overerft van EventObject (GokEvent voldoet hieraan). package be.khleuven.rega.ti2.beans.events.visual.gokker; public interface GokListener extends java.util.eventlistener{ public void gokgebeurd(gokevent gok); Klasse Gokker Als deze klasse wilt verwittigd worden, moet het de afgesproken verwittig contract interface GokListener implementeren. Hiervoor moet het aldus de methode gokgebeurd definiëren. Deze methode zal immers opgeroepen worden door willekeurig objecten. Voorwaarde 3 Alle javabeans moeten de interface Serializable implementeren. Dit is gelukkig eenvoudig: het is een lege interface. Waarom? Dynamische systemen die met componenten werken, geven de mogelijkheid om een complex netwerk van objecten op te bouwen. Als elk van deze objecten de interface Serializable implementeerd kan dit netwerk eenvoudig gesaved en terug herladen worden. Dit is belangrijk voor tools. 271/395 10

272 Voorwaarde 4 Elke javabean klasse moet een default constructor hebben. Dit is een constructor zonder parameters. Dit is nodig om de tools toelaten om een object aan te maken gewoon door te klikken en te slepen naar een ontwerpscherm. In dat geval wordt de default constructor opgeroepen. Properties: eenvoudig : voorzie een set en get methode Gelukkig zijn properties in java eenvoudig te maken: je moet gewoon een set en een get methode voorzien. Vb: de properties naam heeft de bijhorende methodes void setnaam(string s) en String getnaam(). Dit is dan een String property. Let op de n die een N wordt. Klasse Gokker package be.khleuven.rega.ti2.beans.events.visual.gokker; import javax.swing.*; import java.awt.*; public class Gokker extends JPanel implements GokListener,java.io.Serializable { private String naam; private int aantalgewonnen; private int doel[];private int aantaldoelen; public void setdoel(int ndoel) {if(aantaldoelen<100)doel[aantaldoelen++] = ndoel; public int getdoel() {if(aantaldoelen>0)return doel[aantaldoelen-1]; return 0; Property doel public void gokgebeurd(gokevent ge) { boolean gewonnen=false; int gok=ge.getgok(); for (int i = 0; i < aantaldoelen&! gewonnen; i++) { if( doel[i]==gok) gewonnen=true; if(gewonnen) { aantalgewonnen++; repaint(); System.out.println("gokker " + naam + ": " + gok); public int getaantalgewonnen() { return aantalgewonnen; public void print(){ System.out.println(naam+" heeft "+aantalgewonnen +" keer gewonnen"); Verwittig contract Niet wijzigbare property 272/395 11

273 Willekeurig public void paint(graphics g) {super.paint(g); g.drawstring(naam+" "+aantalgewonnen,10,20); for(int i=0;i<aantaldoelen;i++) g.drawstring(doel[i]+" ",10,40+i*20); public void setnaam(string naam){this.naam=naam; public String getnaam(){return naam; public Gokker() {naam=""; aantalgewonnen=0; aantaldoelen=0; doel=new int[100]; Default constructor Property naam De klasse moet een lijst (vector) bijhouden waarin de referentie van alle objecten die moeten verwittigd worden, worden bijgehouden. De methode addgoklistener en removegoklistener moeten worden toegevoegd. (het event noemt dan gok ) De klasse Willekeurig De routines moeten echt met add en remove beginnen en eindigen op Listener. Anders herkennen de tools het event niet automatisch. De parameter moet overerven van EventListener (anders herkennen de tools weeral niet deze registratieroutines). package be.khleuven.rega.ti2.beans.events.visual.gokker; import java.util.random; import java.util.vector; import java.io.serializable; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Willekeurig extends JPanel implements Serializable { private int gok; private Vector list; private Random r=new Random(); private int range; public void werp() { gok=r.nextint(range);// willekeurig getal firegokevent(gok); repaint(); 273/395 12

274 public void addgoklistener(goklistener l) {if(!list.contains(l)) list.addelement(l); public void removegoklistener(goklistener l) {if(list.contains(l)) list.removeelement(l); public void firegokevent(int gok) { for(int i=0;i< list.size();i++) {GokListener gl= (GokListener) list.elementat(i); gl.gokgebeurd(new GokEvent(this,gok)); Property range public void setrange(int range){ this.range=range; public int getrange(){return range; public Willekeurig() { list = new Vector(); setsize(100,100); setvisible(true); Default constructor public void paint(graphics g) { super.paint(g); g.drawstring(""+gok,10,getheight()/2); Laatste stap: jar file Omdat een component kan gebruik maken van hulpklassen, moet u wel aangeven welke classe de hoofdklasse is. Dit gebeurt in een manifest file. Maak een bestand meta-inf/manifest.mf meta-inf/manifest.mf Name: be.khleuven.rega.ti2.beans.events.visual.gokker.willekeurig.class Java-Bean: True Name: be.khleuven.rega.ti2.beans. events.visual.gokker.gokker.class Java-Bean: True Steeds lege lijn achter zetten winzip Vervolgens zipt u alle classfiles + metainf/manifest.mf Rename naar.jar en klaar Tools hebben de mogelijkheid om jar files met javabeans erin te laden en te laten werken. 274/395 13

275 Componenten gemaakt uit componenten We maken eerst een draaiknop: als we erop klikken, draait hij. We maken er een javabean van. Draaiknop component Vervolgens maken we met deze component een andere component: een brandkast samengesteld uit een geheime code en drie draaiknoppen. Data voor event public class DraaiEvent extends EventObject {private int waarde; public DraaiEvent(Object Source,int waarde) { super(source); this.waarde=waarde; public int getwaarde(){return waarde; public interface DraaiListener extends java.util.eventlistener { public void gedraaid(draaievent draaievent); 275/395 14

276 package be.khleuven.vgo.draaiknop; import java.awt.*; import javax.swing.*; import javax.swing.border.*; import java.awt.event.*; import java.util.*; public class Draaiknop extends JPanel implements java.io.serializable { private Vector list; private int waarde=1; MediaTracker tracker; private Image img[]=new Image[12]; public void draai() { waarde=waarde+1; if (waarde>12)waarde=1; firedraaievent(waarde); repaint(); public void adddraailistener(draailistener l) { if (!list.contains(l)) list.addelement(l); public void removedraailistener(draailistener l) { if (list.contains(l)) list.removeelement(l); public void setwaarde(int waarde){this.waarde=waarde;repaint(); public int getwaarde(){return waarde; public void firedraaievent(int waarde) {for(int i=0;i<list.size();i++) {DraaiListener dl=(draailistener) list.elementat(i); dl.gedraaid(new DraaiEvent(this,waarde)); public Draaiknop() {list = new Vector(); Class tk=this.getclass(); img[0]=new ImageIcon(tk.getResource("knop1.jpg")).getImage(); img[1]=new ImageIcon(tk.getResource("knop2.jpg")).getImage(); img[2]=new ImageIcon(tk.getResource("knop3.jpg")).getImage(); img[3]=new ImageIcon(tk.getResource("knop4.jpg")).getImage(); img[4]=new ImageIcon(tk.getResource("knop5.jpg")).getImage(); img[5]=new ImageIcon(tk.getResource("knop6.jpg")).getImage(); img[6]=new ImageIcon(tk.getResource("knop7.jpg")).getImage(); img[7]=new ImageIcon(tk.getResource("knop8.jpg")).getImage(); img[8]=new ImageIcon(tk.getResource("knop9.jpg")).getImage(); img[9]=new ImageIcon(tk.getResource("knop10.jpg")).getImage(); img[10]=new ImageIcon(tk.getResource("knop11.jpg")).getImage(); img[11]=new ImageIcon(tk.getResource("knop12.jpg")).getImage(); De component Brandkast setsize(50,50); setpreferredsize(new Dimension(50,50)); setborder(new EtchedBorder()); addmouselistener(new MouseAdapter() {public void mousepressed(mouseevent me) {draai(); ); setvisible(true); public void paint(graphics g) {super.paint(g); g.drawimage(img[waarde-1],0,0,this); g.drawstring(""+waarde,getwidth()/2-5,getheight()/2+3); De brandkast component omvat drie draaiknoppen. De brandkast heeft een geheime kode. Als de gebruiker de code raadt, wordt de achtergrond groen gekleurd. De brandkast gaat open. De brandkast is een component. 276/395 15

277 package be.khleuven.vgo.draaiknop; import java.awt.event.*; import java.util.eventobject; public class BrandkastEvent extends EventObject {private int waarde1; private int waarde2; private int waarde3; public BrandkastEvent(Object Source,int waarde1,int waarde2, int waarde3) { super(source); this.waarde1=waarde1;this.waarde2=waarde2;this.waarde3=waarde3; public int getwaarde1(){return waarde1; public int getwaarde2(){return waarde2; public int getwaarde3(){return waarde3; package be.khleuven.vgo.draaiknop; public interface BrandkastListener extends java.util.eventlistener { public void gedraaid(brandkastevent draaievent); public void geopend(brandkastevent draaievent); package be.khleuven.vgo.draaiknop; import java.awt.*; import javax.swing.*; import javax.swing.border.*; import java.awt.event.*; import java.util.*; public class Brandkast extends JPanel implements java.io.serializable { private Vector list; private int waarde1=1,waarde2=1,waarde3=1; private int geheim1,geheim2,geheim3; private Draaiknop k1,k2,k3; public void addbrandkastlistener( BrandkastListener l) { if (!list.contains(l)) list.addelement(l); public void removebrandkastlistener(brandkastlistener l) { if (list.contains(l)) list.removeelement(l); public void setwaarde1(int waarde) {this.waarde1=waarde;k1.setwaarde(waarde);repaint(); 277/395 16

278 public void setwaarde2(int waarde) {this.waarde2=waarde;k2.setwaarde(waarde);repaint(); public void setwaarde3(int waarde) {this.waarde3=waarde;k3.setwaarde(waarde);repaint(); public int getwaarde1(){return waarde1; public int getwaarde2(){return waarde2; public int getwaarde3(){return waarde3; public void setgeheim1(int g){this.geheim1=g;repaint(); public void setgeheim2(int g){this.geheim2=g;repaint(); public void setgeheim3(int g){this.geheim3=g;repaint(); public int getgeheim1(){return geheim1; public int getgeheim2(){return geheim2; public int getgeheim3(){return geheim3; public void firebrandkastevent(int waarde1, int waarde2,int waarde3) {setbackground(color.red); if(geheim1==waarde1&&geheim2==waarde2&&geheim3==waarde3) setbackground(color.green); for(int i=0;i<list.size();i++) {BrandkastListener dl= (BrandkastListener) list.elementat(i); dl.gedraaid( new BrandkastEvent(this,waarde1,waarde2,waarde3)); if(geheim1==waarde1&&geheim2==waarde2&&geheim3==waarde3) {dl.geopend( new BrandkastEvent(this,waarde1,waarde2,waarde3)); setbackground(color.green); public Brandkast(){this(1,1,1); Iedereen verwittigen indien geopend of gedraaid public Brandkast(int geheim1,int geheim2,int geheim3) {list = new Vector(); this.geheim1=geheim1;this.geheim2=geheim2; this.geheim3=geheim3; setsize(200,70); setpreferredsize(new Dimension(200,70)); setborder(new EtchedBorder()); setbackground(color.red); k1=new Draaiknop();k2=new Draaiknop();k3=new Draaiknop(); k1.adddraailistener(new DraaiListener() {public void gedraaid(draaievent me) {waarde1=me.getwaarde(); firebrandkastevent(waarde1,waarde2,waarde3);); k2.adddraailistener(new DraaiListener() {public void gedraaid(draaievent me) {waarde2=me.getwaarde(); firebrandkastevent(waarde1,waarde2,waarde3);); k3.adddraailistener(new DraaiListener() {public void gedraaid(draaievent me) {waarde3=me.getwaarde(); firebrandkastevent(waarde1,waarde2,waarde3);); firebrandkastevent(geheim1,geheim2,geheim3); add(k1);add(k2);add(k3); setvisible(true); Manifest.mf Name: be.khleuven.vgo.draaiknop.draaiknopjar.class Java-Bean: True Name: be.khleuven.vgo.draaiknop.brandkastjar.class Java-Bean: True Achter elke javabean declaratie moet een lege lijn komen. We hebben de volgende directory structuur: meta-inf manifest.mf be khleuven vgo draaiknop Classes Jpg s Het geheel steken we in een zip file die we herbenoemen naar een jar. Klaar. 278/395 17

279 De beanbox dedecteert de nieuwe javabeans. We selecteren een Draaiknop en een Brandkast. Een laatste toepassing 1 brandkast 1 draaiknop We gaan met beanbox even een kleine toepassing maken waarbij we de componenten draaiknop en brandkast gebruiken. Telkens we de brandkast openen door zijn code te raden, wordt de extra draaiknop verwittigd en draait hij 1 positie verder 279/395 18

280 We stellen properties in We verbinden event geopend met draai methode We slepen naar de draaiknop: Er verschijnt een lijstje van routine die kunnen opgeroepen worden, we kiezen draai : Telkens de brandkast geopend wordt, zal de draaiknop rechts ervan verwittigd worden en zal de draai routine worden opgeroepen. Deze draaiknop geeft aldus weer hoeveel maal de brandkast geopend werd. 280/395 19

281 De kracht van xml Het gedrag van uw programma aanpassen met een teksteditor XML: het lijkt magisch. Er wordt veel over gepraat. Maar eigenlijk is het niet moeilijk. U kunt ermee gegevens op een hiërarchische wijze structureren. De grootste uitdaging van xml is niet zijn syntax, maar uw fantasie. U moet xml in toepassingen herkennen. Xml: hiërarchische gegevens : gelijk welke hiërarchische gegevens <verhaal> <titel>xml is leuk</titel> <inleiding> <paragraaf> met xml kan je een eigen taaltje definieren. </paragraaf> </inleiding> <midden> <paragraaf> je kan 'tags' genest definieren </paragraaf> <paragraaf> je kan opgeven of een geneste tag 1 maal of meermaal mag voorkomen </paragraaf> </midden> <slot> <paragraaf> je krijgt aldus een informatie boom waarbij de omsluitende tags aangeven wat ze omsluiten </paragraaf> <paragraaf> de gedefinieerde taal mag recursief zijn: je kan bijvoorbeeld in de tag 'midden' opgeven dat hierin terug een 'verhaal' tag mag komen. </paragraaf> </slot> </verhaal> 281/395 1

282 Ander hiërarchisch voorbeeld <zin> <onderwerp> <lidwoord> de </lidwoord> <zelfstandig_naamwoord> man </zelfstandig_naamwoord> </onderwerp> <werkwoord> loopt </werkwoord> <einde>. </einde> </zin> <zin> <onderwerp> <lidwoord> de </lidwoord> <adjectief> grote </adjectief> <zelfstandig_naamwoord> man </zelfstandig_naamwoord> </onderwerp> <werkwoord>loopt</werkwoord> <bijvoeglijk_naamwoord> snel </bijvoeglijk_naamwoord> <gezegde> naar huis </gezegde> <einde>. </einde> </zin> Delen van uw programma naar xml bestand overbrengen Configuratiebestanden zijn tegenwoordig meestal in xml Opties van uw programma highscores Een lijst initialiseren vanuit xml <?xml version="1.0" encoding="utf-8"?> <lijst> <lijst_element kleur= grijs > <naam>peeters</naam> <voornaam>piet</voornaam> <lijst_element> <lijst_element kleur= rood > <naam>mertens</naam> <voornaam>marc</voornaam> <lijst_element> <lijst_element kleur= groen > <naam>jespers</naam> <voornaam>jan</voornaam> <lijst_element> </lijst> Peeters piet Mertens marc Jespers jan Kleuren, meerdere talen: gebruik xml <?xml version="1.0" encoding="utf-8"?> <onze_toepassing> <kleuren> <menu>rood</menu> <button>geel</button> <label>groen</label> </kleuren> <teksten> <tekst woord= welkom frans= bonjour engels= hello nederlands= welkom /> <tekst woord= maandag frans= lundi engels= monday nederlands= maandag /> <tekst woord= tot ziens frans= au revoir engels= bye nederlands= tot ziens /> </teksten> </onze_toepassing> 282/395 2

283 Een dynamisch menu <?xml version="1.0" encoding="utf-8"?> <menu> <optie> <naam>draai</naam> <class>draai_klasse.class</class> <method>doe_draai< /method> </optie> <optie> <naam>vergroot</naam> <class>vergroot_klasse.class</class> <method>doe_vergroot< /method> </optie> </menu> Je weet op voorhand niet welke opties er op uw menu gaan staan: In dit geval gaat het programma bovenstaande xml file lezen, en dynamisch een menu gegeneren. Als actie gekoppeld aan het menu, wordt een methode opgeroepen die bij een opgegeven klasse hoort. Abstracter en abstracter Je kan zelfs een volledige opbouw van een scherm in een xml bestand steken. (Je kan erin steken wat je wilt. Uw eigen fantasie is de enige beperking.) Wel moet je eerst een klein xml-taaltje uitvinden. Scherm layout in xml (zie xaml verderop) <?xml version="1.0" encoding="utf-8"?> <toepassing> <frame naam= frame1 > <veld naam= veld1 type= JLabel /> <veld naam= veld2 type= JButton actie= klasse_2 method= doe_iets /> </frame> <frame naam= frame2 > <veld naam= veld3 type= JLabel /> <veld naam= veld4 type= JButton actie= klasse_2 method= doe_iets /> </frame> </toepassing> 283/395 3

284 Gegevens uitwisselen tussen toepassingen Als je gegevens naar een andere toepassing of naar een andere gebruiker wilt sturen, kunt u deze gegevens structuren met behulp van xml. <?xml version="1.0" encoding="utf-8"?> <aanvraag> <naam>piet</naam> <functie>verkoper</functie> <vraag artikel= FE34RET aantal= 2000 klantnummer= FDZT /> <vraag artikel= 7774RET aantal= 2000 klantnummer= FDZT /> <vraag artikel= FE3888T aantal= 2000 klantnummer= FDZT /> </aanvraag> Xml als onafhankelijke gegevensdrager Er zijn tools (vb WebToDate van DataBacker) die toelaten de inhoud van een grote website op te geven. Deze inhoud wordt dan in een xml bestand bijgehouden. Een ander deel van het programma gaat dan dit xml bestand lezen en volgens een keuze layout een echte website genereren. Vanuit java xml maken De klasse Element Er zijn drie belangrijke klassen in het jdom package: Element Attribute Document Enkele constructoren en functies om content toe te voegen 284/395 4

285 Alle sub-elementen onder een element opvragen Inhoud of sub-elementen weglaten vb1 // xml uitvoer naar bestand a.xml // gebruik van geneste Elementen // jdom : eenvoudig te gebruiken package om xml te bewerken ( import java.io.*; import org.jdom.*; import org.jdom.output.*; public class vb1 {public static void main(string args[]) {//maken van xml jdom object in geheugen Element a = new Element("A");// <A></A> Element b = new Element("B");// <B></B> //tekst komt tussen de omsluitende tags b.addcontent("b_inhoud");// <B>b_inhoud</B> 285/395 5

286 Element b2 = new Element("BB");//<BB></BB> b2.addcontent("bb_inhoud");//<bb>bb_inhoud</bb> Element b3 = new Element("BBB");//<BBB></BBB> //een element komt onder een ander element a.addcontent(b);// <A><B>b_inhoud</B></A> a.addcontent(b2);// <A> // <B>b_inhoud</B> // <BB>bb_inhoud</BB> // </A> a.addcontent(b3);// <A> // <B>b_inhoud</B> // <BB>bb_inhoud</BB> // <BBB></BBB> // </A> Element c = new Element("C");// <C></C> Element c2 = new Element("CC");// <CC></CC> c.addcontent("c_inhoud");// <C>c_inhoud</C> c2.addcontent("cc_inhoud");// <CC>cc_inhoud</CC> b3.addcontent(c); // <A> // <B>b_inhoud</B> // <BB>bb_inhoud</BB> // <BBB> // <C>c_inhoud</C> // </BBB> // </A> b3.addcontent(c2); // <A> // <B>b_inhoud</B> // <BB>bb_inhoud</BB> // <BBB> // <C>c_inhoud</C> // <CC>cc_inhoud</CC> // </BBB> // </A> Document doc=new Document(a); //overbrengen naar externe file XMLOutputter uit = new XMLOutputter(); uit.setindent(" "); uit.setnewlines(true); //output heeft als tweede parameter een OutputStream of een Writer FileOutputStream bestand=null; try{bestand= new FileOutputStream("a.xml"); catch(filenotfoundexception ee){ee.printstacktrace(); try{ uit.output(doc, bestand ); catch(ioexception e){e.printstacktrace(); Mag gelijk welke OutputStream of Writer zijn a.xml werd gecreëerd <?xml version="1.0" encoding="utf-8"?> <A> <B>b_inhoud</B> <BB>bb_inhoud</BB> <BBB> <C>c_inhoud</C> <CC>cc_inhoud</CC> </BBB> </A> 286/395 6

287 Nodige voorwaarden De klasse Attribute Surf naar download jdom.jar waarin de nodige klassen zitten. Om te compileren: set classpath=.;jdom.jar als jdom.jar in zelfde directory staat als te compileren bestand. import java.io.*; import org.jdom.*; import org.jdom.output.*; public class vb4 {public static void main(string args[]) {//maken van xml jdom object in geheugen Element a = new Element("A"); // <A></A> a.setattribute("a1","a1 waarde");// <A a1="a1 waarde"></a> a.setattribute("a2","a2 waarde");// <A a1="a1 waarde" // a2="a2 waarde"></a> Element b = new Element("B"); // <B></B> b.setattribute("b1","b1 waarde");// <B b1="b1 waarde"></b> b.setattribute("b2","b2 waarde");// <B b1="b1 waarde" // b2="b2 waarde"></b> b.addcontent("b_inhoud");// <B b1="b1 waarde" b2="b2 waarde">b_inhoud</b> a.addcontent(b); // <A a1="a1 waarde" a2="a2 waarde"> // <B b1="b1 waarde" // b2="b2 waarde">b_inhoud</b> // </A> Document doc=new Document(a); //overbrengen naar externe file XMLOutputter uit = new XMLOutputter(); uit.setindent(" "); uit.setnewlines(true); // output heeft als tweede parameter een OutputStream of een Writer FileOutputStream bestand=null; try{bestand= new FileOutputStream("a.xml"); catch(filenotfoundexception ee){ee.printstacktrace(); try{ uit.output(doc, bestand ); catch(ioexception e){e.printstacktrace(); /* a.xml: <?xml version="1.0" encoding="utf-8"?> <A a1="a1 waarde" a2="a2 waarde"> <B b1="b1 waarde" b2="b2 waarde">b_inhoud</b> </A>*/ 287/395 7

288 Intern worden Attribute objecten gebruikt a.setattribute("a1","a1 waarde"); De klasse Document Is gelijkwaardig aan: A.setAttribute(new Attribute( a1, a1 waarde ) ); Een xml bestand lezen Pizza.xml <?xml version="1.0" encoding="utf-8"?> <pizza korst="hard" pikant="ja"> napolitain <beleg>veel kaas</beleg> <beleg>veel vlees</beleg> </pizza> Dit is een mixed content xml bestand. De tekst napolitain staat niet tussen tags (De tag pizza heeft zowel gewone tekst napolitain als subelementen). import java.io.*; import org.jdom.*; import org.jdom.output.*; import org.jdom.input.*; import java.util.*; public class jdom_3 {public static void main(string args[]) {try {SAXBuilder builder = new SAXBuilder(false); // false:geen validatie om na te gaan of er fouten zijn // we halen het document in het geheugen Document doc = builder.build( "pizza.xml"); // nu kunnen we het ontleden Element pizza=doc.getrootelement(); 288/395 8

289 Attributen lezen System.out.println("korst= +pizza.getattribute("korst").getvalue()); // // korst=hard // System.out.println("pikant= +pizza.getattribute("pikant").getvalue()); // // pikant=ja // Is ook goed: pizza.getattributevalue( pikant ); Als je op voorhand niet weet hoeveel attributen er zijn: alles aflopen //alle attributen van pizza List attributen=pizza.getattributes(); ListIterator it=attributen.listiterator(); while (it.hasnext()) { Attribute attr=(attribute)it.next(); System.out.println("Attribute: +attr.getname()+" waarde:"+attr.getvalue()); // // Attribute:korst waarde:hard // Attribute:pikant waarde:ja // Een element opvragen // eerste beleg? System.out.println("eerste beleg: +pizza.getchild("beleg").gettext()); // // eerste beleg:veel kaas // Geneste oproepen pizza.getchild("beleg").getchild( subbeleg).getchild( subsubbeleg).gettext()); Je kan in één bevel ineens de tekst van drie niveaus diep opvragen. Is ook goed: pizza.getchildtext("beleg"); 289/395 9

290 Als je niet weet hoeveel elementen: alles aflopen, wel op type testen List alles=pizza.getcontent(); ListIterator it3=alles.listiterator(); while (it3.hasnext()) { System.out.println("...;;;"); Object el=it3.next(); napolitain is van type Text, niet van type Element if(el instanceof Text) System.out.println(((Text)el).getText() ); if(el instanceof Element) System.out.println(((Element)el).getName()+": +((Element)el).getText() ); Resultaat // //...;;; // // napolitain // //...;;; // beleg:veel kaas //...;;; // deel pizza: type:org.jdom.text // //...;;; // beleg:veel vlees //...;;; // // catch(jdomexception e){e.printstacktrace(); Andere klassen Best steek je in een Element andere Elementen Andere mogelijkheden: Text Comment <!-- commentaar --> CDATA <![CDATA[<html>...</html>]]> Mag niet als subtags beschouwd worden, maar als één geheel dtd <?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>tove</to> <from>jani</from> <heading>reminder</heading> <body>don't forget me this weekend</body> </note> 290/395 10

291 Dtd mag ook buiten de xml staan Deze xml file bevat ook een omschrijving van een taaltje. Met DOCTYPE geef je de beschrijving van de taal. In dit geval moet het root element 'note' zijn. Een 'note' bestaat steeds uit een 'to' én een 'from' en een 'heading én een 'body'. <?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>tove</to> <from>jani</from> <heading>reminder</heading> <body>don't forget me this weekend!</body> </note> note.dtd <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> We checken bij laden van het xml bestand of het voldoet aan de dtd import java.io.*; import org.jdom.*; import org.jdom.output.*; import org.jdom.input.*; import java.util.*; public class jdom_4 {public static void main(string args[]) {try {// VALIDATIE true SAXBuilder builder = new SAXBuilder(true); // validatie om na te gaan of er fouten zijn // we halen het document in het geheugen Document doc = builder.build( "note.xml"); 291/395 11

292 Wijzigen van een bestaande xml // nu kunnen we het ontleden Element note=doc.getrootelement(); System.out.println("to:"+note.getChild("to").getText()); System.out.println("from:"+note.getChild("from").getText()); System.out.println("heading:"+note.getChild("heading").getText()); System.out.println("body:"+note.getChild("body").getText()); catch(jdomexception e){e.printstacktrace(); Als xml niet voldoet aan dtd, wordt er een exception geworpen Eenvoudig: je leest het xml bestand. Dan wordt er in het geheugen een boom van Element objecten opgebouwd. Onder elk Element hangt er een List van andere Elementen. Elk Element bevat ook een List van Attribute. Xml document wijzigen Je kan gewoon aan de gegevens structuur in het geheugen, Elementen of Attributen toevoegen of wijzigen. Voorbeeld van wijzigen van een bestaand xml bestand Aan een List iets toevoegen volstaat. Daarna kan je met een XmlOutputter het gewijzigd geheel naar schijf wegschrijven 292/395 12

293 note.xml <?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>tove</to> <from>jani</from> <heading>reminder</heading> <body>don't forget me this weekend</body> </note> import java.io.*; import org.jdom.*; import org.jdom.output.*; import org.jdom.input.*; import java.util.*; public class jdom_5 {public static void main(string args[]) {try {//VALIDATIE :gebeurt voorlopig alleen bij lezen (jdom is beta) SAXBuilder builder = new SAXBuilder(true); Document doc = builder.build( "note.xml"); Element note=doc.getrootelement(); // andere body note.getchild("body").settext("nieuwe body"); // andere heading (op een langere manier: // eerst weglaten en dan terug toevoegen) note.removechild("heading"); Element newheading = new Element("heading").addContent("blabla"); note.addcontent(newheading); catch(jdomexception e){e.printstacktrace(); // // to:tove // from:jani // heading:blabla // body:nieuwe body // namespaces <widget type="gadget"> <head size="medium"/> <big><subwidget ref="gizmo"/></big> <info> <head> <title>description of gadget</title> </head> <body> <h1>gadget</h1> A gadget contains a big gizmo </body> </info> </widget> 293/395 13

294 <head> heeft hier twee betekenissen Onder <info> staat een deel dat als één geheel moet beschouwd worden. De <head> die onder <info> staat behoort tot een andere xml definitie. Dit kan men expliciet aangeven door met namespaces te werken. Dit komt neer op een voorvoegsel te gebruiken om het onderscheid aan te geven <widget xmlns=" xmlns:xhtml=" type="gadget"> <head size="medium"/> <big><subwidget ref="gizmo"/></big> <info> <xhtml:head> <xhtml:title>description of gadget</xhtml:title> </xhtml:head> <xhtml:body> <xhtml:h1>gadget</xhtml:h1> A gadget contains a big gizmo </xhtml:body> </info> </widget> XPATH en XSL De http adressen worden niet gechecked, ze dienen om de namespace uniek te maken en http adressen zijn meestal uniek. Er bestaat een jdom Namespace object. Documentie jdom: XPath dient om delen van een xml structuur aan te duiden. xpath nederlandstalige goede introductie op het net verplicht door te nemen: dtd tutorial handig overzichtschema xsl xpath : uit drukken xsl dient om een xml structuur declaratief te transformeren naar iets anders. xsl tutorial (zeer veel voorbeelden) 294/395 14

295 u ziet de voorbeelden stap voor stap in uitvoering: (xsl tracer: heel handig om xsl in actie te volgen) ander materiaal over xsl: overzicht van xslt tutorials eenvoudige inleiding xml 295/395 15

296 Visuele voorstelling van gelijktijdigheid Threads tijd Processen die gelijktijdig lopen (platform onafhankelijk) Drie processen die de getallen 1->9 tergelijkertijd Op scherm willen zetten en dan een lijn verder gaan import java.awt.*; import javax.swing.*; import java.awt.event.*; class MijnFrame extends JFrame {int rij=15; public MijnFrame() {super(); setbounds(5,5,400,500); setvisible(true); // wachten noodzakelijk anders wordt // scherm opgebouwd terwijl threads // lopen en verdwijnen enkele lijnen try {Thread.sleep(3000); // we wachten 3 seconden catch(interruptedexception e) {System.out.println("thread"+ "interrupted"); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,50,rij+=15); ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,150,rij+=15); ).start(); new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) getgraphics().drawstring(""+i,250,rij+=15); ).start(); addwindowlistener(new WindowAdapter() {public void windowclosing(windowevent e) {System.exit(0); ); public class Thr01 {public static void main(string arg[]) {new MijnFrame(); 296/395 1

297 new Thread Een proces wordt voorgesteld door een Thread object. Aan de constructor van het thread object wordt een object doorgegeven dat de interface Runnable implementeerd. De interface Runnable is zeer eenvoudig: het bevat slechts één routine run, zonder parameters Start() Als de methode start wordt opgeroepen, dan wordt een nieuw proces opgestart en wordt de code die in de methode run staat uitgevoerd in dat ander proces. Stoppen van een thread: als de methode run stopt Gegevens tussen threads delen In bovenstaand voorbeeld heeft de MijnFrame klasse één globale veranderlijke rij. Alle drie threads hun run-methode zitten in een innerklasse van MijnFrame en kunnen bijgevolg gebruik maken van de globale veranderlijke rij. Elke keer u bovenstaand programma uitvoert, zal de uitvoering anders zijn. Het hangt van het uitbatingssysteem af in welke concrete volgorde de processen worden uitgevoerd. Als u niet opgeeft, hebben threads prioriteit 5. U kunt deze prioriteit wijzigen. (Hiervoor moet u gaan kijken bij de klasse Thread). Het is een hele krachttoer van java om zulke systeemafhankelijke dingen als processen machineonafhankelijk te programmeren. Elk uitbatingssysteem heeft meestal een andere wijze waarop de tijd tussen verschillende processen wordt verdeeld. 297/395 2

298 Drie bevelen die na elkaar worden uitgevoerd? Soms wil je er zeker van zijn dat enkele instructies in een thread ononderbroken worden uitgevoerd. Zelden worden de vier instructies on-onderbroken uitgevoerd getgraphics().drawstring(""+i,50,rij+=15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); Dit zijn vier instructies (eenmaal rij+=15 en driemaal drawstring). In een eerste versie laten we alles gelijktijdig uitvoeren. Deze vier instructies kunnen dan op een willekeurige tijdstip onderbroken worden door een andere thread die dan op dezelfde rij gaat schrijven of die vlug even de rij verhoogt. Wat willen we? Monitors Het mechanisme dat in Java wordt gebruikt bestaat uit monitors. Elk object in java kan gemonitored worden zodat geen twee routines die aangegeven zijn met synchronized terzelfdertijd het object kunnen accessen. Van zodra een eerste thread een routine of een deel code, aangegeven met synchronized binnengaat, zal het object waarop gesynchronized wordt gemonitored worden: er wordt op toegezien dat geen enkele andere thread synchronized code voor dit zelfde object zal uitvoeren. 298/395 3

299 Van zodra een thread het blok synchronized(geentweetegelijk){ binnenkomt, wordt er een ingebruik -vlag geplaatst op het object geentweetegelijk. Bij het verlaten van dit synchronized blok, wordt de vlag van geentweetegelijk terug op vrij gezet. Als een andere thread dan ook tracht van blok binnen te komen dat ook synchronizeert op hetzelfde object geentweetegelijk, dan wordt eerst nagegaan of de vlag op vrij staat. Indien niet, dan blijft deze andere thread wachten totdat de andere thread gedaan heeft. Dan pas kan hij het blok binnengaan en zullen anderen thread moeten wachten. class MijnFrame07 extends JFrame {int rij=15; Thread one,two,three; Object geentweetegelijk=new Object();//een willekeurig object public MijnFrame07() {super(); setbounds(5,5,400,500); setvisible(true); one=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,50,rij+=15); getgraphics().drawstring(""+i,60,rij); getgraphics().drawstring(""+i,70,rij); ); one.start(); two=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,150,rij+=15); getgraphics().drawstring(""+i,160,rij); getgraphics().drawstring(""+i,170,rij); ); two.start(); three=new Thread( new Runnable() {public void run() {for(int i=1;i<10;i++) synchronized(geentweetegelijk) {getgraphics().drawstring(""+i,250,rij+=15); getgraphics().drawstring(""+i,260,rij); getgraphics().drawstring(""+i,270,rij); ); three.start(); 299/395 4

300 Langdurige operaties: tergelijkertijd uitvoeren Stel dat een programma twee listboxen moet opvullen uitgaande van twee files. Normaal gebeurt het opvullen één na één : eerst de eerste file, dan pas de tweede file. In de eerste versie van het programma gebeurt dit zo. U merkt dat de tweede listbox nog leeg is, omdat de eerste listbox nog niet vol is. (Het lezen uit een file wordt hier gesimuleerd met een klasse Dokument waarmee we met getline een lijn van het document opvragen. In de routine getline wordt ervoor gezorgd dat deze routine traag is en dat er steeds een andere lijn wordt gegenereerd. // zijn thread wel nuttig? Stel dat u een document moet lezen // en dat het genereren van elke lijn 1 seconde duurt. // Wel erg indien u 100 lijnen moet lezen, niet? // De classe Document simuleert dit: class Dokument {int i=0;string tekst; public Dokument(String tekstt){tekst=tekstt; public String getline() {i++; try {Thread.sleep(300);// de gebruiker moet wachten op deze lijn catch(interruptedexception e){system.out.println(e); return "...lijn "+i+"..."+tekst; 300/395 1

301 class ScrollDoorDocument extends JFrame {DefaultListModel deflistmod=new DefaultListModel(), deflistmod2=new DefaultListModel(); JList list,list2; Dokument dok,dok2; public ScrollDoorDocument() {Container c=getcontentpane(); c.setlayout(new GridLayout(1,2)); list=new JList(defListMod); list2=new JList(defListMod2); Documenten tergelijkertijd laden setvisible(true);setsize(500,300); c.add(new JScrollPane(list)); c.add(new JScrollPane(list2)); pack(); dok=new Dokument("eerste document"); for(int i=0;i<100;i++) deflistmod.addelement( dok.getline() ); dok2=new Dokument("tweede document"); for(int j=0;j<100;j++) deflistmod2.addelement( dok2.getline() ); // ALS HIER NOG CODE ZOU STAAN MOETEN WE LANG WACHTEN VOORDAT DIE // UITGEVOERD ZOU WORDEN. class ScrollDoorDocumentje extends JFrame {DefaultListModel deflistmod=new DefaultListModel(), deflistmod2=new DefaultListModel(); JList list,list2; Dokumentje dok,dok2; public ScrollDoorDocumentje() {Container c=getcontentpane(); c.setlayout(new GridLayout(1,2)); list=new JList(defListMod); list2=new JList(defListMod2); setvisible(true);setsize(500,300); c.add(new JScrollPane(list)); c.add(new JScrollPane(list2)); pack(); new Thread(new Runnable() {public void run() {dok=new Dokumentje("eerste document"); for(int i=0;i<100;i++) deflistmod.addelement( dok.getline() ); ).start(); new Thread(new Runnable() {public void run() {dok2=new Dokumentje("tweede document"); for(int j=0;j<100;j++) deflistmod2.addelement( dok2.getline() ); ).start(); Door het laden van de twee listboxen in twee aparte threads te laten gebeuren, worden de twee listboxen onafhankelijk van elkaar opgevuld. Ook moet het programma niet wachten totdat de twee listboxen opgevuld zijn om verder te gaan. (Achter de code die de twee listboxen opvuld, kan nog andere kode staan.) 301/395 2

302 Servers (sql, html, ftp,ldap...) Bij servers wordt ook dikwijls met threads gewerkt. Stel dat meerdere gebruikers terzelfdertijd een trage bewerking van de server vragen, dan zou de tweede klant van de server moeten wachten totdat de eerste klant gedaan heeft. Bij server wordt daarom steeds een aparte thread opgestart die de transacties van één klant zal behandelen. 302/395 3

303 Klassen Socket en ServerSocket ServerSocket Basis voor servers uitvoer invoer Sql, smtp Html (http) ftp, ldap,... uitvoer invoer Socket Wat voor de ene invoer is, is voor de andere uitvoer Socket link=new ServerSocket(1024).accept(); Programma blijft wachten Communicatie gebeurt tussen twee Socket objecten ServerSocket Socket uitvoer invoer Tot een ander programma met onze computer tracht te verbinden op poort 1024 invoer uitvoer Socket Dan gaat het programma verder en krijgt link een waarde: een Socket object Wat voor de ene invoer is, is voor de andere uitvoer 303/395 1

304 Vanuit elke Socket: twee streams Socket link= new ServerSocket(1024).accept(); BufferedReader in = new BufferedReader(new InputStreamReader( link.getinputstream() )); PrintWriter out= new PrintWriter( link.getoutputstream(),true); Socket1 invoer uitvoer invoer Pas op uitvoer Socket2 Als Socket2 leest vanaf zijn invoer, dan blijft hij wachten totdat Socket1 op zijn uitvoer schrijft. In- en uitvoer van de twee sockets moeten synchroon gebeuren: goede afspraken zijn nodig:vb elk 1 lees, gevolgd door 1 schrijf en Socket1 mag beginnen met lees, dus moet Socket2 beginnen met schrijf Vb: echo server EchoClient EchoServer EchoClient EchoClient EchoClient Elke client kan tekst typen. Alle clients kunnen dit terzelfdertijd doen. De server gaat gewoon voor elke client een aparte thread opstarten : zolang de client niet QUIT intypt, zal deze thread constant in verbinding staan met de client en zal gewoon alle ingetypte tekst terug naar de client zenden 304/395 2

305 Echte server Bij een echte server, zal de client niet gewone tekst sturen, maar bij een sql-server zal de client een sql-vraag doorzenden en zal de server het antwoord terugzenden. Bij een http-server, zal de client (browser) een http-aanvraag doorzenden en zal de client (webserver) de inhoud van de gevraagde html pagina terugzenden stramien van EchoServer (zonder exceptions,...) public static void main(string[] args) throws IOException { ServerSocket servsocket = new ServerSocket(1234); do {Socket client = servsocket.accept(); System.out.println("\nNew client accepted.\n"); ClientHandler handler = new ClientHandler(client); new Thread(handler).start(); while (true);// oneindige lus om constant client te ontvangen class ClientHandler implements Runnable { private Socket client; private BufferedReader in; private PrintWriter out; public ClientHandler(Socket socket) { client = socket; try {in = new BufferedReader(new InputStreamReader( client.getinputstream())); out = new PrintWriter(client.getOutputStream(),true); catch(ioexception e){e.printstacktrace(); public void run() // voor thread!!! { try { String received; do { received = in.readline(); out.println("echo: " + received); while (!received.equals("quit")); client.close(); System.out.println("Closing down connection..."); catch(ioexception e){e.printstacktrace(); Het volledige multithreaded EchoServer programma import java.io.*; import java.net.*; public class MultiEchoServer { private static ServerSocket servsocket; public static void main(string[] args) throws IOException { try{ servsocket = new ServerSocket(1234); catch (IOException e) { System.out.println("\nUnable to set up port!"); System.exit(1); 305/395 3

306 do { //Wait for client... Socket client = servsocket.accept(); class ClientHandler implements Runnable { private Socket client; private BufferedReader in; private PrintWriter out; System.out.println("\nNew client accepted.\n"); //Create a thread to handle communication with //this client and pass the constructor for this //thread a reference to the relevant socket... ClientHandler handler = new ClientHandler(client); new Thread(handler).start(); //As usual, this method calls run. while (true); public ClientHandler(Socket socket) { //Set up reference to associated socket... client = socket; try {in = new BufferedReader(new InputStreamReader( client.getinputstream())); out = new PrintWriter(client.getOutputStream(),true); catch(ioexception e){e.printstacktrace(); public void run() // voor thread!!! { try { String received; do { //Accept message from client on //the socket's input stream... received = in.readline(); //Echo message back to client on //the socket's output stream... out.println("echo: " + received); //Repeat above until 'QUIT' sent by client... while (!received.equals("quit")); catch(ioexception e){e.printstacktrace(); finally {try {if (client!=null) {System.out.println("Closing down connection..."); client.close(); catch(ioexception e){e.printstacktrace(); EchoClient Enter message ('QUIT' to exit): aaaa ECHO: aaaa Enter message ('QUIT' to exit): bbbb ECHO: bbbb Enter message ('QUIT' to exit): cccc ECHO: cccc Enter message ('QUIT' to exit): dddd ECHO: dddd Enter message ('QUIT' to exit): eeee ECHO: eeee Enter message ('QUIT' to exit): ffff ECHO: ffff Enter message ('QUIT' to exit): gggg ECHO: gggg Enter message ('QUIT' to exit): hhhh ECHO: hhhh Enter message ('QUIT' to exit): iiii ECHO: iiii Enter message ('QUIT' to exit): 306/395 4

307 java MultiEchoServer New client accepted. New client accepted. New client accepted. De server gaat de communicaties met de verschillende clients niet door elkaar halen: voor elke client loopt immers een apart proces. Vb: echo server: de client leest van toetsenbord EchoClient toetsenbord EchoServer EchoClient toetsenbord EchoClient toetsenbord EchoClient toetsenbord Elke client heeft 3 communicatie kanalen import java.io.*; import java.net.*; EchoClient public class MultiEchoClient { private static InetAddress host; private static Socket link; private static BufferedReader in; private static PrintWriter out; private static BufferedReader keyboard; public static void main(string[] args) IOException { throws try {host = InetAddress.getLocalHost(); link = new Socket(host, 1234); in = new BufferedReader(new InputStreamReader( link.getinputstream())); out = new PrintWriter(link.getOutputStream(),true); keyboard = new BufferedReader(new InputStreamReader(System.in)); String message, response; do{system.out.print("enter message ('QUIT' to exit): "); message = keyboard.readline(); //Send message to server on out.println(message); //Accept response from server response = in.readline(); //Display server's response to user... System.out.println(response); while (!message.equals("quit")); catch(ioexception ioex){ioex.printstacktrace(); finally {try{if (link!=null) {System.out.println("Closing down connection..."); link.close(); catch(ioexception ioex){ioex.printstacktrace(); 307/395 5

308 We drukken op knop en beurtelinks verandert de tekst op de knop Event Thread mag niet geblokkeerd worden Eenvoudig te programmeren? public class Vb1 extends JFrame {Container c; JButton but; public Vb1() {c= getcontentpane(); c.setlayout(new FlowLayout()); but=new JButton("klik op mij"); c.add(but); but.addactionlistener(new ActionListener() { public void actionperformed(actionevent ae) {but.settext("1");try{thread.sleep(1000);catch(exception e){ but.settext("2");try{thread.sleep(1000);catch(exception e){ but.settext("3");try{thread.sleep(1000);catch(exception e){ but.settext("4");try{thread.sleep(1000);catch(exception e){ but.settext("5");try{thread.sleep(1000);catch(exception e){ but.settext("klik op mij"); ); setdefaultcloseoperation(jframe.exit_on_close); setsize(200,100); setvisible(true); public static void main(string args[]) {new Vb1(); Probleem bij uitvoer Er verschijnen helemaal geen 1,2,3,4,5. De knop klik op mij blijft gedurende vijf seconden ingedrukt. Daarna staat er terug klik op mij 308/395 1

309 Venster wordt niet meer goed hertekend Als we het venster verkleinen en vergroten gedurende die vijf seconden, wordt er helemaal niet meer hertekend EventThread Een apart proces de eventthread staat in voor het hertekenen van een frame en ook voor het behandelen van events. Events: klikken op een knop but.settext("1");try{thread.sleep(1000);catch(exception e){ but.settext("2");try{thread.sleep(1000);catch(exception e){ but.settext("3");try{thread.sleep(1000);catch(exception e){ but.settext("4");try{thread.sleep(1000);catch(exception e){ but.settext("5");try{thread.sleep(1000);catch(exception e){ but.settext("klik op mij"); Deze code wordt uitgedrukt wanneer je op de knop klikt, ze wordt aldus uitgevoerd door de eventthread. Je vraagt vervolgens met but.settext( 1 ) om de knop te hertekenen. Hiervoor wordt er een herteken-aanvraag op de wachtrij van taken van de eventthread gezet. thread. Ze worden zo snel na elkaar uitgevoerd dat je alleen maar klik op mij zult zien. De eventthread kan die taak nog niet uitvoeren, omdat hij nog bezig is met bovenstaande 6 bevelen. Die moeten eerst gedaan zijn, voordat aan een andere taak kan begonnen worden. Eens de zes bevelen van hierboven uitgevoerd, staan er zes hertekenen aanvragen voor de knop op de wachtrij van de event 309/395 2

310 Oplossing: zelf een thread gebruiken voor de zes bevelen public class Vb2 extends JFrame {Container c; JButton but; public Vb2() {c= getcontentpane(); c.setlayout(new FlowLayout()); but=new JButton("klik op mij"); c.add(but); but.addactionlistener(new ActionListener() { public void actionperformed(actionevent ae) {new Thread(new Runnable() {public void run() but.settext("1");try{thread.sleep(1000);catch(exception e){ but.settext("2");try{thread.sleep(1000);catch(exception e){ but.settext("3");try{thread.sleep(1000);catch(exception e){ but.settext("4");try{thread.sleep(1000);catch(exception e){ but.settext("5");try{thread.sleep(1000);catch(exception e){ but.settext("klik op mij"); ).start(); Hier loopt alles al beter. De 'lang durende operatie(5 seconden)' wordt in een andere thread uitgevoerd. Hierdoor blijft de event-thread actief. Nu verschijnt er wel 1,2,3,4,5 achter elkaar. Ik vermoed dat but.settext een 'paint'-event op de event-queue plaats, zodat de event-thread dit juist zal afhandelen. ps: Je kan in dit voorbeeld wel heel snel achter elkaar op de knop drukken: dan worden heel veel threads opgestart die 1,2,3,4,5 achter elkaar uitvoeren. Hierdoor komen 1,2,3,4,5 door elkaar op de knop te voorschijn. Je zou dit kunnen vermijden door but.setenabled(false) uit te voeren de eerste keer dat er op de knop wordt geklikt en op het einde but.setenabled(true); uit te voeren. 310/395 3

311 Spelversie 1: doolhof Vele spelletjes die op elkaar lijken Hoe omgaan met de verschillen? Spelversie 2: vier op een rij Spelversie 3: BreakThrough 311/395 1

312 Spelversie 4: BeerGame Spelversie 5: Molleklop Spelversie 6: Trivial Persuit Gemeenschappelijke delen worden in basisklassen gestoken Verschillen worden in afgeleide klassen gestoken, dit zowel voor de verschillen in Games als voor de verschillen in Spelers 312/395 2

313 Flexibele gemeenschappelijke code De gemeenschappelijke delen gedragen zich flexibel : naar gelang ze met een ander Game aan het werken zijn, zullen ze automatisch andere code uitvoeren. Scheiding Algemeen - Bijzonder De complexiteit van het algemeen beheer van een spel en de bijzonderheid van elk spel zijn perfekt gescheiden. Dit verhoogd de onderhoudbaarheid Een abstract Game De klasse Hoofd werkt met een object spel van het type Game. Dit is een abstracte klasse. (Men kan er geen objecten van maken, wel objecten van afgeleide klassen). In de klasse Hoofd worden alle spelletjes als een Game - object geschouwd. De verschillen tussen de verschillende spelletjes worden geïsoleeerd behandeld in de klassen die overerven van Game 313/395 3

314 Een abstracte Speler In de klasse Game wordt gewerkt met twee objecten p1 en p2 van de klasse Speler. Ook dit is een algemene abstracte Speler klasse (er kunnen ook geen objecten van aangemaakt worden). Bij Doolhof kunnen we de computer laten spelen De verschillen tussen de spelers (computer of human) komen helemaal niet aan bod in de klasse Game. De verschillen komen aan bod in klassen die overerven van Speler Zes verschillende spelen Vijf klassen erven over van Game: VierOpEenRij, Doolhof, Trivia, BreakThrough BeerGame Molleklop erft over van BeerGame Eenvoudige om andere analoge spelletjes toe te voegen 314/395 4

315 Drie typen Player Twee klassen erven over van PC: PC Human PCDoolhof erft over van PC 315/395 5

316 We kiezen een andere look-and-feel Java klassen : volledig flexibel ontwikkeld Een voorbeeld : een scherm met enkele knoppen en een lijst. Het voorbeeld doet niet. De java-klassen zijn zelf al flexibel genoeg We kiezen een andere layout De layout van alle java-componenten kan tijdens de uitvoering van elk willekeurig java programma veranderd worden, door een andere component-layout in te pluggen. Standaard zijn er al enkele look-andfeels voorzien Hiervoor wordt UIManager.setLookAndFeel methode opgeroepen 316/395 1

317 Eigen look-and-feels? U kunt ook eigen look-and-feels maken. Door enkele lijnen code toe te voegen, kunt u hiermee al uw bestaande javatoepassingen (Swing) van componenten layout veranderen. Hoe iets getekend wordt ligt niet vast? Tijdens het tekenen van elke component worden bepaalde routines opgeroepen hiervoor. De naam van deze routines ligt vast. Welke exacte versie van de routine niet. De exacte versie van een routine wordt tijdens de uitvoering van de instructie beslist. Kiezen hoe de componenten op het venster geschikt worden? We kiezen een FlowLayout: vergroten van scherm, zal componenten herschikken 317/395 2

318 Men kan dit combineren met een andere look-and-feel Men kan een diagonaal layout kiezen Of een random schikking van de componenten random schikking Bij random schikking zullen telkens men het vensters van grote verandert, de componenten op een andere random positie komen. 318/395 3

319 Eigen schikkingen ontwerpen Standaard is er een BorderLayout. Er worden vele layouts meegeleverd. Onder andere FlowLayout. DiagonaalLayout en RandomLayout: zelf ontwikkeld DiagonaalLayout en RandomLayout hebben we zelf als test ontwikkeld. Ze implementeren de interface LayoutManager. We kunnen de boord rond elke component afzonderlijk veranderen Elke component heeft de methode setborder. Tijdens uitvoering van een programma kan men hiermee de boord van elke component veranderen. Kies een boord 319/395 4

320 We kunnen de boord combineren met schikking en look-and-feel 320/395 5

321 EigenBorder klasse werd zelf ontwikkeld ListModel: vanwaar krijgt de lijst zijn gegevens? Men kan de data van elke bestaande lijst vervangen door andere gegevens. In dit demoprogramma zijn er twee verschillende objecten met gegevens die in de lijst getoond worden. Met het menu kan men eenvoudig tussen deze twee gegeven-sets wisselen. Kies uw gegevens-set Switch tussen deze gegeven-sets 321/395 6

322 Van waar moet de data komen die in de lijst moet getoond worden? Kiezen hoe elke geselecteerde lijn in de lijst moet getekend worden Men kan zelf eigen klassen overerven van DefaultListModel: -een Model dat zijn gegevens uit een File gaat halen -een ander model dat zijn gegevens uit een sql databank gaat halen. Inverse kleuren (+cijfer) of dansende letters? Steeds wisselende kleuren bij selectie? 322/395 7

323 En natuurlijk te combineren met andere keuzen tekenen aan te passen: overerven van ListCellRenderer, inpluggen in lijst 323/395 8

324 Wat kunnen we zoal veranderen? Dé tetris Men heeft getracht alles wat maar mogelijk is flexibel te maken -richting van het spel : Van boven naar beneden Van onder naar boven Van links naar rechts Van rechts naar links Vorm van de blokken De standaard blokken Grillerige blokken Letters van een woord als vorm De kleuren van de blokken Alle onderdelen van een blok in dezelfde standaard kleuren Alle onderdelen van een blok in een andere kleuren Meer kleuren gebruiken 324/395 1

325 Onderdeel van een blok Een rechthoekje gebruiken als onderdeel van een blok Een bol gebruiken als onderdeel van een blok Een schaduw van het dalende blok laten zien op de blok waar het zou komen indien het naar beneden valt Hulpmiddelen Wat als er een lijn verdwijnt? Alle blokken die erboven lagen, schuiven één lijn naar beneden (gewone zwaartekracht) Alle blokken die erboven lagen, schuiven zo ver mogelijk naar beneden door (sterke zwaartekracht) Waarneer verdwijnen er blokken? Als een lijn vol is Als een vierkant met dezelfde kleur gevormd is Als een hele kolom dezelfde kleur heeft Een combinatie van bovenstaande regels 325/395 2

326 Kies de onderdelen van de blokken Normaal spel Blokken met bollen Kies de spelrichting 326/395 3

327 Van onder naar boven Van links naar rechts, andere dimenties Bollen, met een vierkanten meebewegende schaduw onderaan Kies vorm van blokken 327/395 4

328 Andere vorm van blokken Kies kleuren Verschillende kleuren per blok? 328/395 5

329 Rare kleuren Kies de toetsen waarmee u blokken kunt bewegen Kies wanneer blokken verdwijnen en hoe diep ze dan vallen 329/395 6

330 Schaken of Dammen Wat is gemeenschappelijk? Wat is verschillend? Kies uw spel We kozen Dammen 330/395 1

331 We kozen schaken We werken met een object van de klasse Schaak of Dammen Dammen Beide hebben een array van AbstractPion 331/395 2

332 AbstractPion AbstractPion heeft een abstracte functie isvalidmove. Voor alle specifieke stukken wordt een andere isvalidmove voorzien De verschillen tussen alle pionnen zitten geconcentreerd in klassen die overerven van AbstractPion De klassen Schaak en Dammen werken alleen met AbstractPion-objecten 332/395 3

333 Er zijn twee soorten oefeningen Twee soorten oefeningen Oefeningen met breuken Voor de lagere school Eerste soort oefening: breuk aanduiden door apples te klikken We klikken op 2 van de 3 appels 333/395 1

334 Soort oefening2 : breuk invullen We moeten 2/8 invullen Oef1 erft over van Oef2. Oef1 en Oef2 implementeren de interface OefInterface In StartOef wordt gewoon met een veranderlijke van het type OefInterface gewerkt 334/395 2

335 Merk op dat zowel de klasse Oef1 als de klasse Oef2 een referentie heeft ( s ) naar StartOef. Deze referentie dient om aan StartOef het resultaat van de oefening terug te geven zodat een globale score kan bijgehouden worden. 335/395 3

336 Een Schietspel Alles kan bewegen, sommige kunnen schieten, sommigen bewegen speciaal 336/395 1

337 337/395 2

338 Alles IS EEN Speler De basisklasse van alle bewegende, schietende objecten is Speler. Een Speler is een abstracte klasse. Je kan er zelfs geen objecten van maken. Wel werkt heel het spel met een array van Speler -objecten De programmatie van het globaal spel is hierdoor zeer sterk vereenvoudigd: alle specifieke code voor bewegende delen zit in klassen die overerven van Speler. Een Schip IS EEN Speler, maar kan meer: kan ook sterven, schieten, upgraden Teveel klassen Teveel klassen om gedetailleerd alle methoden te laten zien. Stel eens voor dat deze differentiatie in bewegend, schietend gedrag met behulp van geneste if-then-else instructies in het spel zelf zouden opgenomen geweest zijn : niet te onderhouden 338/395 3

339 Verder details schema Nu kan er eenvoudig gedrag bijgevoegd worden zonder het spel zelf te wijzigen: men moet gewoon overerven van Speler en zorgen dat het object van die nieuwe klasse ergens wordt aangemaakt. De verder programmatie van het spel zelf veranderd helemaal niet. 339/395 4

340 Je speelt tegen de computer Scrabble Verschillende speelstrategieën De computer heeft vier verschillende speelstrategieën (probeer maar eens te winnen tegen Buddha). Het spel werkt met AiEngine Je kan eenvoudig andere speelstrategieën maken zonder de rest te hoeven aan te passen 340/395 1

341 Hoe spelen? Klik op een letter uit Available Tiles en deze letter komt in Puzzle Rack. Rechts klikken op een letter in Puzzle Rack zet de letter terug in Available Tiles Place uw woord, horizontaal of vertikaal, op het bord. Annuleer deze actie eventueel door rechts te klikken Kies add pseudoletter om een letter van een woord dat al op het bord staat, te gebruiken. 341/395 2

342 Je kiest eerst welke versie van het spel je wilt Op mollen kloppen Een mol loop over het scherm: -van links naar rechts en van boven naar onder -op random plaatsen Versie1: Tracht op de muis te klikken als mol onder visier loopt Versie 2: mol springt naar willekeurige vakken: probeer erop te klikken 342/395 1

343 De klasse Mollekes heeft een veranderlijke mt van het type MollekesTemplate (een abstracte klasse). Er zijn twee klassen die hiervan overerven: Catchamole Wackamole Deze twee klassen bevatten de verschillen tussen de twee versies Aan de veranderlijke mt wordt ofwel een object van de klasse Catchamole ofwel een object van de klasse Wackamole toegekend. Het programma (klasse Mollekes) werkt alleen maar met een veranderlijke van het type MollekesTemplate Je kan eenvoudig een derde versie van het spel maken door een derde klasse te laten overerven van MollekesTemplate 343/395 2

344 Als je raakt klikt, verschijnt er een kogelgat Op bewegende rozen schieten Eerste versie: rozen bewegen van rechts naar links Tweede versie: rozen bewegen zigzaggend RoosLevel1 en RoosLevel2 erven over van het algemenere Roos 344/395 1

345 Recursieve definitie Composite patroon Figuur: is een verzameling van : Lijnen Rechthoeken Teksten Andere Figuren Een voorbeeld van een concrete figuur Figuur1: Lijn1 Lijn2 Figuur2: Rechthoek1 Figuur3: Lijn3 Lijn4 Rechthoek2 Lijn5 Voordeel van composite Als je definieert hoe je een figuur kunt verslepen, van kleur veranderen, dan geldt deze functionaliteit automatisch voor lijnen, rechthoeken,...dit spaart veel programmeerwerk. Wordt bij de meeste tekenprogramma s gebruikt. 345/395 1

346 Een andere recursieve definitie Een Activiteit is een verzameling van: Activiteitje Een andere Activiteit Een voorbeeld hiervan ACTIVITEIT:italie ACTIVITEIT:ravenna activiteitje:klooster activiteitje:geleide stadswandeling ACTIVITEIT:venetie activiteitje:dogepaleis activiteitje:brug der Zuchten activiteitje:gondeltocht ACTIVITEIT:kerkbezoek_venetie activiteitje:kerk1 activiteitje:kerk2 activiteitje:kerk3 activiteitje:autobus Een Activiteit heeft een array ( activiteiten) van objecten van type Activiteit Flexibele array De array activiteiten kan objecten van het type Activiteit bevatten. Omdat Activiteitje overerft van Activiteit, kan men in de array activiteiten zowel objecten van de klasse Activiteit als van de klasse Activiteitje steken 346/395 2

347 Een Activiteit heeft een naam en een kostprijs Activiteit kerkbezoek_venetie=new Activiteit(); kerkbezoek_venetie.setnaam("kerbezoek_venetie"); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk1", 3)); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk2", 2)); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk3", 5)); Activiteit venetie_trip=new Activiteit(); venetie_trip.setnaam("venetie"); venetie_trip.voegactiviteittoe(new Activiteitje("Dogepaleis", 10)); venetie_trip.voegactiviteittoe(new Activiteitje("Brug der Zuchten", 2)); venetie_trip.voegactiviteittoe(new Activiteitje("Gondeltocht", 20)); venetie_trip.voegactiviteittoe(kerkbezoek_venetie); voegactiviteittoe( Activiteit of Activiteitje) Activiteit ravenna_trip=new Activiteit(); ravenna_trip.setnaam("ravenna"); ravenna_trip.voegactiviteittoe(new Activiteitje("klooster", 10)); ravenna_trip.voegactiviteittoe(new Activiteitje("geleide stadswandeling", 6)); Activiteit italie_trip=new Activiteit(); italie_trip.setnaam("italie"); italie_trip.voegactiviteittoe(ravenna_trip); italie_trip.voegactiviteittoe(venetie_trip); italie_trip.voegactiviteittoe(new Activiteitje("autobus",200)); italie_trip.druk(1); voegactiviteittoe is flexibel Je kan aan de routine voegactiviteittoe objecten van het type Activiteit toevoegen. Omdat de klasse Activiteitje overerft van de klasse Activiteit, mag men ook objecten van de klasse Activiteitje als argument aan voegactiviteittoe doorgeven Elk Activiteitje-object IS EEN Activiteit. class Activiteit {private String naam; private Activiteit activiteiten[]=new Activiteit[100]; private int aantal=0; public void setnaam(string n){naam=n; public void voegactiviteittoe(activiteit activiteit) {activiteiten[aantal]=activiteit; aantal++; public int getprijs() {int totaal=0; for(int i=0;i<aantal;i++) totaal+=activiteiten[i].getprijs(); andere versie return totaal; public void druk(int aantalspaties) {drukspaties(aantalspaties); System.out.println("ACTIVITEIT:"+naam); for(int i=0;i<aantal;i++) activiteiten[i].druk(aantalspaties+3); andere versie drukspaties(aantalspaties); System.out.println( "totale prijs voor "+naam+" :"+getprijs() ); protected void drukspaties(int aantal) {for(int i=0;i<aantal;i++)system.out.print(" "); 347/395 3

348 Flexibele oproepen activiteiten[i].getprijs(); Tijdens de uitvoering wordt beslist welke versie van getprijs wordt opgeroepen (getprijs van Activiteit of getprijs van Activiteitje) activiteiten[i].druk(aantalspaties+3); Dit is ook een flexibele oproep. Het ligt niet op voorhand vast welke versie van druk zal opgeroepen worden. class Activiteitje extends Activiteit {private String naam; private int prijs; public Activiteitje(String n,int p) {naam=n;prijs=p; public int getprijs(){return prijs; public void druk(int aantalspaties) {drukspaties(aantalspaties);system.out.println( "activiteitje:"+naam+" "+prijs); Heel programma class Activiteit {private String naam; private Activiteit activiteiten[]=new Activiteit[100]; private int aantal=0; public void setnaam(string n){naam=n; public void voegactiviteittoe(activiteit activiteit) {activiteiten[aantal]=activiteit; aantal++; public int getprijs() {int totaal=0; for(int i=0;i<aantal;i++) totaal+=activiteiten[i].getprijs(); return totaal; public void druk(int aantalspaties) {drukspaties(aantalspaties); System.out.println("ACTIVITEIT:"+naam); for(int i=0;i<aantal;i++) activiteiten[i].druk(aantalspaties+3); drukspaties(aantalspaties); System.out.println( "totale prijs voor "+naam+" :"+getprijs() ); protected void drukspaties(int aantal) {for(int i=0;i<aantal;i++)system.out.print(" "); class Activiteitje extends Activiteit {private String naam; private int prijs; public Activiteitje(String n,int p) {naam=n;prijs=p; public int getprijs(){return prijs; public void druk(int aantalspaties) {drukspaties(aantalspaties);system.out.println("activiteitj e:"+naam+" "+prijs); 348/395 4

349 public class Composite { public static void main(string args[]) {Activiteit kerkbezoek_venetie=new Activiteit(); kerkbezoek_venetie.setnaam("kerbezoek_venetie"); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk1", 3)); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk2", 2)); kerkbezoek_venetie.voegactiviteittoe(new Activiteitje("kerk3", 5)); Activiteit venetie_trip=new Activiteit(); venetie_trip.setnaam("venetie"); venetie_trip.voegactiviteittoe(new Activiteitje("Dogepaleis", 10)); venetie_trip.voegactiviteittoe(new Activiteitje("Brug der Zuchten", 2)); venetie_trip.voegactiviteittoe(new Activiteitje("Gondeltocht", 20)); venetie_trip.voegactiviteittoe(kerkbezoek_venetie); Activiteit ravenna_trip=new Activiteit(); ravenna_trip.setnaam("ravenna"); ravenna_trip.voegactiviteittoe(new Activiteitje("klooster", 10)); ravenna_trip.voegactiviteittoe(new Activiteitje("geleide stadswandeling", 6)); Activiteit italie_trip=new Activiteit(); italie_trip.setnaam("italie"); italie_trip.voegactiviteittoe(ravenna_trip); italie_trip.voegactiviteittoe(venetie_trip); italie_trip.voegactiviteittoe(new Activiteitje("autobus",200)); italie_trip.druk(1); Resultaat ACTIVITEIT:italie ACTIVITEIT:ravenna activiteitje:klooster 10 activiteitje:geleide stadswandeling 6 totale prijs voor ravenna :16 ACTIVITEIT:venetie activiteitje:dogepaleis 10 activiteitje:brug der Zuchten 2 activiteitje:gondeltocht 20 ACTIVITEIT:kerbezoek_venetie activiteitje:kerk1 3 activiteitje:kerk2 2 activiteitje:kerk3 5 totale prijs voor kerbezoek_venetie :10 totale prijs voor venetie :42 activiteitje:autobus 200 totale prijs voor italie : /395 5

350 Een flexibele lijst Chain of responsability Stel een lijst van aan elkaar gelinkte objecten. Voor elk object kan je doeiets oproepen. Een flexibele lijst doeiets doeiets doeiets?????? Welke versie van doeiets? Als de objecten tot dezelfde klassehiërarchie behoren, weet je op voorhand niet welke versie van doeiets zal opgeroepen worden. Dit wordt tijdens de uitvoering van het programma beslist. We moeten x fr teruggeven Hoeveel stukken van 1 euro, 50 cent,...hoeveel bankbiljetten van 10, 5,..euro geven we best terug? Als we geen bankbiljet van 5 meer hebben, moeten we 5 maal 1 euro teruggeven (indien we dit hebben, dan gaan we over tot 50 cent,...) 350/395 1

351 Als we niet meer kunnen teruggeven? Als het bedrag dat we niet meer kunnen teruggeven klein is, kunnen we dit laten vallen,...(goodwill) Goodwill, Muntjes behoren tot dezelfde klassehiërarchie Welke versie van geefterugvoor? De blokjes zijn aan elkaar gelinkt met het veldje volgendgeld (van het type Geld) geefterugvoor volgendgeld volgendgeld volgendgeld geefterugvoor geefterugvoor Redundantie Omdat zowel Goodwill als Muntjes de veranderlijke volgendgeld hebben, zou het beter zijn om deze veranderlijke in de klasse Geld te steken, private te maken en een functie getvolgendgeld te voorzien die de waarde van die private veranderlijke teruggeeft.?????? In de afgeleide klassen kan men dan met deze functie getvolgendgeld werken. 351/395 2

352 Opbouwen van de lijst Goodwill goodwill=new Goodwill(100,null); Muntjes Euro_1=new Muntjes(1, 4,goodwill); Muntjes Euro_2=new Muntjes(2, 4,Euro_1); Muntjes Euro_5=new Muntjes(5, 4,Euro_2); Muntjes Euro_20=new Muntjes(20, 4, Euro_5); Muntjes Euro_50=new Muntjes(50, 4, Euro_20); Muntjes Euro_100=new Muntjes(100, 4, Euro_50); Muntjes Euro_200=new Muntjes(200, 4, Euro_100); Muntjes Euro_500=new Muntjes(500, 4, Euro_200); Van elke munt hebben we 4 stuks We moeten 6 maal 675 teruggeven We beginnen met te trachten van 500 euro terug te geven Resultaat Euro_500.geefTerugVoor(675); Euro_500.geefTerugVoor(675); Euro_500.geefTerugVoor(675); Euro_500.geefTerugVoor(675); Euro_500.geefTerugVoor(675); Euro_500.geefTerugVoor(675); (goodwill:63):het is niet erg zeker? We regelen het met nieuwjaar Sorry, ik zal de rest (463) later komen brengen 352/395 3

353 interface Geld { public void geefterugvoor(int bedrag); class Goodwill implements Geld { private int goodwill; private Geld volgendgeld; public Goodwill(int goodwill,geld volgendgeld) {this.goodwill=goodwill; this.volgendgeld=volgendgeld; class Muntjes implements Geld { int aantal; int bedragmunt; Geld volgendgeld; public Muntjes(int bedragmunt,int aantal,geld volgendgeld) {this.bedragmunt=bedragmunt;this.aantal=aantal; this.volgendgeld=volgendgeld; public void geefterugvoor(int bedrag) {if(bedrag < 100 ) {System.out.println("(goodwill:"+bedrag +"):het is niet erg zeker? We... "); goodwill=goodwill-bedrag;//geduld raakt op else if(volgendgeld!=null) volgendgeld.geefterugvoor(bedrag); else System.out.println("Sorry, ik zal de rest ("+bedrag+") later komen brengen"); public void geefterugvoor(int bedrag) {//bepaal aantal muntjes van dit we kunnen teruggeven if(aantal>0) {int nodig=bedrag/bedragmunt; if(nodig <= aantal) {for(int i=0;i<nodig;i++) System.out.print(bedragMunt+" "); aantal=aantal-nodig; bedrag=bedrag-nodig*bedragmunt; else {for(int i=0;i<aantal;i++) System.out.print(bedragMunt+" "); bedrag=bedrag-aantal*bedragmunt; if(bedrag>0) if(volgendgeld!=null)volgendgeld.geefterugvoor(bedrag); else System.out.println("we kunnen niet verder: teruggeven, er blijft over:"+bedrag); Flexibele oproep volgendgeld.geefterugvoor(bedrag); We weten niet op voorhand welke versie van geefterugvoor zal opgeroepen worden 353/395 4

354 Conclusie Bij chain of responsability bouwen we een ketting op Elk onderdeel heeft een bepaalde verantwoordelijkheid en kan al dan niet beroep doen op het volgend element in de flexibele lijst. 354/395 5

355 Een flexibele lijst Decorator Stel een lijst van aan elkaar gelinkte objecten. Voor elk object kan je doeiets oproepen. Een flexibele lijst waarbij elk element een andere transformatie doet doeiets doeiets doeiets?????? Eerst enkele losse programma s Stel dat je een klasse SpatiesWeg wilt maken dat in een lusje letters uit een tekst wilt lezen. Neem als tekst "dit is een zin De methode die we hiervoor gebruiken is read_letter. Het resultaat zijn achtereenvolgens de letters uit "ditiseenzin" De klasse Tekst en SpatiesWeg hebben beiden een methode read_letter. In een verdere versie zullen deze twee klassen een gemeenschappelijke interface implementeren met die methode. 355/395 1

356 //"dit is een zin" ->"ditiseenzin" class SpatiesWeg { private Tekst s; public SpatiesWeg(Tekst s){this.s=s; public char read_letter() {char hulp; do{hulp=s.read_letter(); while(hulp!=((char)(-1)) &&hulp==' '); return hulp; public class Decorator1 { public static void main(string args[]) { Tekst tr=new Tekst("dit is een zin"); SpatiesWeg swr=new SpatiesWeg(tr); char hulp=swr.read_letter(); while(hulp!=(char)-1) { System.out.print(hulp); hulp=swr.read_letter(); Een tweede transformatie De eerste letter van een nieuw woord wordt als hoofdletter teruggegeven "dit is een zin"->"dit Is Een Zin" //"dit is een zin"->"dit Is Een Zin" class EersteLetterHoofdLetter { private Tekst s; private char vorigeletter=' '; public EersteLetterHoofdLetter(Tekst s){this.s=s; public char read_letter() {char hulp=s.read_letter(); if(vorigeletter==' '&& Character.isLowerCase(hulp)) hulp=character.touppercase(hulp); vorigeletter=hulp; return hulp; public class Decorator2 { public static void main(string args[]) { Tekst tr=new Tekst("dit is een zin"); EersteLetterHoofdLetter swr =new EersteLetterHoofdLetter(tr); char hulp=swr.read_letter(); while(hulp!=(char)-1) { System.out.print(hulp); hulp=swr.read_letter(); 356/395 2

357 Een derde transformatie We gaan spaties tussenvoegen "dit is een zin"->"d i t i s e e n z i n De oneven letters die read_letter zal teruggeven staan in de oorspronkelijke string. De even letters zijn steeds een spatie Waarom drie transformaties? Dan kunnen we deze drie transformatie in willekeurige volgorde combineren. "dit is een zin" -> "D i t I s E e n Z i n " "dit is een zin" -> "DitIsEenZin" "dit is een zin" -> "D i t i s e e n z i n " "dit is een zin" -> "DITISEENZIN" "dit is een zin" -> "Dit Is Een Zin" "dit is een zin" -> "ditiseenzin" "dit is een zin" -> "DitIsEenZin" test( new SpatiesWegXReader( new ExtraSpatiesXReader( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") )))); 357/395 3

358 "dit is een zin" -> "D i t I s E e n Z i n " "dit is een zin"->"d i t i s e e n z i n " test( new ExtraSpatiesXReader( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") ))); test( new ExtraSpatiesXReader ( new EersteLetterHoofdLetterXReader( new SpatiesWegXReader( new TekstXReader("dit is een zin") )))); "dit is een zin" -> "DITISEENZIN" "dit is een zin" -> "Dit Is Een Zin" test( new SpatiesWegXReader ( new EersteLetterHoofdLetterXReader( new ExtraSpatiesXReader ( new TekstXReader("dit is een zin") )))); test( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") )); 358/395 4

359 "dit is een zin" -> "ditiseenzin" test( new SpatiesWegXReader( new TekstXReader("dit is een zin") )); Flexibiliteit Omdat we aan elk object een object van een andere klasse moeten kunnen doorgeven, moeten we ze een zelfde interface laten implementeren. Elk object heeft een referentie naar een volgend XReader object Heel het programma interface XReader {public char read_letter(); class TekstXReader implements XReader { private String s; private int index=0; public TekstXReader(String s){this.s=s; public char read_letter() {if(index<s.length())return s.charat(index++); else return (char)-1;//geeft einde weer 359/395 5

360 class SpatiesWegXReader implements XReader { private XReader s; public SpatiesWegXReader(XReader s){this.s=s; public char read_letter() {char hulp; do{hulp=s.read_letter(); while(hulp!=((char)(-1)) &&hulp==' '); return hulp; Flexibele oproep class EersteLetterHoofdLetterXReader implements XReader { private XReader s; private char vorigeletter=' '; public EersteLetterHoofdLetterXReader(XReader s){this.s=s; public char read_letter() {char hulp=s.read_letter(); if(vorigeletter==' '&& Character.isLowerCase(hulp)) hulp=character.touppercase(hulp); vorigeletter=hulp; return hulp; class ExtraSpatiesXReader implements XReader { private XReader s; private boolean extraspatie=false; public ExtraSpatiesXReader(XReader s){this.s=s; public char read_letter() {char hulp=' '; if(!extraspatie) hulp=s.read_letter(); extraspatie=!extraspatie; return hulp; public class Decorator4 { public static void test(xreader xr) { char hulp=xr.read_letter(); while(hulp!=(char)-1) { System.out.print(hulp); hulp=xr.read_letter(); System.out.println(); public static void main(string args[]) { test( new ExtraSpatiesXReader( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") ))); test( new SpatiesWegXReader( new ExtraSpatiesXReader( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") )))); test( new ExtraSpatiesXReader ( new EersteLetterHoofdLetterXReader( new SpatiesWegXReader( new TekstXReader("dit is een zin") )))); test( new SpatiesWegXReader ( new EersteLetterHoofdLetterXReader( new ExtraSpatiesXReader ( new TekstXReader("dit is een zin") )))); test( new EersteLetterHoofdLetterXReader( new TekstXReader("dit is een zin") )); test( new SpatiesWegXReader( new TekstXReader("dit is een zin") )); En de java.io klassen? Reader, BufferedReader, StringReader, FileReader, CharArrayReader,... zijn volgens dit decorator patroon gemaakt. Je kan Reader s op een willekeurige wijze combineren. 360/395 6

361 Het is een client-server programma Wackotank Over een netwerk tanks tegen elkaar laten vechten De eerste gebruiker moet de server die met de anderen communiceert opstarten door start new game te kiezen Hij moet instellen op welke poort het programma draait (standaard 2000) Andere kunnen aansluiten bij dit spel Anderen starten ook wackotank.jar op maar kiezen nu connect to game en vullen het ip-adres van de pc, waar start new game werd gekozen, in. (deze moet ipconfig runnen om zijn ip-adres te kennen). Hoe spelen? Beweeg de tank met de pijltjes toetsen. De geschutskoepel richten doe je door de muis te bewegen Schieten doe je door met de muis te klikken 361/395 1

362 Twee typen kogels Overal in het veld verschijnen voorraden van canon of missiles. Je kan overschakelen tussen beiden door P of O in te drukken. Het spel is flexibel geprogrammeerd ten opzichte van deze typen kogels De eerste gebruiker kiest start new game De andere kiezen connect to game 362/395 2

363 Xml? Layout kiezen: in het opstartscherm De layout van het veld wordt in xml bepaald: Obstacles : waar ze staan en in welke richting images die overal gebruikt worden Voor elke layout is er een ander bijhorend xml bestand desert layout De achtergrond De achtergrond bestaat uit een matrix waarvan men de dimentie in een xml bestand steekt, samen met uit hoeveel tekeningen die matrix is opgebouwd en waar deze tekeningen komen. In het eerste voorbeeld hebben we een 4x4 matrix bestaande uit twee tekeningen 363/395 3

364 level01.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE level (View Source for full doctype...)> <level name="desert"> <tileimages> <image path="ground/rock.png" /> <image path="ground/concrete.jpg" /> </tileimages> <tiles x="4" y="4" width="512" height="512"> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="1" /> <tile img="1" /> <tile img="0" /> <tile img="0" /> <tile img="1" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> <tile img="0" /> </tiles> De obstacles en hun posities zitten ook in level01.xml Eerst definieren we alle obstacles -images Vervolgens geven we aan waar we overal zulk obstakel willen hebben en of het al dan niet geroteerd is. <obstacleimages> <image path="obstacles/wall.png" /> <image path="obstacles/antitank1.png" /> </obstacleimages> <obstacles> <obstacle x="512" y="512" rot="0" img="0" /> <obstacle x="609" y="512" rot="0" img="0" /> <obstacle x="706" y="512" rot="0" img="0" /> <obstacle x="900" y="512" rot="0" img="0" /> <obstacle x="997" y="512" rot= 45" img="0" /> <obstacle x="700" y="1200" rot="0" img="1«/> <obstacle x="800" y="1250" rot="0" img="1«/> <obstacle x="750" y="1350" rot="0" img="1«/> <obstacle x="600" y="1450" rot="0" img="1«/> <obstacle x="825" y="1500" rot="0" img="1«/>...enzovoorts... </obstacles> </level> Meerdere layouts voorzien door andere xml bestanden op te geven Het is eenvoudig om nieuwe levels toe te voegen met andere tekeningen en andere obstakels. Het programma dedecteert dat er nieuwe xml bestanden zijn en zal de namen van deze levels automatisch in het opstartscherm laten zien 364/395 4

365 dunes layout level02.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE level (View Source for full doctype...)> <level name="dunes"> <tileimages> <image path="ground/grass1.jpg" /> <image path="ground/grass2.jpg" /> <image path="ground/grass3.jpg" /> </tileimages> <tiles x="2" y="3" width="512" height="512"> <tile img="0" /> <tile img="0" /> <tile img="1" /> <tile img="1" /> <tile img="2" /> <tile img="2" /> </tiles> Dtd <obstacleimages> <image path="obstacles/wall.png" /> </obstacleimages> <obstacles> <obstacle x="0048" y="512" rot="0" img="0" /> <obstacle x="0145" y="512" rot="0" img="0" /> <obstacle x="0242" y="512" rot="0" img="0" /> <obstacle x="0339" y="512" rot="0" img="0" /> <obstacle x="0436" y="512" rot="0" img="0" /> <obstacle x="0533" y="512" rot="0" img="0" /> <obstacle x="800" y="512" rot="0" img="0" /> <obstacle x="897" y="512" rot="0" img="0" /> <obstacle x="994" y="512" rot="0" img="0" /> </obstacles> </level> In deze volledige xml heeft Willem de volledige dtd (een soort beschrijving van de xml-taal die hij ontwikkeld heeft om de layout vast te leggen) opgenomen. <!ELEMENT level (tileimages,tiles,obstacleimages,obstacles)> <!ELEMENT tileimages (image*)> <!ELEMENT tiles (tile*)> <!ELEMENT obstacleimages (image*)> <!ELEMENT obstacles (obstacle*)> 365/395 5

366 <!ELEMENT level (tileimages,tiles, obstacleimages,obstacles)> Met behulp van zulke dtd kan men nagaan of de xml die volgt wel voldoet aan de xmltaal Zo is het begin element steeds level Hierin moeten tileimages,tiles,obstacleimages,obstacles komen. <!ELEMENT tileimages (image*)> tileimages bestaat uit 0 of meerdere image elementen. <!ATTLIST image path CDATA #REQUIRED> Een image heeft een attribuut path dat verplicht aanwezig moet zijn. Volledige xml van level01.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE level [ <!ELEMENT level (tileimages,tiles,obstacleimages,obstacles)> <!ELEMENT tileimages (image*)> <!ELEMENT tiles (tile*)> <!ELEMENT obstacleimages (image*)> <!ELEMENT obstacles (obstacle*)> <!ELEMENT tile EMPTY> <!ELEMENT obstacle EMPTY> <!ELEMENT image EMPTY> <!ATTLIST level name CDATA #REQUIRED> <!ATTLIST tiles x CDATA #REQUIRED> <!ATTLIST tiles y CDATA #REQUIRED> <!ATTLIST tiles width CDATA #REQUIRED> <!ATTLIST tiles height CDATA #REQUIRED> <!ATTLIST tile img CDATA #REQUIRED> <!ATTLIST obstacle x CDATA #REQUIRED> <!ATTLIST obstacle y CDATA #REQUIRED> <!ATTLIST obstacle rot CDATA #REQUIRED> <!ATTLIST obstacle img CDATA #REQUIRED> <!ATTLIST image path CDATA #REQUIRED> ]> <level name="desert"> <tileimages> <image path="ground/rock.png"></image> <image path="ground/concrete.jpg"></image> </tileimages> <tiles x="4" y="4" width="512" height="512"> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="1"></tile> <tile img="1"></tile> <tile img="0"></tile> <tile img="0"></tile> 366/395 6

367 <tile img="1"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> <tile img="0"></tile> </tiles> <obstacleimages> <image path="obstacles/wall.png"></image> <image path="obstacles/antitank1.png"></image> </obstacleimages> <obstacles> <obstacle x="512" y="512" rot="0" img="0"></obstacle> <obstacle x="609" y="512" rot="0" img="0"></obstacle> <obstacle x="706" y="512" rot="0" img="0"></obstacle> <obstacle x="900" y="512" rot="0" img="0"></obstacle> <obstacle x="997" y="512" rot="0" img="0"></obstacle> <obstacle x="1094" y="512" rot="0" img="0"></obstacle> <obstacle x="1224" y="800" rot="0.75" img="0 /> <obstacle x="1024" y="1024" rot="3.14" img="0 /> <obstacle x="1121" y="1024" rot="3.14" img="0 /> <obstacle x="512" y="1536" rot="3.14" img="0 /> <obstacle x="609" y="1536" rot="3.14" img="0 /> <obstacle x="800" y="1536" rot="3.14" img="0 /> <obstacle x="897" y="1536" rot="3.14" img="0 /> <obstacle x="994" y="1536" rot="3.14" img="0 /> <obstacle x="700" y="1200" rot="0" img="1 /> <obstacle x="800" y="1250" rot="0" img="1 /> <obstacle x="750" y="1350" rot="0" img="1 /> <obstacle x="600" y="1450" rot="0" img="1 /> <obstacle x="825" y="1500" rot="0" img="1 /> </obstacles> </level> <obstacle x="1024" y="700" rot="0.75" img="0"> </obstacle> <obstacle x="1024" y="900" rot="0.75" img="0"> </obstacle> 367/395 7

368 Matrixbewerkingen op tekeningen Beschreven in xml Transparante vlakken Filters komen uit xml bestand 368/395 1

369 Filters zitten in xml bestand Het bestand standaardfilters.xml wordt gelezen. Hieruit wordt het menu opgebouwd De matrixbewerkingen zitten in het xml bestand StandaardFilters.xml <?xml version="1.0" encoding="utf-8"?> <effecten> <Info> <Versie>10000</Versie> </Info> <Scherpte> <Verzachten> <type>matrix</type> <sliders> </sliders> <matrix> <div>5</div> <off>0</off> <size>3</size> <e0>1</e0><e1>0</e1><e2>1</e2> <e3>0</e3><e4>1</e4><e5>0</e5> <e6>1</e6><e7>0</e7><e8>1</e8> </matrix> </Verzachten> 369/395 2

370 <GausianBlur> <type>matrix</type> <sliders> </sliders> <matrix> <div>33</div> <off>0</off> <size>5</size> <e0>2</e0><e1>1</e1><e2>1</e2><e3>1</e3> <e4>2</e4><e5>1</e5><e6>2</e6><e7>2</e7> <e8>2</e8><e9>1</e9><e10>1</e10><e11>2</e11> <e12>0</e12><e13>2</e13><e14>1</e14> <e15>1</e15><e16>2</e16><e17>2</e17> <e18>2</e18><e19>1</e19><e20>2</e20> <e21>1</e21><e22>1</e22> <e23>1</e23><e24>2</e24> </matrix> </GausianBlur> <Verscherpen> <type>matrix</type> <sliders> </sliders> <matrix> <div>1</div> <off>0</off> <size>3</size> <e0>0</e0><e1>-1</e1><e2>0</e2> <e3>-1</e3><e4>5</e4><e5>-1</e5> <e6>0</e6><e7>-1</e7><e8>0</e8> </matrix> </Verscherpen> </Scherpte> Kleuren filters Antieke filter 370/395 3

371 <Kleuren> <Antiek> <type>calc</type> <sliders> </sliders> <calc> <methode>+</methode> <div>1</div> <r>50</r><g>20</g><b>-50</b> </calc> </Antiek> Bitafvlakking : slider is ook in xml beschreven Gamma-filter, slider zit ook in xml Helderheid aanpassen, met xml slider 371/395 4

372 5 <Bitafvlakking> <type>lut</type> <sliders> <a> <naam>bpp</naam> <min>1</min> <max>64</max> </a> </sliders> <lut> <start>0</start> <eind>255</eind> <deler>1</deler> </lut> </Bitafvlakking> <Gamma> <type>calc</type> <sliders> <a> <naam>level</naam> <min>0</min> <max>400</max> </a> </sliders> <calc> <methode>*</methode> <div>100</div> <r>0</r> <g>0</g> <b>0</b> </calc> </Gamma> <Helderheid> <type>calc</type> <sliders> <a> <naam>level</naam> <min>-100</min> <max>100</max> </a> </sliders> <calc> <methode>+</methode> <div>1</div> <r>0</r> <g>0</g> <b>0</b> </calc> </Helderheid> Ghostkleuren Inversie 372/395

373 <Ghostcolors> <type>lut</type> <sliders></sliders> <lut> <start>100</start> <eind>50</eind> <deler>1</deler> </lut> </Ghostcolors> <Inversie> <type>lut</type> <sliders></sliders> <lut> <start>255</start> <eind>0</eind> <deler>1</deler> </lut> </Inversie> Rood Groen Blauw veranderen <RGB> <type>calc</type> <sliders> <a> <naam>rood</naam> <min>-255</min> <max>255</max> </a> <b> <naam>groen</naam> <min>-255</min> <max>255</max> </b> <c> <naam>blauw</naam> <min>-255</min> <max>255</max> </c> </sliders> <calc> <methode>+</methode> <div>1</div> <r>0</r> <g>1</g> <b>2</b> </calc> </RGB> Vergrijs 373/395 6

374 <Vergrijs> <type>calc</type> <sliders> <a> <naam>rood</naam> <max>100</max> <min>0</min> </a> <b> <naam>groen</naam> <max>100</max> <min>0</min> </b> <c> <naam>blauw</naam> <max>100</max> <min>0</min> </c> </sliders> <calc> <methode>mix</methode> <div>1</div> <r>0</r> <g>1</g> <b>2</b> </calc> </Vergrijs> Speciale effekten Emboss Relief 374/395 7

375 </Kleuren> <Speciaal> <Emboss> <type>matrix</type> <sliders></sliders> <matrix> <div>1</div> <off>127</off> <size>3</size> <e0>1</e0><e1>0</e1><e2>0</e2> <e3>0</e3><e4>0</e4><e5>0</e5> <e6>0</e6><e7>0</e7><e8>-1</e8> </matrix> </Emboss> <Relief> <type>matrix</type> <sliders></sliders> <matrix> <div>1</div> <off>127</off> <size>3</size> <e0>1</e0><e1>1</e1><e2>-1</e2> <e3>1</e3><e4>0</e4><e5>-1</e5> <e6>0</e6><e7>-1</e7><e8>-1</e8> </matrix> </Relief> </Speciaal> Stenen Textiel 375/395 8

376 <Bumpmaps> <Stenen> <type>alpha</type> <sliders></sliders> <alpha> <file>muur.png</file> <tile>j</tile> </alpha> </Stenen> Undo functie is voorzien (meerdere levels) <Textiel> <type>alpha</type> <sliders></sliders> <alpha> <file>papier.png</file> <tile>j</tile> </alpha> </Textiel> </Bumpmaps> </effecten> 376/395 9

377 Veel xml bestanden Verschillende versies van spelletjes Beschreven in xml Menu dynamisch vanuit xml genereert Dit menu wordt gegenereerd vanuit games.xml Games.xml <?xml version="1.0" encoding="utf-8"?> <games> <game file="si.xml"/> <game file="snake.xml"/> <game file="pong.xml"/> <game file="warpong.xml"/> <game file="bubble.xml"/> </games> 377/395 1

378 Bubbles: Bij raak schieten halveert de springende bal Het spel werkt met algemene Enemy In het spel zelf zitten geen bijzonderheden van het specifieke spel. Deze zitten in klassen die overerven van Enemy. 378/395 2

379 Een meer volledig overervingsverband Welke objecten bij welk spel horen: zit in xml Het aanmaken van de juiste objecten is niet voor elk spel afzonderlijk geprogrammeerd. Voor elk spel is er een xml bestand dat weergeeft welke concrete vijanden er voor dat spel zijn. Het algemeen programma gaat tijdens de uitvoering deze concrete vijanden aanmaken. Nieuwe Spelletjes, andere vijanden? Andere speler? Overerven van Player! Gewoon een xml bestand voor het nieuw spelletje aanmaken. Zijn er andere spelers? Andere vijanden? Overerven van Enemy, overerven van Player en de namen van deze nieuwe klassen gebruiken in de xml beschrijving van het spel 379/395 3

380 Bubble.xml <?xml version="1.0" encoding="utf-8"?> <game name="bubble Trouble" background="bt01.jpg"> <help><![cdata[<html> <p> <font face="courier New" size="5" color="#0000ff"> <u><b>bubble Trouble</b></u><br> </font> </p> <p> <font face="courier New"> Try to remove all bubbles from te level, but be carefull<br> that a bubble doesn't hit you! Splattered bubbles split<br> into 2 smaller bubbles. </font> </p>...verder html... ]]> </help> <players> <BTPlayer name="player 1" x="200" y="490"> <KeyboardController/> <BTNormalGun x="27" y="60"/> </BTPlayer> </players> <level name="bubble Trouble 1"> <BTEnemy x="100" y="300" size="50" goright="true"/> </level> <level name="bubble Trouble 2"> <BTEnemy x="300" y="200" size="50" goright="false"/> <BTEnemy x="100" y="300" size="50" goright="true"/> </level> </game> Bubbels.xml Hierin staan aldus welke spelers er zijn, welke klassen (BTPlayer) erbij horen. Welke vijanden er zijn, welke klassen (BTEnemy ) erbij horen. Hoe aangeven of het spel met toetsenbord bestuurbaar is? <KeyboardController/> Opgeven in xml bestand van spel Hoeveel levels zijn er? Hoe het spel bestuurd wordt (<KeyboardController/>) 380/395 4

381 Hoe het spel besturen? Games.xml <?xml version="1.0" encoding="utf-8"?> <games> <game file="si.xml"/> <game file="snake.xml"/> <game file="pong.xml"/> <game file="warpong.xml"/> <game file="bubble.xml"/> </games> Space Invaders 381/395 5

382 Het spel is gemaakt voor Enemy Er zijn meerdere spelletjes, allemaal hebben ze vijanden, die zich anders gedragen. Voor space invaders: andere enemies De verschillen tussen de vijanden zitten niet in het algemeen spel, maar in klassen die ervan overerven. Space invaders <?xml version="1.0" encoding="utf-8"?> <game name="space Invaders"> <help><![cdata[<html>...html uitleg over het spel... ]]> </help> <players> <SIPlayer name="player 1" x="200" y="460"> <SIPlayerGun x="10" y="0" angle="90"/> <SIPlayerGun x="40" y="0" angle="90"/> <KeyboardController/> </SIPlayer> <SIPlayer name="player 1" x="200" y="460"> <SIPlayerGun x="25" y="0" angle="90"/> <MouseController/> </SIPlayer> </players> <!-- *********************************************** --> <!-- ****************** LEVEL 1 ******************** --> <!-- *********************************************** --> <level name="level 1"> <SISpawner/> <SIItemSpawner/> <ObjectGroup> <SIEnemy x="10" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="60" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="110" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="160" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="210" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="260" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="310" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy>... Nog vijanden </ObjectGroup> </level> 382/395 6

383 <!-- ************************************************** --> <!-- ****************** LEVEL 2 ********************** --> <!-- ************************************************* --> <level name="level 2"> <SIItemSpawner/> <ObjectGroup> <SISpecialEnemy x="10" y="10"> <SIEnemyGun x="20" y="0" angle="270"/> </SISpecialEnemy> <SIEnemy x="60" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy> <SIEnemy x="110" y="10"> <SIEnemyGun x="20" y="0" angle="270"/></sienemy>...nog vijanden... </ObjectGroup> </level> SIEnemy.xml <?xml version="1.0" encoding="utf-8"?> <descriptor name="sienemy"> <item index="0" type="game" name="game" /> <item index="1" type="double" name="x" /> <item index="2" type="double" name="y" /> </descriptor> <!-- ************************************************* --> <!-- ********************* LEVEL 3 ******************* --> <!-- ************************************************* -->...enzovoorts Overal horen java-klassen bij: flexibiliteit m.b.v. xml public class SIEnemy extends Enemy { public SIEnemy(Game parent) {this(parent, 10, 10); public SIEnemy(Game parent, double x, double y) { super(parent,new Rectangle2D.Double(x,y,41,34),10,100, 10); public String tostring(){ return "[SIEnemy ] "; protected int getfireinterval() { return (int) (Math.random() * ); protected Image getgraphic() { return Toolkit.getDefaultToolkit().getImage( this.getclass().getresource("si-enemy.png")); protected void move(rectangle2d size) {setx(getx() + 0.5); SIEnemyGun.xml <?xml version="1.0" encoding="utf-8"?> <descriptor name="sienemygun"> <item index="0" type="game" name="game" /> <item index="1" type="prev" name="gameobject" /> <item index="2" type="double" name="x" /> <item index="3" type="double" name="y" /> <item index="4" type="double" name="angle" /> </descriptor> protected void dieaction() {new Explosion(this); 383/395 7

384 Games.xml Snake met twee spelers <?xml version="1.0" encoding="utf-8"?> <games> <game file="si.xml"/> <game file="snake.xml"/> <game file="pong.xml"/> <game file="warpong.xml"/> <game file="bubble.xml"/> </games> Snake.xml: twee spelers (muis+keyboard) <?xml version="1.0" encoding="utf-8"?> <game name="snake"> <help><![cdata[<html>...html help tekst... ]]> </help> <players> <SnakePlayer name="player 1" x="200" y="200"> <KeyboardController/> </SnakePlayer> <SnakePlayer name="player 2" x="200" y="300"> <MouseController/> </SnakePlayer> </players> <level name="snake"> <SnakeItem/> </level> </game> /* Auteur: Sam Bertels * SnakePlayer is een speler voor het spel * snake. Aan dit object kunnen SnakePieces * (of andere objecten die de interface SnakeIF * implementeren) gekoppeld worden om zo een * slang te bekomen. * ************************************/ public class SnakePlayer extends Player implements SnakeIF { private double prevmove = System.currentTimeMillis(); private SnakeIF child = null; public SnakePlayer(Game p, String n, double x, double y) { super(p, n, new Rectangle2D.Double(x,y,20,20), 1, 0, 0, false, false); setspeed(getwidth()); go(right); super.move(); new SnakePiece(p, this.getlastitem()); new SnakePiece(p, this.getlastitem()); new SnakePiece(p, this.getlastitem()); new SnakePiece(p, this.getlastitem()); new SnakePiece(p, this.getlastitem()); 384/395 8

385 public SnakeIF getlastitem() { if (child == null) return this; else return child.getlastitem(); public void setnextitem(snakeif item) { child = item; public void stop(int direction) { public void doside(int direction) { died(); public void paint(graphics2d g) { g.setcolor(new Color(173,233,255)); g.fillrect((int) getx(), (int) gety(),(int) getwidth(), (int) getheight()); public void go(int direction) { // Zorg ervoor dat er slechts in 1 richting kan bewogen // worden (-> schuin bewegen mag niet bij snake) super.stop(this.up); super.stop(this.left); super.go(direction); public void goto(int x, int y) { // Voor de bediening via de muis: bereken in welke richting // de muis het dichtst tegen dit object zit double dx = getx() - x; double dy = gety() - y; // En beweeg in die richting if (Math.abs(dX) > Math.abs(dY)) { // Beweeg in Y-richting if (dx < 0) go(right); else go(left); else { // Beweeg in X-richting if (dy < 0) go(down); else go(up); public void move() { // Zorg ervoor dat enkel om de 200ms bewogen kan worden // (anders heeft de slang een iets te hoge snelheid) double x = System.currentTimeMillis(); if (x - prevmove >= 200) { super.move(); prevmove = x; protected void died() { // Verwijder ook het child-object uit het spel if (child!= null) ((GameObject) child).died(); super.died(); Games.xml Pong <?xml version="1.0" encoding="utf-8"?> <games> <game file="si.xml"/> <game file="snake.xml"/> <game file="pong.xml"/> <game file="warpong.xml"/> <game file="bubble.xml"/> </games> 385/395 9

386 Pong.xml Games.xml <?xml version="1.0" encoding="utf- 8"?> <game name="pong" maxscore="10"> <help><![cdata[<html> html help tekst ]]> </help> <players> <PongPlayer name="player 1" x="20" y="200"> <KeyboardController/> </PongPlayer> <PongPlayer name="player 2" x="760" y="200"> <MouseController/> </PongPlayer> </players> <?xml version="1.0" encoding="utf-8"?> <games> <game file="si.xml"/> <game file="snake.xml"/> <game file="pong.xml"/> <game file="warpong.xml"/> <game file="bubble.xml"/> </games> <level name="pong"> <ChangeableBackground/> <PongBullet/> </level> </game> WarPong Weer een ander spel: pong waarbij men kan schieten <?xml version="1.0" encoding="utf-8"?> <game name="war-pong" background="back.jpg"> <help><![cdata[<html> html help tekst ]]> </help> <players> <PongPlayer name="player 1" x="20" y="200"> <SIPlayerGun x="10" y="30" angle="0"/> <KeyboardController/> </PongPlayer> <PongPlayer name="player 2" x="760" y="200"> <SIPlayerGun x="10" y="30" angle="180"/> <MouseController/> </PongPlayer> </players> <level name="pong"> <PongBullet/> </level> </game> 386/395 10

387 Start toepassing De layout van een hele toepassing in xml Xaml, avalon Bijhorende Start.xml Bijhorende Start.java <?xml version="1.0" encoding="utf-8"?> <frame name="welkom" DefaultCloseOperation="JFrame.EXIT_ON_CLOSE" size="400,400" > <panel background="yellow"> <button id="btn1" text="hallo world demo"/> <button id="btn2" text="menu demo"/> <button id="btn3" text="rekenmachine :-)"/> <button id="btn4" text="datasource demo(weblogs/rss))"/> <button id="btn5" text="datasource demo2 (amazon)"/> import...//ingekort // Stijn public class Start extends JFrameResource { public JButton btn1, btn2, btn3, btn4,btn5; public Start() { new XmlToGui(this).convert(this.getClass().getResource( "/xml/start.xml").tostring()); this.setvisible(true); public static void main(string[] args) { new Start(); </panel> </frame> public void btn1_click(actionevent e) {new HelloWorld(); public void btn2_click(actionevent e) {new MenuTest(); public void btn3_click(actionevent e) {new Rekenmachine(); public void btn4_click(actionevent e) {new Demo4(); public void btn5_click(actionevent e) {new Demo0(); 387/395 1

388 XmltoGui Gaat de xml file lezen en hiermee de veranderlijken uit Start.java gaan initializeren. Toepassing 1 HelloWorld demo Gaat eventlisteners voor het indrukken van de knoppen en deze koppelen aan de routines btn1_click... scherm.xml (hoort bij HelloWorld.java) <?xml version="1.0" encoding="utf-8"?> <frame name="frame2"> <panel background="yellow"> <label LabelFor="tf" Font="Comic Sans MS-BOLD-30" Foreground="blue" text="hello World!"/> <textfield id="tf" Columns="20" Text="stijn" background="red"/> <button id="btn1" Text="Click Here"/> <label id="result" text="initieel"/> </panel> </frame> import...//ingekort HelloWorld.java public class HelloWorld extends JFrameResource { public JTextField tf; public JButton btn1; public JLabel result; public HelloWorld() { new XmlToGui(this).convert(this.getClass().getResource( "/xml/scherm.xml").tostring()); this.setsize(400, 400); this.setvisible(true); public static void main(string[] args) {new HelloWorld(); public void btn1_click(actionevent e) { result.settext(tf.gettext()); 388/395 2

389 Toepassing2: MenuTest Menu.xml (hoort bij MenuTest.java) <?xml version="1.0" encoding="utf-8"?> <frame title="menu testing" size="1000,400" > <panel background="yellow" layout="borderlayout"> </panel> </frame> <menubar constraints="borderlayout.north"> <menu label="file"> <menuitem label="new" icon="icons/new.gif"/> <menuitem label="exit" id="exit" icon="icons/exit.gif"/> </menu> <menu label="edit" id="edit"> <menuitem label="cut" icon="icons/cut.gif"/> <menuitem label="copy" icon="icons/copy.gif"/> <menuitem label="paste" icon="icons/paste.gif"/> </menu> </menubar> MenuTest.java import...//ingekort public class MenuTest extends JFrameResource { public JMenuBar menubar; public JMenuItem exit; Toepassing 3: Rekenmachine public MenuTest() { new XmlToGui(this).convert(this.getClass().getResource( /xml/menu.xml").tostring()); this.setvisible(true); public static void main(string[] args) {new MenuTest(); public void exit_click(actionevent e) { System.out.println("sluiten"); 389/395 3

390 Rekenmachine.xml <?xml version="1.0" encoding="utf-8"?> <frame title="rekenmachine" size="500,400"> <panel background="yellow" layout="borderlayout"> <label Font="Impact-BOLD-30" Foreground="blue" text="welkom op de rega rekenmachine" constraints="borderlayout.north"/> <textfield id="result" Columns="5" font ="Impact-BOLD-50" Text="" background="red" constraints="borderlayout.west"/> <panel background="blue constraints="borderlayout.center" layout="gridlayout(4,3)"> <button id="btn1" text="1"/> <button id="btn2" text="2"/> <button id="btn3" text="3"/> <button id="btn4" text="4"/> <button id="btn5" text="5"/> <button id="btn6" text="6"/> <button id="btn7" text="7"/> <button id="btn8" text="8" background="blue"/> <button id="btn9" text="9"/> <button id="btn0" text="0"/> </panel> <panel constraints="borderlayout.south"> <button id="plus" text="+"/> <button id="min" text="-"/> <button id="maal" text="*"/> <button id="gedeeld" text="/"/> <button id="is" text="="/> </panel> </panel> </frame> import...//ingekort Rekenmachine.java public class Rekenmachine extends JFrameResource { public JTextField result; public JButton btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0; public JButton plus, min, maal, gedeeld, is; private Operator op; public Rekenmachine() { new XmlToGui(this).convert(this.getClass().getResource( "/xml/rekenmachine.xml").tostring()); setvisible(true); public static void main(string[] args) {new Rekenmachine(); public void btn1_click( ActionEvent e) { result.settext(result.gettext() + "1"); public void btn2_click( ActionEvent e) { result.settext(result.gettext() + "2"); public void btn3_click( ActionEvent e) { result.settext(result.gettext() + "3"); public void btn4_click( ActionEvent e) { result.settext(result.gettext() + "4"); public void btn5_click( ActionEvent e) { result.settext(result.gettext() + "5"); public void btn6_click( ActionEvent e) { result.settext(result.gettext() + "6"); public void btn7_click( ActionEvent e) { result.settext(result.gettext() + "7"); public void btn8_click( ActionEvent e) { result.settext(result.gettext() + "8"); public void btn9_click( ActionEvent e) { result.settext(result.gettext() + "9"); public void btn0_click( ActionEvent e) { result.settext(result.gettext() + "0"); public void plus_click( ActionEvent e) { op = new PlusOperator(); parsen(); 390/395 4

391 public void min_click( ActionEvent e) { op = new MinOperator(); parsen(); public void maal_click( ActionEvent e) { op = new MultiplyOperator(); parsen(); public void gedeeld_click( ActionEvent e) { op = new DivideOperator(); parsen(); public void is_click( ActionEvent e) { try { op.sety(double.parsedouble(result.gettext())); double resultaat = op.function(); result.settext(resultaat + ""); catch (NumberFormatException ex) { System.out.println("verkeerd display formaat"); private void parsen() { try { op.setx(double.parsedouble(result.gettext())); result.settext(""); catch (NumberFormatException e) { System.out.println("verkeerd display formaat"); Toepassing 4: weblogs (Demo4.java) Toepassing (rss) haalt automatisch info van internet 391/395 5

392 Demo4.xml <?xml version="1.0" encoding="utf-8"?> <frame title="weblogs.asp.net" size="1000,400" > <frame.datasource> <xmldatasource source= name="klanten"/> </frame.datasource> <panel layout="borderlayout"> <panel color="blue" constraints="borderlayout.west"> <combobox preferredsize="200,200" datasourcename="klanten collection="/rss/channel/item"> <combobox.itemstyle> <panel layout="boxlayout(boxlayout.y_axis) background="yellow"> <label id="title" text="*bind(title)"/> </panel> </combobox.itemstyle> <combobox.alternateitemstyle> <panel layout="boxlayout(boxlayout.y_axis)" background="gray"> <label id="title" text="*bind(title)"/> </panel> </combobox.alternateitemstyle> <combobox.selecteditemstyle> <panel layout="boxlayout(boxlayout.y_axis)" background="red"> <label id="title" text="*bind(title)" background="yellow"/> </panel> </combobox.selecteditemstyle> </combobox> </panel> <scrollpane constraints="borderlayout.center"> <scrollpane.viewportview> <panel background="blue" layout="boxlayout(boxlayout.y_axis)"> <label font="arial-bold-20" text="*rebind(title)"></label> <editorpane text="*rebind(description)" contenttype="text/html"/> </panel> </scrollpane.viewportview> </scrollpane> </panel> </frame> import...//ingekort Demo4.java Toepassing 5: amazon.com informatie over boeken public class Demo4 extends JFrameResource { public Demo4() { new XmlToGui(this).convert(this.getClass().getResource( "/xml/demo4.xml").tostring()); this.setvisible(true); public static void main(string[] args) { new Demo4(); 392/395 6

393 Informatie van elk boek wordt opgehaald Surf naar 99X8DF&devt=D19U3UCK99X8DF&KeywordSearch=java& mode=books&type=lite&page=1&f=xml Je krijgt gegevens terug in xml formaat Deze gegevens worden door onze toepassing getoond. Het antwoord is xml <?xml version="1.0" encoding="utf-8"?> - <ProductInfo xmlns:xsi=" xsi:nonamespaceschemalocation=" - <Request> - <Args> <Arg value="mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;.NET CLR )" name="useragent" /> <Arg value="0axjwpdw64eckhgwye6m" name="requestid" /> <Arg value="us" name="locale" /> <Arg value="1" name="page" /> <Arg value="java" name="keywordsearch" /> <Arg value="d19u3uck99x8df" name="dev-t" /> <Arg value="d19u3uck99x8df" name="t" /> <Arg value="xml" name="f" /> <Arg value="books" name="mode" /> <Arg value="lite" name="type" /> </Args> </Request> <TotalResults>2846</TotalResults> <TotalPages>285</TotalPages> 393/395 7

MyDHL+ Van Non-Corporate naar Corporate

MyDHL+ Van Non-Corporate naar Corporate MyDHL+ Van Non-Corporate naar Corporate Van Non-Corporate naar Corporate In MyDHL+ is het mogelijk om meerdere gebruikers aan uw set-up toe te voegen. Wanneer er bijvoorbeeld meerdere collega s van dezelfde

Nadere informatie

Wat is een grafische gebruikersinterface (GUI)?

Wat is een grafische gebruikersinterface (GUI)? Wat is een grafische gebruikersinterface (GUI)? GUI is een Engelse afkorting voor Graphical User Interface, oftewel grafische gebruikersinterface. Het is de term voor het bedieningspaneel van een computerprogramma.

Nadere informatie

Swing.

Swing. Swing joost.vennekens@kuleuven.be Typische applicatie View Model van domein Typische applicatie MVC View Model Controller Belangrijk Strikte scheiding tussen taken Duidelijkere code met minder bugs Herbruikbaarheid

Nadere informatie

Een stoomcursus door Edgar de Graaf, november 2006

Een stoomcursus door Edgar de Graaf, november 2006 Programmeren in Java Een stoomcursus door Edgar de Graaf, november 2006 Deze tekst geeft een zeer korte inleiding in de programmeertaal Java, uitgaande van kennis van de taal C++. Daarnaast bestudere men

Nadere informatie

MyDHL+ ProView activeren in MyDHL+

MyDHL+ ProView activeren in MyDHL+ MyDHL+ ProView activeren in MyDHL+ ProView activeren in MyDHL+ In MyDHL+ is het mogelijk om van uw zendingen, die op uw accountnummer zijn aangemaakt, de status te zien. Daarnaast is het ook mogelijk om

Nadere informatie

Firewall van de Speedtouch 789wl volledig uitschakelen?

Firewall van de Speedtouch 789wl volledig uitschakelen? Firewall van de Speedtouch 789wl volledig uitschakelen? De firewall van de Speedtouch 789 (wl) kan niet volledig uitgeschakeld worden via de Web interface: De firewall blijft namelijk op stateful staan

Nadere informatie

Programmeren in Java. De Java-GUI

Programmeren in Java. De Java-GUI DE JAVA-GUI Programmeren in Java 1 Programmeren in Java Section Page Inleiding................................................. 1 1 De Java-GUI............................................. 2 1 Swing-componenten......................................

Nadere informatie

http://www.liacs.nl/home/kosters/java/

http://www.liacs.nl/home/kosters/java/ sheets Programmeren 1 Java college 2, Walter Kosters De sheets zijn gebaseerd op de hoofdstukken 2 tot en met 6 van: D. Bell en M. Parr, Java voor studenten, Prentice Hall, 2002 http://www.liacs.nl/home/kosters/java/

Nadere informatie

SAMPLE 11 = + 11 = + + Exploring Combinations of Ten + + = = + + = + = = + = = 11. Step Up. Step Ahead

SAMPLE 11 = + 11 = + + Exploring Combinations of Ten + + = = + + = + = = + = = 11. Step Up. Step Ahead 7.1 Exploring Combinations of Ten Look at these cubes. 2. Color some of the cubes to make three parts. Then write a matching sentence. 10 What addition sentence matches the picture? How else could you

Nadere informatie

Settings for the C100BRS4 MAC Address Spoofing with cable Internet.

Settings for the C100BRS4 MAC Address Spoofing with cable Internet. Settings for the C100BRS4 MAC Address Spoofing with cable Internet. General: Please use the latest firmware for the router. The firmware is available on http://www.conceptronic.net! Use Firmware version

Nadere informatie

voegtoe: eerst methode bevat gebruiken, alleen toevoegen als bevat() false is

voegtoe: eerst methode bevat gebruiken, alleen toevoegen als bevat() false is PROEF-Tentamen Inleiding programmeren (IN1608WI), X januari 2010, 9.00-11.00, Technische Universiteit Delft, Faculteit EWI, Afdeling 2. Open boek tentamen: bij het tentamen mag alleen gebruik worden gemaakt

Nadere informatie

Applicaties met een grafische user-interface

Applicaties met een grafische user-interface Hoofdstuk 1 Applicaties met een grafische user-interface 1.1 Het MVC paradigma Om code te verkrijgen die onderhoudbaar en herbruikbaar is, is het van groot belang dat GUI code strikt gescheiden wordt van

Nadere informatie

DALISOFT. 33. Configuring DALI ballasts with the TDS20620V2 DALI Tool. Connect the TDS20620V2. Start DALISOFT

DALISOFT. 33. Configuring DALI ballasts with the TDS20620V2 DALI Tool. Connect the TDS20620V2. Start DALISOFT TELETASK Handbook Multiple DoIP Central units DALISOFT 33. Configuring DALI ballasts with the TDS20620V2 DALI Tool Connect the TDS20620V2 If there is a TDS13620 connected to the DALI-bus, remove it first.

Nadere informatie

B1 Woordkennis: Spelling

B1 Woordkennis: Spelling B1 Woordkennis: Spelling Bestuderen Inleiding Op B1 niveau gaan we wat meer aandacht schenken aan spelling. Je mag niet meer zoveel fouten maken als op A1 en A2 niveau. We bespreken een aantal belangrijke

Nadere informatie

2019 SUNEXCHANGE USER GUIDE LAST UPDATED

2019 SUNEXCHANGE USER GUIDE LAST UPDATED 2019 SUNEXCHANGE USER GUIDE LAST UPDATED 0 - -19 1 WELCOME TO SUNEX DISTRIBUTOR PORTAL This user manual will cover all the screens and functions of our site. MAIN SCREEN: Welcome message. 2 LOGIN SCREEN:

Nadere informatie

Add the standing fingers to get the tens and multiply the closed fingers to get the units.

Add the standing fingers to get the tens and multiply the closed fingers to get the units. Digit work Here's a useful system of finger reckoning from the Middle Ages. To multiply $6 \times 9$, hold up one finger to represent the difference between the five fingers on that hand and the first

Nadere informatie

Hoe te verbinden met NDI Remote Office (NDIRO): Apple OS X How to connect to NDI Remote Office (NDIRO): Apple OS X

Hoe te verbinden met NDI Remote Office (NDIRO): Apple OS X How to connect to NDI Remote Office (NDIRO): Apple OS X Handleiding/Manual Hoe te verbinden met (NDIRO): Apple OS X How to connect to (NDIRO): Apple OS X Inhoudsopgave / Table of Contents 1 Verbinden met het gebruik van Apple OS X (Nederlands)... 3 2 Connect

Nadere informatie

EM7680 Firmware Update by OTA

EM7680 Firmware Update by OTA EM7680 Firmware Update by OTA 2 NEDERLANDS/ENGLISH EM7680 Firmware update by OTA Table of contents 1.0 (NL) Introductie... 3 2.0 (NL) Firmware installeren... 3 3.0 (NL) Release notes:... 3 4.0 (NL) Overige

Nadere informatie

Omschrijf bij ieder onderdeel van de methode de betekenis ervan. Java kent twee groepen van klassen die een GUI kunnen maken: awt en swing.

Omschrijf bij ieder onderdeel van de methode de betekenis ervan. Java kent twee groepen van klassen die een GUI kunnen maken: awt en swing. irkel (met Jpanel) ij de onderstaande opdracht behoort het bestand Panels: JPanels_1.java (map Panel) in de map irkel. pplicaties in Java hebben altijd een publieke klasse waarin een methode main voorkomt.

Nadere informatie

General info on using shopping carts with Ingenico epayments

General info on using shopping carts with Ingenico epayments Inhoudsopgave 1. Disclaimer 2. What is a PSPID? 3. What is an API user? How is it different from other users? 4. What is an operation code? And should I choose "Authorisation" or "Sale"? 5. What is an

Nadere informatie

MyDHL+ Tarief berekenen

MyDHL+ Tarief berekenen MyDHL+ Tarief berekenen Bereken tarief in MyDHL+ In MyDHL+ kunt u met Bereken tarief heel eenvoudig en snel opvragen welke producten er mogelijk zijn voor een bestemming. Ook ziet u hierbij het geschatte

Nadere informatie

Shipment Centre EU Quick Print Client handleiding [NL]

Shipment Centre EU Quick Print Client handleiding [NL] Shipment Centre EU Quick Print Client handleiding [NL] Please scroll down for English. Met de Quick Print Client kunt u printers in Shipment Centre EU configureren. De Quick Print Client kan alleen op

Nadere informatie

Tentamen Objectgeorienteerd Programmeren

Tentamen Objectgeorienteerd Programmeren Tentamen Objectgeorienteerd Programmeren 5082IMOP6Y maandag 16 november 2015 13:00 15:00 Schrijf je naam en studentnummer op de regel hieronder. Sla deze pagina niet om tot de surveillant vertelt dat het

Nadere informatie

ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM Read Online and Download Ebook ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM DOWNLOAD EBOOK : ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK STAFLEU

Nadere informatie

Het handboek van KDE Screen Ruler. Lauri Watts Vertaling van het handboek: Niels Reedijk Vertaler/Nalezer: Alexander S. Koning

Het handboek van KDE Screen Ruler. Lauri Watts Vertaling van het handboek: Niels Reedijk Vertaler/Nalezer: Alexander S. Koning Lauri Watts Vertaling van het handboek: Niels Reedijk Vertaler/Nalezer: Alexander S. Koning 2 Inhoudsopgave 1 Inleiding 5 2 Menubeschrijvingen 6 3 Dankbetuigingen en licentie 8 Samenvatting KDE Screen

Nadere informatie

[BP-ebMS-H-000] Welke versie van Hermes moet er gebruikt worden?

[BP-ebMS-H-000] Welke versie van Hermes moet er gebruikt worden? [BP-ebMS-H-000] Welke versie van Hermes moet er gebruikt worden? Gebruik altijd de laatste versie omdat er serieuse bug-fixes in kunnen zitten. Check altijd de release notes en openstaande bugs. Er is

Nadere informatie

Functioneel Ontwerp / Wireframes:

Functioneel Ontwerp / Wireframes: Functioneel Ontwerp / Wireframes: Het functioneel ontwerp van de ilands applicatie voor op de iphone is gebaseerd op het iphone Human Interface Guidelines handboek geschreven door Apple Inc 2007. Rounded-Rectangle

Nadere informatie

How to install and use dictionaries on the ICARUS Illumina HD (E652BK)

How to install and use dictionaries on the ICARUS Illumina HD (E652BK) (for Dutch go to page 4) How to install and use dictionaries on the ICARUS Illumina HD (E652BK) The Illumina HD offers dictionary support for StarDict dictionaries.this is a (free) open source dictionary

Nadere informatie

Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14

Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14 QUICK GUIDE C Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14 Version 0.9 (June 2014) Per May 2014 OB10 has changed its name to Tungsten Network

Nadere informatie

The upside down Louisa tutorial by Dorothée: Noortjeprullemie.blogspot.be Written for Compagnie M.: m.com

The upside down Louisa tutorial by Dorothée: Noortjeprullemie.blogspot.be Written for Compagnie M.:  m.com The upside down Louisa tutorial by Dorothée: Noortjeprullemie.blogspot.be Written for Compagnie M.: www.compagnie- m.com Dorothée heeft een unieke Compagnie M. hack gemaakt: de Louisa op zijn kop. Als

Nadere informatie

IMP Uitwerking week 13

IMP Uitwerking week 13 IMP Uitwerking week 13 Opgave 1 Nee. Anders moet bijvoorbeeld een venster applicatie een subklasse zijn van zowel Frame en WindowListener. Als de applicatie ook een button of een menu heeft, dan moet het

Nadere informatie

The genesis of the game is unclear. Possibly, dominoes originates from China and the stones were brought here by Marco Polo, but this is uncertain.

The genesis of the game is unclear. Possibly, dominoes originates from China and the stones were brought here by Marco Polo, but this is uncertain. Domino tiles Dominoes is a game played with rectangular domino 'tiles'. Today the tiles are often made of plastic or wood, but in the past, they were made of real stone or ivory. They have a rectangle

Nadere informatie

!!!! Wild!Peacock!Omslagdoek!! Vertaling!door!Eerlijke!Wol.!! Het!garen!voor!dit!patroon!is!te!verkrijgen!op! Benodigdheden:!!

!!!! Wild!Peacock!Omslagdoek!! Vertaling!door!Eerlijke!Wol.!! Het!garen!voor!dit!patroon!is!te!verkrijgen!op!  Benodigdheden:!! WildPeacockOmslagdoek VertalingdoorEerlijkeWol. Hetgarenvoorditpatroonisteverkrijgenopwww.eerlijkewol.nl Benodigdheden: 4strengenWildPeacockRecycledSilkYarn rondbreinaaldnr8(jekuntnatuurlijkookgewonebreinaaldengebruiken,maar

Nadere informatie

LDAP Server on Yeastar MyPBX & tiptel 31xx/32xx series

LDAP Server on Yeastar MyPBX & tiptel 31xx/32xx series LDAP Server on Yeastar MyPBX & tiptel 31xx/32xx series Tiptel b.v. Camerastraat 2 1322 BC Almere tel.: +31-36-5366650 fax.: +31-36-5367881 info@tiptel.nl Versie 1.2.0 (09022016) Nederlands: De LDAP server

Nadere informatie

Handleiding Zuludesk Parent

Handleiding Zuludesk Parent Handleiding Zuludesk Parent Handleiding Zuludesk Parent Met Zuludesk Parent kunt u buiten schooltijden de ipad van uw kind beheren. Hieronder vind u een korte handleiding met de mogelijkheden. Gebruik

Nadere informatie

public Paneel() { knop = new JButton( Klik ); knop.addactionlistener( new KnopHandler() ); tekstvak = new JTextField(10); add(knop); add(tekstvak);

public Paneel() { knop = new JButton( Klik ); knop.addactionlistener( new KnopHandler() ); tekstvak = new JTextField(10); add(knop); add(tekstvak); Vaknaam: Programmeren I (Java) - Tentamen Module: 2 Datum/Tijd: 17 mrt 2015 / 18.30 20:30 Richting: ICT Code: IC011 Docent: E. Lieuw Boeken en aantekeningen NIET toegestaan. Kladpapier is wel toegestaan.

Nadere informatie

ALGORITMIEK: answers exercise class 7

ALGORITMIEK: answers exercise class 7 Problem 1. See slides 2 4 of lecture 8. Problem 2. See slides 4 6 of lecture 8. ALGORITMIEK: answers exercise class 7 Problem 5. a. Als we twee negatieve (< 0) getallen bij elkaar optellen is het antwoord

Nadere informatie

/ /

/   / Cookie statement / www.temagroningen.nl / board@temagroningen.nl / www.temagroningen.nl / board@temagroningen.nl Dutch hospitality is a cookie with your coffee or tea. Digital hospitality is a cookie for

Nadere informatie

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE. Toets Inleiding Kansrekening 1 8 februari 2010

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE. Toets Inleiding Kansrekening 1 8 februari 2010 FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE Toets Inleiding Kansrekening 1 8 februari 2010 Voeg aan het antwoord van een opgave altijd het bewijs, de berekening of de argumentatie toe. Als je een onderdeel

Nadere informatie

Game of Life in Java

Game of Life in Java Game of Life in Java Auteur: drs. M.S.L.F. Manssen http://www.manssen.eu Game of Life is een spel van een rooster van cellen, waarbij telkens een ronde gebeurt waarbij de nieuwe waardes van cellen gebeurt.

Nadere informatie

MyDHL+ Uw accountnummer(s) delen

MyDHL+ Uw accountnummer(s) delen MyDHL+ Uw accountnummer(s) delen met anderen Uw accountnummer(s) delen met anderen in MyDHL+ In MyDHL+ is het mogelijk om uw accountnummer(s) te delen met anderen om op uw accountnummer een zending te

Nadere informatie

After that, the digits are written after each other: first the row numbers, followed by the column numbers.

After that, the digits are written after each other: first the row numbers, followed by the column numbers. Bifid cipher The bifid cipher is one of the classical cipher techniques that can also easily be executed by hand. The technique was invented around 1901 by amateur cryptographer Felix Delastelle. The cipher

Nadere informatie

Intermax backup exclusion files

Intermax backup exclusion files Intermax backup exclusion files Document type: Referentienummer: Versienummer : Documentatie 1.0 Datum publicatie: Datum laatste wijziging: Auteur: 24-2-2011 24-2-2011 Anton van der Linden Onderwerp: Documentclassificatie:

Nadere informatie

Handleiding JCreator. Inhoud. Een Workspace en een eerste project maken

Handleiding JCreator. Inhoud. Een Workspace en een eerste project maken Handleiding JCreator Inhoud Een Workspace en een eerste project maken Een tweede project maken De editor van JCreator Aanpassen van de basis-directory Documentatie over klassen en methoden van de JDK Bestand

Nadere informatie

Luister alsjeblieft naar een opname als je de vragen beantwoordt of speel de stukken zelf!

Luister alsjeblieft naar een opname als je de vragen beantwoordt of speel de stukken zelf! Martijn Hooning COLLEGE ANALYSE OPDRACHT 1 9 september 2009 Hierbij een paar vragen over twee stukken die we deze week en vorige week hebben besproken: Mondnacht van Schumann, en het eerste deel van het

Nadere informatie

EM7580 Firmware Update by Micro SD card

EM7580 Firmware Update by Micro SD card EM7580 Firmware Update by Micro SD card 2 NEDERLANDS/ENGLISH EM7580 Firmware update by Micro SD card Table of contents 1.0 (NL) Introductie... 3 2.0 (NL) Firmware installeren... 3 3.0 (NL) Opmerking...

Nadere informatie

Lessen Java: Reeks 3. David Blinder Jan G. Cornelis

Lessen Java: Reeks 3. David Blinder Jan G. Cornelis Lessen Java: Reeks 3 David Blinder Jan G. Cornelis Vraag 0: Gebruik van de Debugger Syntax errors: fouten door verkeerd gebruik van Javacommandos code zal niet compileren. Locatie van de fout is bijna

Nadere informatie

Engels op Niveau A2 Workshops Woordkennis 1

Engels op Niveau A2 Workshops Woordkennis 1 A2 Workshops Woordkennis 1 A2 Workshops Woordkennis 1 A2 Woordkennis 1 Bestuderen Hoe leer je 2000 woorden? Als je een nieuwe taal wilt spreken en schrijven, heb je vooral veel nieuwe woorden nodig. Je

Nadere informatie

Zo vind u hierin de volgende documentatie over de klasse Applet: +----java.awt.panel. +----java.applet.applet

Zo vind u hierin de volgende documentatie over de klasse Applet: +----java.awt.panel. +----java.applet.applet VII. De Java-klassen A. Package Het mooie aan Java is de programmeur een hele serie van klassen en afgeleide klassen geleverd worden, die de programmeur naar behoefte kan gebruiken. De basisklasse van

Nadere informatie

ICARUS Illumina E653BK on Windows 8 (upgraded) how to install USB drivers

ICARUS Illumina E653BK on Windows 8 (upgraded) how to install USB drivers ICARUS Illumina E653BK on Windows 8 (upgraded) how to install USB drivers English Instructions Windows 8 out-of-the-box supports the ICARUS Illumina (E653) e-reader. However, when users upgrade their Windows

Nadere informatie

EM7680 Firmware Auto-Update for Kodi 17.2

EM7680 Firmware Auto-Update for Kodi 17.2 EM7680 Firmware Auto-Update for Kodi 17.2 2 NEDERLANDS/ENGLISH EM7680 Firmware Auto-update for Kodi 17.2 Table of contents 1.0 (NL) Introductie... 3 2.0 (NL) Firmware installeren... 3 3.0 (NL) Opmerking...

Nadere informatie

9 daagse Mindful-leSs 3 stappen plan training

9 daagse Mindful-leSs 3 stappen plan training 9 daagse Mindful-leSs 3 stappen plan training In 9 dagen jezelf volledig op de kaart zetten Je energie aangevuld en in staat om die batterij op peil te houden. Aan het eind heb jij Een goed gevoel in je

Nadere informatie

Veel gestelde vragen nieuwe webloginpagina

Veel gestelde vragen nieuwe webloginpagina Veel gestelde vragen nieuwe webloginpagina Op deze pagina treft u een aantal veel gestelde vragen aan over het opstarten van de nieuwe webloginpagina http://weblogin.tudelft.nl: 1. Ik krijg de melding

Nadere informatie

L.Net s88sd16-n aansluitingen en programmering.

L.Net s88sd16-n aansluitingen en programmering. De L.Net s88sd16-n wordt via één van de L.Net aansluitingen aangesloten op de LocoNet aansluiting van de centrale, bij een Intellibox of Twin-Center is dat de LocoNet-T aansluiting. L.Net s88sd16-n aansluitingen

Nadere informatie

OPEN TRAINING. Onderhandelingen met leveranciers voor aankopers. Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt.

OPEN TRAINING. Onderhandelingen met leveranciers voor aankopers. Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt. OPEN TRAINING Onderhandelingen met leveranciers voor aankopers Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt. Philip Meyers Making sure to come well prepared at the negotiation

Nadere informatie

eerste voorbeelden in Java

eerste voorbeelden in Java Beginselen van programmeren 2 eerste voorbeelden in Java vereisten: een editor: om programma in te tikken en te bewaren een Java compiler: zet ingetikte (bron-) programma om naar byte-code een Java Virtuele

Nadere informatie

Imperatief Programmeren, derde deeltentamen (INFOIMP) 4 november 2005

Imperatief Programmeren, derde deeltentamen (INFOIMP) 4 november 2005 Departement Informatica en Informatiekunde, Faculteit Bètawetenschappen, UU. In elektronische vorm beschikbaar gemaakt door de TBC van A Eskwadraat. Het college INFOIMP werd in 2005/2006 gegeven door Jeroen

Nadere informatie

Classification of triangles

Classification of triangles Classification of triangles A triangle is a geometrical shape that is formed when 3 non-collinear points are joined. The joining line segments are the sides of the triangle. The angles in between the sides

Nadere informatie

The first line of the input contains an integer $t \in \mathbb{n}$. This is followed by $t$ lines of text. This text consists of:

The first line of the input contains an integer $t \in \mathbb{n}$. This is followed by $t$ lines of text. This text consists of: Document properties Most word processors show some properties of the text in a document, such as the number of words or the number of letters in that document. Write a program that can determine some of

Nadere informatie

Yes/No (if not you pay an additional EUR 75 fee to be a member in 2020

Yes/No (if not you pay an additional EUR 75 fee to be a member in 2020 Meedoen aan dit evenement? Meld je eenvoudig aan Ben je lid? Ja/Nee Do you want to participate? Please apply Are you a LRCH member? Yes/No (if not you pay an additional EUR 75 fee to be a member in 2020

Nadere informatie

Illustrator Tutorial - How to Create a Watch

Illustrator Tutorial - How to Create a Watch Illustrator Tutorial - How to Create a Watch «Andrew Bannecker - Simple, True and Tender Vector Movie Posters by GABZ» Categories: Tutorials Have you ever seen print advertising of some watch brand before?

Nadere informatie

EM7680 Firmware Update by Micro SD card

EM7680 Firmware Update by Micro SD card EM7680 Firmware Update by Micro SD card 2 NEDERLANDS/ENGLISH EM7680 Firmware update by Micro SD card Table of contents 1.0 (NL) Introductie... 2 2.0 (NL) Firmware installeren... 2 3.0 (NL) Opmerking...

Nadere informatie

Tentamen Inleiding Programmeren (IN1608WI), 2 februari 2012, 9.00-11.00, Technische Universiteit Delft, Faculteit EWI, Afdeling 2.

Tentamen Inleiding Programmeren (IN1608WI), 2 februari 2012, 9.00-11.00, Technische Universiteit Delft, Faculteit EWI, Afdeling 2. Tentamen Inleiding Programmeren (IN1608WI), 2 februari 2012, 9.00-11.00, Technische Universiteit Delft, Faculteit EWI, Afdeling 2. Gesloten boek tentamen, bij dit tentamen mag je geen gebmik maken van

Nadere informatie

Gebruik van het LOGO in geautomatiseerde verkiezingen

Gebruik van het LOGO in geautomatiseerde verkiezingen BIJLAGE 1 S.A. STERIA Benelux N.V. Gebruik van het LOGO in geautomatiseerde verkiezingen Technische bepalingen voor de weergave van het logo op de schermen. Versie 1.2 Guy JASPERS Revisions Revision Description

Nadere informatie

EM7680 Firmware Update by Micro SD card or USB

EM7680 Firmware Update by Micro SD card or USB EM7680 Firmware Update by Micro SD card or USB 2 NEDERLANDS/ENGLISH EM7680 Firmware update by Micro SD card or USB Table of contents 1.0 (NL) Introductie... 3 2.0 (NL) Firmware installeren... 3 3.0 (NL)

Nadere informatie

Preschool Kindergarten

Preschool Kindergarten Preschool Kindergarten Objectives Students will recognize the values of numerals 1 to 10. Students will use objects to solve addition problems with sums from 1 to 10. Materials Needed Large number cards

Nadere informatie

Zelftest Programmeren in Java

Zelftest Programmeren in Java Zelftest Programmeren in Java Document: n0883test.fm 22/01/2013 ABIS Training & Consulting P.O. Box 220 B-3000 Leuven Belgium TRAINING & CONSULTING INLEIDING BIJ DE ZELFTEST PROGRAMMEREN IN JAVA Deze test

Nadere informatie

(1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs. (2) Ons gezelschap is er om kunsteducatie te verbeteren

(1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs. (2) Ons gezelschap is er om kunsteducatie te verbeteren (1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs (2) Ons gezelschap is er om kunsteducatie te verbeteren (3) Ons gezelschap helpt gemeenschappen te vormen en te binden (4) De producties

Nadere informatie

Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO

Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO Handleiding/Manual Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO Inhoudsopgave / Table of Contents 1 Verbinden met het gebruik van

Nadere informatie

Y.S. Lubbers en W. Witvoet

Y.S. Lubbers en W. Witvoet WEBDESIGN Eigen Site Evaluatie door: Y.S. Lubbers en W. Witvoet 1 Summary Summary Prefix 1. Content en structuur gescheiden houden 2. Grammaticaal correcte en beschrijvende markup 3. Kopregels 4. Client-

Nadere informatie

Introduction to Compgenomics Part II. Lee Katz January 13, 2010

Introduction to Compgenomics Part II. Lee Katz January 13, 2010 Introduction to Compgenomics Part II Lee Katz January 13, 2010 All students and groups should be on the Wiki Wiki needs to be closed and secured by Friday How are we doing 2 Introduction to using the server

Nadere informatie

Calculator spelling. Assignment

Calculator spelling. Assignment Calculator spelling A 7-segmentdisplay is used to represent digits (and sometimes also letters). If a screen is held upside down by coincide, the digits may look like letters from the alphabet. This finding

Nadere informatie

L.Net s88sd16-n aansluitingen en programmering.

L.Net s88sd16-n aansluitingen en programmering. De L.Net s88sd16-n wordt via één van de L.Net aansluitingen aangesloten op de LocoNet aansluiting van de centrale, bij een Intellibox of Twin-Center is dat de LocoNet-T aansluiting. L.Net s88sd16-n aansluitingen

Nadere informatie

Tentamen Objectgeorienteerd Programmeren IN1205 Voorbeeld

Tentamen Objectgeorienteerd Programmeren IN1205 Voorbeeld Tentamen Objectgeorienteerd Programmeren IN1205 Voorbeeld Afdeling ST Faculteit EWI TU Delft Bij dit tentamen mag u gebruik maken van: Barnes, Object-Oriented Programming with Java en de Notitie Algoritmiek

Nadere informatie

EM7680 Firmware Update by Micro SD card or USB stick

EM7680 Firmware Update by Micro SD card or USB stick EM7680 Firmware Update by Micro SD card or USB stick 2 NEDERLANDS/ENGLISH EM7680 Firmware update by Micro SD card or USB stick Table of contents 1.0 (NL) Introductie... 3 2.0 (NL) Firmware installeren...

Nadere informatie

Quality requirements concerning the packaging of oak lumber of Houthandel Wijers vof (09.09.14)

Quality requirements concerning the packaging of oak lumber of Houthandel Wijers vof (09.09.14) Quality requirements concerning the packaging of oak lumber of (09.09.14) Content: 1. Requirements on sticks 2. Requirements on placing sticks 3. Requirements on construction pallets 4. Stick length and

Nadere informatie

Systeem Wand Samenstellings Applicatie. Cabinet configuration tool. Nederlandse handleiding

Systeem Wand Samenstellings Applicatie. Cabinet configuration tool. Nederlandse handleiding Systeem Wand Samenstellings Applicatie Cabinet configuration tool Nederlandse handleiding 1 Handleiding bylsma wand configuratie tool... 2 1.1 Disclaimer... 2 2 Wand samenstellen... 2 2.1 Applicatie lay-out...

Nadere informatie

My Inspiration I got my inspiration from a lamp that I already had made 2 years ago. The lamp is the you can see on the right.

My Inspiration I got my inspiration from a lamp that I already had made 2 years ago. The lamp is the you can see on the right. Mijn Inspiratie Ik kreeg het idee om een variant te maken van een lamp die ik al eerder had gemaakt. Bij de lamp die in de onderstaande foto s is afgebeeld kun je het licht dimmen door de lamellen open

Nadere informatie

FAAC DRIVER. Driver install procedure for FAAC boards. Installatieprocedure voor driver voor FAAC-kaarten.

FAAC DRIVER. Driver install procedure for FAAC boards. Installatieprocedure voor driver voor FAAC-kaarten. FAAC DRIVER Driver install procedure for FAAC boards Installatieprocedure voor driver voor FAAC-kaarten www.record-toegangstechniek.nl 1 When a FAAC board (E124 or E145) is connected to the USB port, it

Nadere informatie

Blackboard Toetsvragen maken in Word

Blackboard Toetsvragen maken in Word Blackboard Toetsvragen maken in Word Inleiding We gaan vragen maken in een Word en deze vragen via kopiëren en plakken vertalen naar een tekstbestand (.txt) wat Blackboard begrijpt. Opmerking: Dit is iets

Nadere informatie

Group work to study a new subject.

Group work to study a new subject. CONTEXT SUBJECT AGE LEVEL AND COUNTRY FEATURE OF GROUP STUDENTS NUMBER MATERIALS AND TOOLS KIND OF GAME DURATION Order of operations 12 13 years 1 ste year of secundary school (technical class) Belgium

Nadere informatie

Find Neighbor Polygons in a Layer

Find Neighbor Polygons in a Layer Find Neighbor Polygons in a Layer QGIS Tutorials and Tips Author Ujaval Gandhi http://google.com/+ujavalgandhi Translations by Dick Groskamp This work is licensed under a Creative Commons Attribution 4.0

Nadere informatie

Handleiding Installatie ADS

Handleiding Installatie ADS Handleiding Installatie ADS Versie: 1.0 Versiedatum: 19-03-2014 Inleiding Deze handleiding helpt u met de installatie van Advantage Database Server. Zorg ervoor dat u bij de aanvang van de installatie

Nadere informatie

This appendix lists all the messages that the DRS may send to a registrant's administrative contact.

This appendix lists all the messages that the DRS may send to a registrant's administrative contact. This appendix lists all the messages that the DRS may send to a registrant's administrative contact. Subject: 1010 De houdernaam voor #domeinnaam# is veranderd / Registrant of #domeinnaam# has been changed

Nadere informatie

BathySurvey. A Trimble Access hydrographic survey module

BathySurvey. A Trimble Access hydrographic survey module BathySurvey A Trimble Access hydrographic survey module Contents 1. Introduction... 3 2. Installation... 4 3. Main Screen... 5 4. Device... 6 5. Jobs... 7 6. Settings Odom Echotrac... 8 7. Settings Ohmex

Nadere informatie

Contents. Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation

Contents. Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation TeleBank Contents Introduction Problem Definition The Application Co-operation operation and User friendliness Design Implementation Introduction - TeleBank Automatic bank services Initiates a Dialog with

Nadere informatie

Deel 1: schriftelijk deel

Deel 1: schriftelijk deel Examen Computerarchitectuur en systeemsoftware Donderdag 15 januari 2009, namiddag Deel 1: schriftelijk deel Algemene bemerkingen: Het examen bestaat uit 2 delen. Dit zijn de vragen voor het eerste deel.

Nadere informatie

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE Tentamen Bewijzen en Technieken 1 7 januari 211, duur 3 uur. Voeg aan het antwoord van een opgave altijd het bewijs, de berekening of de argumentatie toe.

Nadere informatie

Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2

Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2 167 Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2 Task clarity 1. I understand exactly what the task is 2. I understand exactly what is required of

Nadere informatie

Country recognition. Assignment

Country recognition. Assignment Country recognition You are given a text file containing a list of countries, together with a description of their borders. Each line of the file contains the name of a country, followed by a tab and a

Nadere informatie

Stap 1: Registreer via de link op de G-schijf beschikbaar na inloggen met de teken-account, verzend via Submit. Nadien krijg je een bevestiging op

Stap 1: Registreer via de link op de G-schijf beschikbaar na inloggen met de teken-account, verzend via Submit. Nadien krijg je een bevestiging op Stap 1: Registreer via de link op de G-schijf beschikbaar na inloggen met de teken-account, verzend via Submit. Nadien krijg je een bevestiging op het scherm met de melding dat de registratie compleet

Nadere informatie

Release notes Q3-2018

Release notes Q3-2018 Release notes Q3-2 018 Release notes Q3-2018 release This release is a smaller release due to the GDPR release that was in between Q1-2018 and Q3-2018 release. Please note that if you want to make use

Nadere informatie

X. Grafische elementen

X. Grafische elementen X. Grafische elementen Om u te helpen bij grafische voorstellingen heeft java een aantal grafische afbeeldingen die u kunt gebruiken. Meestal worden zij in de methode paint(graphics g) geplaatst. Zij moeten

Nadere informatie

RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM Read Online and Download Ebook RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM DOWNLOAD EBOOK : RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN STAFLEU

Nadere informatie

Installatie instructies

Installatie instructies OpenIMS CE Versie 4.2 Installatie instructies OpenSesame ICT BV Inhoudsopgave 1 INLEIDING... 3 2 INSTALLATIE INSTRUCTIES... 4 3 OPENIMS SITECOLLECTIE CONFIGURATIE... 6 OpenIMS CE Installatie instructies

Nadere informatie

Activant Prophet 21. Prophet 21 Version 12.0 Upgrade Information

Activant Prophet 21. Prophet 21 Version 12.0 Upgrade Information Activant Prophet 21 Prophet 21 Version 12.0 Upgrade Information This class is designed for Customers interested in upgrading to version 12.0 IT staff responsible for the managing of the Prophet 21 system

Nadere informatie

De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition)

De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition) De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition) L. A. te Winkel Click here if your download doesn"t start automatically

Nadere informatie

NSPYRE LEGO MINDSTORMS UITDAGING (JAVA) INLEIDING. DOEL: SIMULATOR:

NSPYRE LEGO MINDSTORMS UITDAGING (JAVA) INLEIDING. DOEL: SIMULATOR: NSPYRE LEGO MINDSTORMS UITDAGING (JAVA) INLEIDING. Door mee te doen aan deze uitdaging kan je Nspyre laten zien wat je kan. Schrijf jij de beste oplossing dan is deze lego mindstorms nxt 2.0 set voor jou.

Nadere informatie