-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSwing.html
More file actions
613 lines (601 loc) · 34.9 KB
/
Swing.html
File metadata and controls
613 lines (601 loc) · 34.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
<!DOCTYPE html><html lang="de"><head>
<title>Variationen zum Thema: Java</title>
<meta name="title" content="Variationen zum Thema: Java">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="UTF-8">
<meta name="description" content="Introduction to Java Programming">
<meta name="keywords" content="java,introduction">
<meta name="author" content="Ralph P. Lano">
<meta name="robots" content="index,follow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="book.css">
</head>
<body><center>
<div id="wrap">
<ul class="sidenav">
<p><a href="index.html">Variationen zum Thema</a><a href="index.html">Java</a></p>
<li><a href="Karel.html">Karel</a></li>
<li><a href="Graphics.html">Graphics</a></li>
<li><a href="Console.html">Console</a></li>
<li><a href="Agrar.html">Agrar</a></li>
<li><a href="MindReader.html">MindReader</a></li>
<li><a href="Swing.html">Swing</a></li>
<li><a href="Asteroids.html">Asteroids</a></li>
<li><a href="Stocks.html">Stocks</a></li>
</ul>
<div class="content"><p>
<img alt="" src="images/a6a5703b-cf7a-4778-aaa4-c635989a9ec5.png" style="display: block; margin-left: auto; margin-right: auto; width: 225px; height: 204px;" /></p>
<h1>
Swing</h1>
<p>
Programme mit grafischer Benutzeroberfläche (GUI) sind das Thema in diesem Kapitel. Ähnlich wie bei Grafikprogrammen gibt es vorgefertigte Komponenten mit denen wir dann die verschiedensten kleinen und großen Programme bauen. Das ist ein bischen wie Lego. Zusätzlich werden wir hier noch ein wenig mehr über Instanzvariablen erfahren.</p>
<p>
.</p>
<h2>
<img alt="" src="images/377f7211-da52-4eb6-8c44-0ac3a14e6b81.png" style="width: 292px; height: 261px; float: right;" />Grafische Benutzeroberfläche</h2>
<p>
Die meisten Programme mit denen wir täglich zu tun haben, sind Programme mit einer grafischen Benutzeroberfläche, oder englisch <em>graphical user interface</em> (GUI oder UI). GUIs bestehen aus grafischen Elementen, die wir auch Widgets oder Interaktoren nennen, weil wir mit ihnen interagieren können. Bekannte Beispiele sind:</p>
<ul>
<li>
Labels</li>
<li>
Buttons</li>
<li>
Textfields</li>
<li>
Checkboxes</li>
<li>
Radiobuttons</li>
<li>
Comboboxes</li>
</ul>
<p>
Im Folgenden werden wir einen nach dem anderen ausprobieren.</p>
<p>
.</p>
<h2>
<img alt="" src="images/DateAndTime.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JLabel</h2>
<p>
Wir beginnen wieder ganz einfach, mit dem JLabel. Es gibt praktisch keinen Unterschied zum GLabel. Er wird verwendet um Text in GUI Programmen darzustellen:</p>
<pre>
public class DateAndTime extends <span style="color:#0000ff;">Program</span> {
public void <span style="color:#0000ff;">init</span>() {
Date dt = new Date();
JLabel fritz = new JLabel(dt.toLocaleString());
fritz.setFont( new Font("SansSerif", Font.PLAIN, 20) );
add( fritz, NORTH );
}
}</pre>
<p>
Zu unseren bisherigen Programmen sehen wir zwei Unterschiede: wir schreiben jetzt "<em>extends Program</em>" anstelle von GraphicsProgram oder ConsoleProgram. Und wir verwenden die <em>init()</em> Methode anstatt der <em>run()</em> Methode. Das ist zwar nicht zwingend notwendig, aber guter Stil. Desweiteren sehen wir zum ersten Mal die Klasse <em>Date</em>. Die ist ganz praktisch wenn man Datum oder Uhrzeit benötigt. Und wir sehen die Klasse <em>Font</em>, mit der man den Font eines JLabels ändern kann. </p>
<p>
.</p>
<h2>
<img alt="" src="images/FirstButton.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JButton</h2>
<p>
Der JLabel ist nicht besonders interaktiv, und verdient den Namen eigentlich gar nicht. Anders ist das allerdings beim <em>JButton</em>, denn auf den kann man mit der Maus klicken. Wir fangen ganz einfach an:</p>
<pre>
public class FirstButton extends ConsoleProgram {
public void init() {
JButton btn = new JButton("OK");
add( btn, SOUTH );
}
}
</pre>
<p>
Wir legen einen neuen JButton an, auf dem "OK" stehen soll. Den fügen wir dann im Süden hinzu. Unser FirstButton Programm ist wieder ein ConsoleProgram, das ist aber o.k. Wenn wir das Programm starten, können wir zwar auf den Knopf drücken, es passiert aber nichts.</p>
<p>
Damit der Knopf richtig funktioniert, also interaktiv wird, müssen wir zwei Dinge tun:</p>
<ol>
<li>
wir müssen am Ende von init() die Method <em>addActionListeners()</em> aufrufen und</li>
<li>
wir müssen eine Methode namens <em>public void actionPerformed()</em> implementieren.</li>
</ol>
<p>
Der erste Schritt sagt dem Programm, dass wir informiert werden möchten wenn irgendein Knopf gedrückt wird. Und wie werden wir informiert? Man ruft uns an, allerdings nicht auf dem Telefon, sondern mit der Methode <em>actionPerformed()</em>. D.h., jedes Mal wenn jemand auf den Knopf drückt, dann wird diese Methode aufgerufen. Schauen wir uns das im Ganzen noch mal an:</p>
<pre>
public class FirstButton extends ConsoleProgram {
public void init() {
JButton btn = new JButton("OK");
add( btn, SOUTH );
<span style="color:#0000ff;">addActionListeners()</span>;
}
public void <span style="color:#0000ff;">actionPerformed</span>( ActionEvent e ) {
println("hi:" + e.getActionCommand());
}
}</pre>
<p>
Jetzt wird in dem Konsolenteil unseres Programms jedes Mal "hi:" mit dem Namen des Knopfes ausgegeben, wenn wir mit der Maus auf den Knopf drücken. </p>
<p>
.</p>
<h2>
<img alt="" src="images/3d9087be-7b2e-4d4e-af76-a1127aa0645b.png" style="margin-left: 10px; margin-right: 10px; width: 387px; height: 178px; float: right;" />Regionen</h2>
<p>
In den zwei Beispielen oben, haben wir bereits zwei Regionen kennengelernt, insgesamt gibt es fünf: EAST, WEST, SOUTH, NORTH, und CENTER. Wir können unsere Widgets in jede dieser Regionen einfügen. </p>
<p>
Eines was ein <em>ConsoleProgram</em> von einem <em>GraphicsProgram</em> und wiederum von einem <em>Program</em> unterscheidet, ist was in der CENTER Region ist. Beim <em>Program</em> ist da gar nichts. Beim <em>GraphicsProgram</em> ist da ein GCanvas eingefügt, und beim <em>ConsoleProgram</em> eine TextArea.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Login2.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JTextField</h2>
<p>
Mittels des JTextField Widgets können wir Text und auch Zahlen einlesen. Im folgenden Beispiel möchten wir, dass sich die Nutzer mit ihrem Namen anmelden.</p>
<pre>
public class Login extends ConsoleProgram {
<span style="color:#0000ff;">private JTextField tf;</span>
public void init() {
JLabel lbl = new JLabel("Name: ");
add(lbl, SOUTH);
<span style="color:#0000ff;">tf</span> = new JTextField(10);
add(<span style="color:#0000ff;">tf</span>, SOUTH);
JButton btn = new JButton("Login");
add( btn, SOUTH );
addActionListeners();
}
public void actionPerformed(ActionEvent e) {
println("Name: " + <span style="color:#0000ff;">tf</span>.getText());
}
}</pre>
<p>
Wir legen einen JLabel, ein JTextField und einen JButton an. Im Konstruktor des JTextField sagen wir wie breit das Feld sein soll. Wichtig ist noch, dass das JTextField <em>tf</em> eine Instanzvariable ist. Wäre es nämlich eine lokale Variable, dann könnten wir nicht in der <em>actionPerformed()</em> Methode darauf zugreifen.</p>
<p>
Ein kleine Verbesserung des Codes bringt die folgende Zeile</p>
<pre>
tf.addActionListener(this);</pre>
<p>
die wir direkt vor dem add(tf) einfügen können. Diese Zeile bewirkt nämlich, dass es auch genügt einfach die "Enter" Taste zu drücken um sich einzuloggen.</p>
<p>
.</p>
<h2>
Instanzvariablen</h2>
<p>
Wir haben zwar schon im letzten Kapitel kurz mal mit Instanzvariablen zu tun gehabt, aber gerade im obigen Beispiel haben wir gesehen wofür sie eigentlich gut sind: sie erlauben es zwischen verschiedenen Methoden Information auszutauschen. Bisher konnten wir dafür lediglich Parameter und Rückgabewerte verwenden.</p>
<p>
Ein anderer Grund warum Instanzvariablen ganz praktisch sein können, hat damit zu tun, dass alle lokalen Variablen wieder gelöscht werden, wenn eine Methode verlassen wird. Also z.B. in der Methode <em>rollTheDie()</em>,</p>
<pre>
private void rollTheDie() {
RandomGenerator rgen = new RandomGenerator();
int dieRoll = rgen.nextInt(1,6);
println(dieRoll);
}
</pre>
<p>
wird bei jedem Aufruf ein neue Instanz des RandomGenerator erzeugt und dann beim Verlassen wieder gelöscht. Beides braucht Zeit. Wenn wir aber den RandomGenerator als Instanzvariable anlegen,</p>
<pre>
public class InstanceVariables extends ConsoleProgram {
private RandomGenerator rgen = new RandomGenerator();
private void rollTheDie() {
int dieRoll = rgen.nextInt(1,6);
println(dieRoll);
}
}</pre>
<p>
dann müssen wir ihn nur einmal anlegen, und zusätzlich können wir ihn auch in anderen Methoden verwenden. Das ist viel resourcenschonender.</p>
<p>
.</p>
<h2>
Instanzvariablen vs. Lokale Variablen</h2>
<p>
Es gibt also zwei Arten von Variablem: Instanzvariablen und lokale Variablen. Für Instanzvariablen gilt, dass sie in der Klasse deklariert werden und nicht in einer Methode, dass sie in der gesamten Klasse sichtbar sind, also von allen Methoden einer Klasse auf sie zugegriffen werden kann und dass sie solange leben wie das Objekt existiert.</p>
<p>
Für lokale Variablen gilt umgekehrt, dass sie innerhalb einer Methode deklariert werden, dass nur innerhalb dieser Methode auf sie zugegriffen werden kann und dass sie beim Verlassen der Methode wieder gelöscht werden.</p>
<p>
Nun stellt sich die Frage, wann verwende ich welche? Die Antwort ist in den meisten Fällen relativ einfach:</p>
<ul>
<li>
wenn es sich um eine Berechnung handelt, die lokal in einer Methode ausgeführt werden kann, dann verwendet man lokale Variablen, z.B. die Umwandlung von Grad nach Fahrenheit ist eine lokale Berechnung</li>
<li>
wenn eine Information in mehreren Methoden verwendet wird, dann sollte man eine Instanzvariable nehmen</li>
<li>
wenn es sich um den internen Zustand eines Objektes handelt, dann sollte man eine Instanzvariable verwenden. Z.B. bei der Klasse Student waren der Name, die Id und die Credits interne Zustände.</li>
</ul>
<p>
<strong>SEP: Wenn möglich sollte man lokalen Variablen verwenden.</strong></p>
<p>
.</p>
<h2>
Übung: OKCancel</h2>
<p>
Als kleine Übung fügen wir noch einen <em>Cancel</em> Knopf zu unserem Programm hinzu. Wie können wir jetzt unterscheiden welcher der beiden Knöpfe denn gedrückt wurde? Hier ist manchmal sinnvoller die <em>getSource()</em> Methode des ActionEvents zu verwenden,</p>
<pre>
public void actionPerformed( ActionEvent e ) {
if( e.getSource() == btnOK ) {
...
}
}
</pre>
<p>
um zwischen mehreren Knöpfen oder Widgets im Allgemeinen zu unterscheiden.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Pizza.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JCheckBox</h2>
<p>
Als nächstes Widget widmen wir uns den Checkboxes: Diese verwenden wir wenn wir mehrere Auswahlmöglichkeiten haben.</p>
<pre>
public void init() {
JLabel lbl = new JLabel("Select your toppings:");
add(lbl, NORTH);
JCheckBox topping1 = new JCheckBox("Tomatoes");
add(topping1, CENTER);
JCheckBox topping2 = new JCheckBox("Bacon");
add(topping2, CENTER);
JCheckBox topping3 = new JCheckBox("Onions");
add(topping3, CENTER);
}</pre>
<p>
In dem Pizza Beispiel soll es möglich sein alle möglichen Kombinationen von Toppings auszuwählen. Das kann man am besten mit Checkboxes erreichen. Um festzustellen welche Toppings ausgewählt wurden, gibt es die Methode <em>isSelected()</em>:</p>
<pre>
boolean b = topping1.isSelected();</pre>
<p>
Das müssten wir natürlich für alle drei Toppings machen.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Exam.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JRadioButton</h2>
<p>
Radiobuttons werden verwendet wenn es darum geht Entscheidungen zu treffen. Betrachten wir das folgende Beispiel:</p>
<pre>
public void init() {
JLabel lbl = new JLabel("Correct: 1 + 1 = 2 ?");
add(lbl, NORTH);
JRadioButton yes = new JRadioButton("Yes");
yes.<span style="color:#0000ff;">setSelected</span>(true);
add(yes, SOUTH);
JRadioButton no = new JRadioButton("No");
add(no, SOUTH);
ButtonGroup happyGrp = new ButtonGroup();
happyGrp.add(yes);
happyGrp.add(no);
}</pre>
<p>
Wir haben hier zwei Radiobuttons, wobei der "yes" Button vorselektiert ist. Damit das Programm weiß welche Buttons zusammengehören, fügt man zusammengehörtige Buttons in eine ButtonGroup zusammen. Das führt dann dazu, dass immer nur einer der Buttons ausgewählt sein kann. Will man wissen welchen Button der Nutzer gewählt hat, kann man das mittels:</p>
<pre>
boolean b = yes.isSelected();</pre>
<p>
feststellen.</p>
<p>
.</p>
<h2>
<img alt="" src="images/FavoriteColor.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />JComboBox</h2>
<p>
Als Beispiel für die JComboBox schauen wir uns das Beispiel FavoriteColor an. Es geht darum eine Farbe aus einer Liste auszuwählen:</p>
<pre>
public class FavoriteColor extends ConsoleProgram {
private <span style="color:#0000ff;">JComboBox</span> colorPicker;
public void init() {
colorPicker = new JComboBox();
colorPicker.addItem("Red");
colorPicker.addItem("White");
colorPicker.addItem("Blue");
add(colorPicker, SOUTH);
addActionListeners();
}
public void actionPerformed(ActionEvent e) {
println("Color:" + <span style="color:#0000ff;">colorPicker.getSelectedItem()</span>);
}
}</pre>
<p>
In der init() Methode initialisieren wir den <em>colorPicker</em> mit den voreingestellten Farben. Um festzustellen welche Farbe der Benutzer ausgewählt hat, gibt es die <em>getSelectedItem()</em> Methode. Damit wir aber auf diese in der <em>actionPerformed()</em> Methode zugreifen können, muss <em>colorPicker</em> eine Instanzvariable sein.</p>
<p>
Anmerkung: wenn wir ganz genau das Verhalten der JComboBox beobachten, werden wir feststellen, dass sie ein paar klein Quirks hat. Kann man nix machen.</p>
<p>
.</p>
<h2>
Swing Interactor Hierarchie</h2>
<p>
Ähnlich wie es bei den ACM Grafikklassen eine Hierarchie gibt, gibt es die auch bei den Swing Interaktor Klassen. Die wichtigsten sind in dem nebenstehenden Diagramm zusammengefasst, es gibt aber noch weitaus mehr.</p>
<p>
<img alt="" src="http://127.0.0.1:8000../images/SwingClassDiagram.jpg" style="margin-left: 10px; margin-right: 10px; width: 673px; height: 405px;" /></p>
<p>
.</p>
<h2>
<img alt="" src="images/40c49c0e-a278-44cc-963a-fb7a816b117f.png" style="width: 246px; height: 251px; float: right;" />Layout</h2>
<p>
Wir haben bereits die Regionen EAST, WEST, SOUTH, NORTH, und CENTER kennengelernt. Diese sind eine Besonderheit des BorderLayouts. Es gibt neben dem BorderLayout noch einige andere. Es geht darum wie man mehrere Widgets auf dem Bildschirm "auslegt" (layout). Zu den wichtigeren Layouts zählen die Folgenden: </p>
<ul>
<li>
<strong>BorderLayout:</strong> hier gibt es fünf Regionen und Widgets müssen explizit einer Region zugewiesen werden</li>
<li>
<strong>FlowLayout:</strong> ist das einfachste Layout, Widgets werden einfach von links nach rechts nebeneinander ausgelegt</li>
<li>
<strong>GridLayout:</strong> der verfügbare Platz wird in gleich große Flächen aufgeteilt, z.B. 3 mal 2</li>
</ul>
<p>
Sehen wir uns ein paar Beispiele dazu an. Um den BorderLayout zu verwenden würden wir folgenden Code verwenden:</p>
<pre>
setLayout(new BorderLayout());
add(new JButton("EAST"), EAST);
add(new JButton("WEST"), WEST);
...
</pre>
<p>
Für den FlowLayout sieht das dann so aus:</p>
<pre>
setLayout(new FlowLayout());
add(new JButton("0"));
add(new JButton("1"));
...</pre>
<p>
und für den GridLayout so:</p>
<pre>
setLayout(new GridLayout(2, 3));
add(new JButton("0"));
add(new JButton("1"));
...</pre>
<p>
Grafisch sieht das Ganze dann so aus:</p>
<table align="center" border="0" cellpadding="10" cellspacing="10" style="width: 100%;">
<tbody>
<tr>
<td style="text-align: center; vertical-align: middle;">
<img alt="" src="images/BorderLayout.png" style="width: 200px; height: 100px;" /></td>
<td style="text-align: center; vertical-align: middle;">
<img alt="" src="images/FlowLayout.png" style="width: 200px; height: 100px;" /></td>
<td style="text-align: center; vertical-align: middle;">
<img alt="" src="images/GridLayout.png" style="width: 200px; height: 100px;" /></td>
</tr>
</tbody>
</table>
<p>
.</p>
<h2>
JPanel</h2>
<p>
Was die GCompound für GraphicsProgramme war ist das JPanel für Swing: es erlaubt es uns mehrere Widgets zu einem neuen Widget zusammenzufassen. Details zur JPanel Klasse sehen wir weiter unten im Projekt "Quiz".</p>
<p>
.</p>
<hr />
<h1>
Review</h1>
<p>
Glückwunsch! Wie wir gleich sehen werden, haben wir jetzt das Rüstzeug um fast beliebige grafische Benutzeroberflächen (UIs) zu basteln. Wir wissen jetzt wie man mit</p>
<ul>
<li>
Labels</li>
<li>
Buttons</li>
<li>
Textfields</li>
<li>
Checkboxes</li>
<li>
Radiobuttons</li>
<li>
und Comboboxes</li>
</ul>
<p>
arbeitet. Außerdem haben wir etwas über verschiedene Layouts erfahren, und kurz die Bekanntschaft des JPanel gemacht. </p>
<p>
Mindestens genauso wichtig war aber die Vertiefung zu Instanzvariablen. Es sollte jetzt etwas klarer sein, was diese von lokalen Variablen unterscheidet, und wann man welche der beiden verwendet.</p>
<p>
.</p>
<hr />
<h1>
Projekte</h1>
<p>
In den Projekten in diesem Kapitel werden wir viele UIs erstellen. Manche brauchen wir später noch, um sie mit Leben zu füllen. Es gibt wieder viel zu tun.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Clock.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />Clock</h2>
<p>
Wir wollen eine kleine Uhr schreiben. Dazu verwenden wir die <em>Date</em> Klasse und deren Methoden <em>getHours()</em>, <em>getMinutes()</em> und <em>getSeconds()</em>. Für die Anzeige selbst verwenden wir einen JLabel. Und natürlich sollte der Text des JLabels sich einmal pro Sekunde (besser zweimal) verändern. Dazu kann man die <em>setText()</em> Methode des JLabels verwenden. </p>
<p>
Unter Umständen macht es Sinn eine Methode <em>padWithZeros()</em> zu schreiben, die sicher stellt, dass anstelle von "6" Minuten "06" Minuten angezeigt werden.</p>
<p>
.</p>
<h2>
<img alt="" src="images/wordGuess.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 80px; float: right;" />WordGuess</h2>
<p>
WordGuess ist sozusagen eine grafische Version von Hangman aus dem letzten Kapitel. Es geht darum ein Wort zu erraten indem man Buchstaben eingibt.</p>
<p>
Man beginnt damit, dass man ein zufälliges Wort mit der Methode <em>pickRandomWord()</em> auswählt. Dieses Wort sollte man als Instanzvariable <em>wordToGuess</em> speichern. Dann sollte man daraus ein Wort mit lauter "-" Strichen machen, <em>wordShown</em>, auch eine Instanzvariable. Dann kreiirt man einen JLabel und fügt diesen im Norden dazu:</p>
<pre>
wordLbl = new JLabel(wordShown);
add(wordLbl, NORTH);
wordLbl.<span style="color:#0000ff;">addKeyListener(this)</span>;
wordLbl.requestFocus();</pre>
<p>
Interessant ist die Art und Weise wie wir hier den KeyListener hinzufügen: zunächst einmal ist er Teil des Labels, und zum zweiten übergeben wir "this" als Parameter. Momentan brauchen wir das noch nicht zu verstehen, aber effektiv führt es dazu, dass wir auf KeyEvents hören können. Die <em>requestFocus()</em> Methode ist nötig, damit der KeyListener auch funktioniert.</p>
<p>
Was bleibt ist die Methode <em>keyTyped(KeyEvent e)</em> zu implementieren. Diese wird aufgerufen wenn der Nutzer eine Taste drückt, was dann passieren soll ist ähnlich wie bei Hangman, wir prüfen ob der Buchstabe im <em>wordToGuess</em> vorhanden ist, und updaten den Label falls er ist. Natürlich macht es auch noch Sinn zu zählen wieviel Versuche benötigt wurden um das Wort zu erraten.</p>
<p>
.</p>
<h2>
<img alt="" src="images/StopWatch.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />StopWatch</h2>
<p>
Eine Stoppuhr muss eine höhere Genauigkeit haben als die Klasse <em>Date</em> liefert. Dafür gibt es die Systemmethode</p>
<pre>
long time = System.currentTimeMillis();</pre>
<p>
welche uns die Millisekunden liefert, die seit 0 Uhr des 1.1.1970 verstrichen sind. Wenn wir eine Zeit die in Millisekunden gegeben ist durch 1000 teilen erhalten wir die Sekunden, wenn wir Modulo 1000 nehmen erhalten wir die Millisekunden.</p>
<p>
Die Anzeige soll animiert sein, deswegen macht es Sinn das Ganze in einen GameLoop zu stecken. Ausserdem macht es Sinn einen Delay von 20ms zu haben, sonst kommt die Anzeige nicht nach mit dem Anzeigen. Und wir sollten zwei Knöpfe mit einbauen, einen zum Starten und einen zum Pausieren.</p>
<p>
Den primitiven Datentyp <em>long</em> haben wir bisher noch nicht gesehen. Er verhält sich wie ein <em>int</em>, ist also für Ganzzahlen gedacht. Der einzige Unterschied, während ints 32 Bit sind, sind die longs 64 Bit, d.h. sie eignen sich für größere Zahlen (32-bit Zahlen sind zwischen -2^31 und +2^31 und 64-bit Zahlen sind zwischen -2^63 und +2^63).</p>
<p>
.</p>
<h2>
<img alt="" src="images/CountDown.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />CountDown</h2>
<p>
Ganz ähnlich wie die Stoppuhr funktioniert der CountDown. Anstelle eines JLabels verwenden wir aber ein JTextField. Der Vorteil ist, dass man dieses editieren kann, d.h. man kann die Zahl einstellen von der man aus rückwärts zählen möchte. Sobald der Nutzer dann auf den "Start" Knopf drückt, soll der Countdown beginnen.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Ticker.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 51px;" />Ticker</h2>
<p>
Eine Laufschrift (Werbe-Ticker) ist relativ einfach zu implementieren. Wir verwenden dazu einen JLabel. In einem GameLoop bewegen wir in dann alle 50 Millisekunden um 5 Pixel nach links mittels</p>
<pre>
lbl.move(-5, 0);</pre>
<p>
.</p>
<h2>
<img alt="" src="images/AlarmClock.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />AlarmClock</h2>
<p>
Eine Alarmuhr zu schreiben, die mit einem Stunden:Minuten:Sekunden Format arbeitet ist überraschen kompliziert. Allerdings wenn man die Methoden für die Konvertierung vom Stunden:Minuten:Sekunden nach Sekunden (<em>convertTimeInSeconds()</em>) und von Sekunden nach Stunden:Minuten:Sekunden (<em>convertSecondsInTime()</em>) schon hat, dann ist es gar nicht so schwer, und eigentlich identisch mit dem CountDown Projekt.</p>
<p>
Für das Programm verwenden wir einen großen JLabel, den wir im Norden platzieren. Außerdem gibt es ein JTextField im Süden für die Eingabe der Alarmzeit im Stunden:Minuten:Sekunden Format. Und es gibt einen JButton über den der Alarm gestartet wird. </p>
<p>
Es macht Sinn zwei Instanzvariablen zu verwenden:</p>
<pre>
private long alarmTime = -1;
private boolean alarmStarted = false;</pre>
<p>
Die erste ist einfach die Zeit in Sekunden, und die zweite wird verwendet um dem GameLoop mitzuteilen, dass etwas zu tun ist:</p>
<pre>
while (true) {
if (alarmStarted) {
// display remaining time
...
}
pause(DELAY);
}</pre>
<p>
Wenn der JButton gedrückt wird, dann wir zum einen alarmTime auf die Sekunden gesetzt, und alarmStarted wird auf <em>true</em> gesetzt, damit der GameLoop weiß, dass er jetzt was anzeigen soll.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Editor.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 200px; float: right;" />Editor</h2>
<p>
Als nächstes steht ein Texteditor auf unserem Plan. Dieser besteht zum Einen aus einem JTextField für den Dateinamen und zwei JButtons, einer zum Laden, der andere zum Speichern von Dateien. Diese drei Widgets platzieren wir im Norden, also dem unteren Teil. </p>
<p>
In den Mittelteil, CENTER, kommt eine JTextArea:</p>
<pre>
display = new JTextArea();
display.setFont(new Font("Courier", Font.BOLD, 18));
add(display, CENTER);</pre>
<p>
Eine JTextArea ist wie ein JTextField, nur dass man da auch Mehrzeilentext eingeben kann.</p>
<p>
Wie das Laden und Speichern von Dateien geht, lernen wir im nächsten Kapitel.</p>
<p>
.</p>
<h2>
<img alt="" src="images/Quiz.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 133px; float: right;" />Quiz</h2>
<p>
Wir wollen eine UI für ein MultipleChoice Quiz schreiben. Diese besteht aus einer Frage, also einen JLabel, den wir im oberen Bereich (NORTH) platzieren. Dann folgen die möglichen Antworten. Dies sind natürlich Checkboxes. Da sie zusammen gehören, gruppieren wir sie in eine Buttongroup. Die Checkboxes kommen in den mittleren Bereich (CENTER). Schließlich, wollen wir auch noch zwei Navigations Knöpfe hinzufügen, im unteren Bereich. Das Programm soll keine weitere Funktion haben, die kommt im übernächsten Kapitel.</p>
<p>
Dieses Programm ist ein schönes Beispiel wie man JPanels einsetzen kann. Multiplechoice Fragen bestehen nicht immer aus drei Antworten. Manchmal sind es weniger, manchmal mehr. Also macht es Sinn, die Fragen zusammenzufassen und in ein JPanel einzufügen. </p>
<pre>
JPanel answersPnl = new JPanel();
answersPnl.setLayout(new GridLayout(3, 1));
JRadioButton btn1 = new JRadioButton(answer1);
answersPnl.add(btn1);
...
add(answersPnl, CENTER);</pre>
<p>
Dieses JPanel fügen wir dann in den mittleren Bereich.</p>
<p>
.</p>
<h2>
<img alt="" src="images/DrawingEditor2.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />DrawingEditor </h2>
<p>
Im übernächsten Kapitel wollen wir einen DrawingEditor schreiben. Bis dahin können wir schon einmal etwas Vorarbeit leisten. Die Idee ist, dass wir zwischen den Formen Rechteck und Kreis mittels zweier Radiobuttons auswählen können. Zusätzlich wollen wir einstellen können ob die Formen ausgefüllt sind oder nicht, das geht am besten mit einer Checkbox. Und schließlich, möchten wir noch die Farbe der Formen bestimmen können.</p>
<p>
.</p>
<hr />
<h1>
Challenges</h1>
<p>
.</p>
<h2>
<img alt="" src="images/Calculator.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 200px; float: right;" />Calculator</h2>
<p>
Unser nächstes Projekt ist ein kleiner Taschenrechner. Dieser besteht aus einem JTextField (<em>display</em>) und 16 JButtons. Am besten wir platzieren das JTextField im Norden, und die JButtons in ein 4x4 GridLayout.</p>
<p>
Bei der Programmierung der Logik des Rechners müssen wir etwas überlegen. Zunächst macht es Sinn zwei Instanzvariablen einzuführen:</p>
<pre>
private double operand1 = 0;
private char operation = '+';</pre>
<p>
Wenn wir z.B. "6 - 2" mit einem Taschenrechner ausrechnen, dann geben wir ja zuerst die Zahl "6" ein, dann das "-" und danach die "2". Wir müssen uns also zwischendurch sowohl die "6" als auch das "-" merken, deswegen die beiden Instanzvariablen. </p>
<p>
In der <em>actionPerformed()</em> Methode müssen wir also unterscheiden zwischen dem "=" Zeichen, den Operatoren "+", "-","*" und "/", und den Ziffern. </p>
<p>
Wenn Ziffern eingegeben werden, dann fügen wir die einfach zum display hinzu:</p>
<pre>
char cmd = e.getActionCommand().charAt(0);
display.setText(display.getText() + cmd);</pre>
<p>
Wenn ein Operator gedrückt wurde, dann müssen wir die Instanzvariablen <em>operand1</em> und <em>operation</em> setzen, also</p>
<pre>
operand1 = Double.parseDouble(display.getText());
display.setText("");
operation = cmd;</pre>
<p>
Und wenn das "=" Zeichen gedrückt wurde, dann müssen wir die Berechnung durchführen und anzeigen:</p>
<pre>
double operand2 = Double.parseDouble(display.getText());
double result = calculate(operand1, operand2, operation);
display.setText("" + result);</pre>
<p>
Es bleibt also lediglich die Methode <em>calculate(double operand1, double operand2, char operation)</em> zu implementieren.</p>
<p>
.</p>
<hr />
<h1>
Fragen</h1>
<ol>
<li>
Es gibt lokale Variablen, Instanzvariablen und Konstanten. Erklären Sie den Unterschied.<br />
</li>
<li>
Was muß man ändern, damit aus 'PI' eine Konstante wird?<br />
<span style="font-family:courier new,courier,monospace;">double PI = 3.14;</span><br />
</li>
<li>
Im folgenden Beispiel gibt es mehrere Variablen, teilweise mit gleichem Namen. Beschreiben Sie wie die Variablen zusammenhängen, und welche wo gültig sind.<br />
<br />
<span style="font-family:courier new,courier,monospace;"> public class Lifetime {<br />
<br />
public void run() {<br />
int i = 3;<br />
cow(i);<br />
}<br />
<br />
private void cow( int n ) {<br />
for (int i=0; i<3; i++) {<br />
...<br />
}<br />
}<br />
}</span><br />
</li>
<li>
Nennen Sie drei verschiedene LayoutManager.<br />
</li>
<li>
Skizzieren Sie wie die UI für den folgenden Code aussehen würde.<br />
</li>
<li>
Wenn Sie auf einen JButton klicken, welche Art von Ereignis (Event) wird dann ausgelöst?</li>
</ol>
<p>
.</p>
<hr />
<h1>
Referenzen</h1>
<p>
In diesem Kapitel ist die bevorzugte Referenz das Buch von Eric Robert [1]. Ein schönes, allerdings anspruchvolles Tutorial ist das von Oracle, den Machern von Java [2].</p>
<p>
[1] The Art and Science of Java, von Eric Roberts, Addison-Wesley, 2008</p>
<p>
[2] The Swing Tutorial, <a href="http://docs.oracle.com/javase/tutorial/uiswing/">docs.oracle.com/javase/tutorial/uiswing/</a></p>
<p>
.</p>
<p class="footer">
Copyright © 2016-2021 <a href="http://www.lano.de">Ralph P. Lano</a>. All rights reserved.
</p>
</div>
</center>
</div>
</body>
</html>