www.r-krell.de |
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr |
Willkommen/Übersicht > Informatik > Java-Seite b1) (2017)
Teil b1)_2017
Einführung in das Programmieren mit Swing-Oberflächen
Eine vollständige Übersicht aller meiner Seiten "Informatik mit Java" gibt's auf der Informatik-Hauptseite!
Auf dieser Seite b1)_2017 finden Sie:
zum Seitenanfang / zum Seitenende
Im Folgenden wird davon ausgegangen, dass Sie - wie auf Seite a) beschrieben -- eine aktuelle SDK und den Java-Editor installiert haben. Während auf meinen weiterhin verfügbaren "Informatik-mit-Java"-Seiten b) ff über einen längeren Zeitraum Berichte zusammengetragen wurden, die sich i.W. auf Informatik-Leistungskurse stützen, in denen zunächst mit dem Hamster und dann mit Stift&Co in das Programmieren eingeführt wurde, habe ich 2017 in einem Grundkurs einen anderen Weg beschritten: Algorithmik und Programmieren werden direkt durch Schreiben von Anwendungen mit moderner Oberfläche erlernt! Durchgeführt wird bzw. wurde der Unterricht in einem halbjährigen Grundkurs in der Einführungsphase der gymnasialen Oberstufe. Die heterogene Schülerschaft bringt ganz überwiegend keinerlei Vorkenntnisse über Programmieren mit; viele Schülerinnen und Schüler hatten früher nicht mal Informatik-Unterricht. Allerdings habe ich im ersten Halbjahr in diesem Kurs neben Grundzügen der Bild- und Textverarbeitung in den sinnvollen Gebrauch von Tabellenkalkulation und Datenbankprogrammen (inkl. Datenbankentwurf nach der 2. NF mit ER-Diagrammen) eingeführt. Das Programmieren in Java begann aber genau so wie hier dargestellt und ohne weitere Präliminarien.
Die Oberflächen der geschriebenen Programme werden mit dem GUI-Builder des Java-Editors per Maus erstellt; besondere Eigenschaften der Oberflächenelemente werden im Objektinspektor ergänzt bzw. angepasst. Der dafür vom GUI-Builder automatisch erzeugte Programmtext soll nicht verändert und braucht nicht verstanden zu werden. Eigener Programmtext für die Funktionalität wird zunächst nur in die ActionPerformed-Methoden der Schaltflächen geschrieben. Für jede Arbeit des Programms muss also ein Knopf in die Oberfläche gesetzt werden, der die gewünschte Funktion auslöst. Der Quelltext zur Funktionalität der Knöpfe soll sehr wohl verstanden und gezielt (weiter) entwickelt werden - damit und dafür wird das Programmieren erlernt! Im Folgenden ist dieser selbst geschriebene Quelltext farbig unterlegt.
Die Oberflächen werden in Swing geschrieben. Seit der Ende Februar 2017 erschienenen Version 14 unterstützt der Java-Editor auch JavaFX; allerdings hatte der Unterricht schon vorher begonnen.
zum Seitenanfang / zum Seitenende
Gewünscht ist ein Programm, das den Benutzer auf Knopfdruck grüßt (oder natürlich auch die Welt, falls vor dem Betätigen der [Grüßen]-Schaltfläche als Name "Welt" eingetippt wurde). Die Oberfläche könnte etwa wie folgt aussehen:
Um dieses Programm zu schreiben, wird einfach der „Java-Editor" gestartet und dort mit dem Menübefehl Datei | Neu ein neuer JFrame erzeugt. Nach Wahl eines Ordners und Eingabe eines Programm- bzw. Klassennamens (etwa "P1_Gruss") öffnen sich zwei Fenster: Das Formular P1_Gruss.jfm für die Oberfläche und das Fenster P1_Gruss.java für den Java-Programmtext ("Quelltext"). Aus dem Reiter "Swing 1" werden ein JLabel, ein JTextField, zwei JButtons und noch ein JTextField auf das Formular gezogen. Mit dem Objektinspektor wird der Text des JLabels von "text" auf "Bitte Namen eingeben:" verändert;
außerdem erhalten die Textfelder und Schaltflächen an Stelle der automatisch erzeugten Namen "jTextField1", "jButton1", "jButton2" und "jTextField2" die sprechenden Namen "jTfEingabe", "jBtLoesch", "jBtGruss" und "jTfAusgabe". Die ersten drei Buchstaben kürzen den Typ des Bildschirmobjekts ab. Weiterhin wird der Text auf den Knöpfen abgeändert und die Editierbarkeit des Ausgabefeldes von true auf false gesetzt. Der Java-Editor erzeugt automatisch eine Menge Quelltext, der aber nicht beachtet und nicht verändert werden muss.
Dieses Programm kann schon kompiliert und (mit dem grünen Pfeil) gestartet werden. In das neue, in der Bildschirmmitte erscheinende Fenster kann bereits Text in die Eingabezeile eingetippt werden; auch lassen sich die Knöpfe betätigen - allerdings passiert beim Klick auf die Knöpfe noch nichts. Die Funktionalität muss - wie im Folgenden beschrieben - von Hand ergänzt werden: und zwar an den mit //TODO .. gekennzeichneten Stellen in den beiden bereits automatisch angelegten ActionPerformed-Methoden der Knöpfe. Nur hier - im Bild in den Zeilen 78 und 82 - beginnt jetzt das Programmieren durch die Schülerinnen und Schüler! Die blauen Kommentare "// TODO hier Quelltext einfügen" werden dazu überschreiben bzw. entfernt.
zum Seitenanfang / zum Seitenende
Nachdem die Oberfläche des Programms "P1_Gruss" wie oben beschrieben erstellt wurde, muss jetzt dem Computer mitgeteilt werden, was er beim Druck auf die beiden Schaltflächen machen soll.
Beginnen wir mit dem [ Löschen ]-Knopf: Hier ist klar, dass die beiden Textfelder gelöscht werden sollen, d.h. nachher kein Text mehr drin stehen soll - der Text leer sein soll. Der mitgeteilte Javabefehl jTfEingabe.setText("") wird als naheliegend verstanden. Auch die Anweisungen im [ Grüßen ]-Knopf sind einsichtig. Hier wurden in der letzten Methode drei Zeilen verwendet, um an die früher viel beschworene Dreiteilung jedes Programms nach dem EVA-Prinzip zu erinnern: Eingabe - Verarbeitung - Ausgabe:
public void jBtLoesch_ActionPerformed(ActionEvent evt) { jTfEingabe.setText(""); jTfAusgabe.setText(""); } // end of jBtLoesch_ActionPerformed public void jBtGruss_ActionPerformed(ActionEvent evt) { |
Im Javaeditor sieht der grün unterlegte Quelltext so aus:
Natürlich hätte das Zusammensetzen des festen, hier vom Editor blau gefärbten Textes mit dem eingegebenen, vom Programm aus dem Eingabefeld geholten Namen (in der Variablen rein) auch in der Klammer hinter setText passieren können:
public void jBtGruss_ActionPerformed(ActionEvent evt) {
String rein = jTfEingabe.getText();
jTfAusgabe.setText("Der Computer grüßt dich mit: Hallo "+rein);
} // end of jBtGruss_ActionPerformed
Und letztlich könnte man den Methodenrumpf sogar zu einem -- allerdings weniger gut lesbaren -- Einzeiler zusammen ziehen:
public void jBtGruss_ActionPerformed(ActionEvent evt) {
jTfAusgabe.setText("Der Computer grüßt dich mit: Hallo "+jTfEingabe.getText());
} // end of jBtGruss_ActionPerformed
Andererseits kann bei mehreren Zeilen schon das Variablenkonzept angesprochen werden, wobei deutlich werden muss, dass nur vor der ersten Verwendung der Typ zu Definition angegeben wird; bei der späteren Verwendung entfällt die Typangabe.
zum Seitenanfang / zum Seitenende
Während normalerweise die Befehle vom Computer genau in der aufgeschriebenen Reihenfolge abgearbeitet werden und dabei jede Anweisung genau einmal ausgeführt wird, können Kontrollstrukturen dies ändern. Bei einer Verzweigung wird der Befehl oder werden alle Befehle im Rumpf der Verzweigung entweder nach wie vor einmal ausgeführt (wenn die Bedingung zutrifft), oder sie werden gar nicht ausgeführt. Die Abfolge wird nicht verändert, aber manche Befehle werden statt genau einmal jetzt höchstens einmal ausgeführt, nämlich situationsabhängig vielleicht auch gar nicht. Das Programm P2 erfordert eine solche Verzweigung: Wieder soll die Person, deren Name eingegeben wurde, gegrüßt werden. Allerdings sollen Frauen mit "Hallo, liebe Carla" und Männer mit "Hallo, lieber Georg" gegrüßt werden. Es ist klar, dass dem Computer dazu das Geschlecht der Person mitgeteilt werden muss (geschieht hier über JRadioButtons). Und während bei Männern ein "r" and das "liebe" angehängt werden muss, kann diese Ergänzung bei Frauen entfallen.
Damit ein JRadioButton beim Anklicken automatisch den anderen, früher angeklickten (oder im Objekt-Inspektor auf Selected = true gesetzten) JRadioButton ausschaltet, müssen sie einer gemeinsamen Gruppe angehören. Im Java-Editor wird dazu eine JButtonGroup auf das Formular gezogen. Die Buttongroup sieht man bein Laufenlassen des Programm nicht. Aber den einzelnen Radiobuttons kann jetzt jeweils im Objektinspektor diese Gruppe zugewiesen werden; außerdem erstellt der Javaeditor dann automatisch eine Methode zur bequemen Abfrage des gewählten Radiobuttons.
public void jBtGruss_ActionPerformed(ActionEvent evt) { String rein = jTfEingabe.getText(); String raus = "Hallo, liebe"; if (buttonGroup1_getSelectedRadioButtonLabel().equals("männlich")) { raus = raus + "r"; } raus = raus + " " +rein; jTfAusgabe.setText(raus); } // end of jBtGruss_ActionPerformed |
Dass das "r" richtig erscheint, zeigt nochmal eine Bildschirmansicht des Java-Editors mit veränderter Eingabe:
zum Seitenanfang / zum Seitenende
Bei Anreden kann i.d.R. nur genau eine von zwei Alternativen gewählt werden. Deshalb ist es - anders als in der Übungsaufgabe 2 zu P2 - nicht nötig, nochmal nachzufragen, ob man die Anrede "Herr" haben will, wenn man keine "Frau" ist: Wer nicht als "Frau" angeredet werden will, gilt automatisch als "Herr" und umgekehrt. Deshalb sind nicht zwei einseitige Verzweigungen mit zwei Fragen nötig, sondern es reicht eine Abfrage mit einer zweiseitigen Verzweigung, bei der genau ein Zweig gewählt wird.
Soll beim Gruß sogar die Tageszeit berücksichtigt werden, ist eine mehrfache Verzweigung mit mehr als 2 Möglichkeiten sinnvoll. Während hier durch die Radiobuttons immer eine gültige Alternative ausgewählt werden muss (wenn zuvor im Objekt-Inspektor genau eine Zeit als selected voreingestellt wurde), kann in anderen Anwendungen noch ein default:-Fall als Ersatz-Möglichkeit sinnvoll sein, wenn kein case-Wert gewählt wurde.
public void jBtGruss_ActionPerformed(ActionEvent evt) { String rein = jTfEingabe.getText(); String raus; if (bGAnrede_getSelectedRadioButtonLabel().equals("Herr")) { raus = "Herr "; } else { raus = "Frau "; } switch (bGZeit_getSelectedRadioButtonLabel()) { case "Morgen" : raus = "Guten Morgen, "+ raus; break; case "Tag" : raus = "Guten Tag, "+ raus; break; case "Abend" : raus = "Guten Abend, "+ raus; break; case "Nacht" : raus = "Gute Nacht, "+ raus; break; } // end of switch jTfAusgabe.setText(raus+rein); } // end of jBtGruss_ActionPerformed |
switch (bGZeit_getSelectedRadioButtonLabel())
{
case "Morgen" : raus = "Guten Morgen, "+raus; break;
case "Tag" : raus = "Guten Tag, "+raus; break;
case "Abend" : raus = "Guten Abend, "+raus; break;
case "Nacht" : raus = "Gute Nacht, "+raus; break;
default : raus = "Mahlzeit, "+raus; break;
} // end of switch
// Version 1
if (bGZeit_getSelectedRadioButtonLabel().equals("Morgen")) {
raus = "Guten Morgen, "+raus;
}
if (bGZeit_getSelectedRadioButtonLabel().equals("Tag")) {
raus = "Guten Tag, "+raus;
}
if (bGZeit_getSelectedRadioButtonLabel().equals("Abend")) {
raus = "Guten Abend, "+raus;
}
if (bGZeit_getSelectedRadioButtonLabel().equals("Nacht")) {
raus = "Gute Nacht, "+raus;
} //**
// Version 2
if (bGZeit_getSelectedRadioButtonLabel().equals("Morgen")) {
raus = "Guten Morgen, "+raus;
} else {
if (bGZeit_getSelectedRadioButtonLabel().equals("Tag")) {
raus = "Guten Tag, "+raus;
} else {
if (bGZeit_getSelectedRadioButtonLabel().equals("Abend")) {
raus = "Guten Abend, "+raus;
} else {
if (bGZeit_getSelectedRadioButtonLabel().equals("Nacht")) {
raus = "Gute Nacht, "+raus;
} else {
raus = "Mahlzeit, "+raus;
}
}
}
}
zum Seitenanfang / zum Seitenende
Nichts erhöht die Freude am Programmieren so stark, wie ein erfolgreich selbst programmiertes, funktionsfähiges Spiel, das hier in drei Stadien gezeigt wird
Die "X" und "O" werden abwechselnd auf die gedrückten Knöpfe geschrieben; das Textfeld zeigt aktuelle Informationen zum Spiel und mit [ Neustart ] werden wieder alle Knopf-Beschriftungen gelöscht, der zug-Zähler auf 0 gesetzt und so das Spiel wieder in den Anfangszustand gebracht.
Weil noch keine Reihungen zur Verfügung stehen, werden die neun Knöpfe einzeln im GUI-Builder in die Oberfläche gesetzt und mit dem Objekt-Inspektor sinnvoll benannt. Ich habe sie mit ObenLinks, ObenMitte, ObenRechts, MitteLinks, MitteMitte;... usw. bis UntenRechts bezeichnet bzw. tatsächlich jBtObLi, jBtObMi,.. bis jBtUnRe genannt. Die Beschriftung eines gedrückten Knopfes kann in dessen ActionPerformed-Methode leicht mit dem schon von Textfeldern bekannten setText verändert werden, z.B. etwa durch jBtObLi.setText("X");
Schnell wird klar, dass allerdings kein festes "X" im .setText("X") stehen darf und auch die abwechselnde Verwendung von .setText("O") bei jedem zweiten Knopf keine Lösung ist, weil nicht vorhersehbar ist, in welcher Reihenfolge die Knöpfe später gedrückt werden. Vielmehr muss es eine übergeordnete Variable (außerhalb der Knopf_ActionPerformed-Methoden) geben, die sich merkt, welcher Spieler - "X" oder "O" - als nächster dran ist. Hier dient das Attribut spieler diesem Zweck. Außerdem muss nach jedem Knopfdruck der Wert von spieler gewechselt werden (von "X" nach "O" und umgekehrt). Dieser Wechsel könnte neun mal, nämlich in jedem Knopf, programmiert werden. Leichter ist es allerdings, den Wechsel einmal in eine eigene Methode auszulagern (ähnlich der ButtonGroup_getSelectedRadioButtonLabel-Methode in P2 und P3, die allerdings automatisch vom Java-Editor bzw. dessen GUI-Builder angelegt wurde). Und diese jetzt selbst zu schreibende bzw. geschriebene Methode spielerWechseln wird dann aus jedem Knopf heraus aufgerufen.
Von meinen Schülerinnen und Schülern wurde hingegen nicht sofort daran gedacht, in einem weiteren Attribut (der globalen Variable zug) die Anzahl der Züge mit zu zählen, damit der Computer Unentschieden erkennen kann bzw. das Weiterspielen nach einem Sieg verbietet. Als menschlicher Spieler erkennt man auf einen Blick, dass alle Knöpfe voll sind (und kein Gewinn angezeigt wird, also Unentschieden erreicht wurde) oder man sieht den Gewinn und startet normalerweise in beiden Fällen mit [ Neustart ] ein neues Spiel - und probiert meist gar nicht aus, ob noch weitere Züge möglich sind. Deshalb wurde von den meisten Schülern auch nicht geprüft, ob ein gedrückter Knopf tatsächlich vorher leer war: erst nach mehreren Testspielen wurde eher zufällig mal ein falscher, bereits beschrifteter Knopf gedrückt (und Einzelne betrogen damit dann lieber gelegentlich ihre Mitspieler, statt das Programm zu verbessern). Die für diese Überprüfungen nötigen Programmteile habe ich nachstehend rot gefärbt; sie sind anfangs verzichtbar und können später ergänzt werden.
String spieler = "X"; int zug = 0; private void spielerWechseln() { switch (spieler) { case "X" : spieler = "O"; break; case "O" : spieler = "X"; break; default : spieler = "?"; } // end of switch jTfAnzeige.setText("\""+spieler+"\" ist am Zug"); } public void jBtObLi_ActionPerformed(ActionEvent evt) { if (zug<9 && jBtObLi.getText().equals("")) { jBtObLi.setText(spieler); if ((jBtObMi.getText().equals(spieler) && jBtObRe.getText().equals(spieler)) ||(jBtMiLi.getText().equals(spieler) && jBtUnLi.getText().equals(spieler)) ||(jBtMiMi.getText().equals(spieler) && jBtUnRe.getText().equals(spieler))) { jTfAnzeige.setText("Sieg für \""+spieler+"\"!"); zug = 10; } // end of if else if (zug>=8) { jTfAnzeige.setText("Unentschieden!"); } else { zug = zug+1; spielerWechseln(); } } // end of if } // end of jBtObLi_ActionPerformed public void jBtObMi_ActionPerformed(ActionEvent evt) { if (zug<9 && jBtObMi.getText().equals("")) { jBtObMi.setText(spieler); if ((jBtObLi.getText().equals(spieler) && jBtObRe.getText().equals(spieler)) ||(jBtMiMi.getText().equals(spieler) && jBtUnMi.getText().equals(spieler))) { jTfAnzeige.setText("Sieg für \""+spieler+"\"!"); zug = 10; } // end of if else if (zug>=8) { jTfAnzeige.setText("Unentschieden!"); } else { zug = zug+1; spielerWechseln(); } } // end of if } // end of jBtObMi_ActionPerformed public void jBtObRe_ActionPerformed(ActionEvent evt) { ... |
Neu sind hier auch die mehrteiligen Bedingungen (wobei && 'und' bedeutet: alle Teil-Bedingungen müssen wahr sein, damit der if-Fall ausgeführt wird). Da eine Dreier-Gewinnstellung nur beim Druck auf einen Knopf geschlossen werden kann, muss in jeder Knopf-Methode überprüft werden, welche Dreier mit diesem Knopf eventuell vervollständigt werden könnten. Ein Dreier ist dann vollständig, wenn die zwei anderen Knöpfe in der gleichen Zeile, Spalte oder Diagonale beide das gleiche Aufschrift "X" oder "O" wie der Spieler haben (bevor dessen Inhalts-Text für den nächsten Zug gewechselt wird). Deshalb wird auf .equals(spieler) abgefragt.
zum Seitenanfang / zum Seitenende
Gebeten, das Wichtigste fürs Programmieren schriftlich festzuhalten (es wird kein Buch verwendet), habe ich für meinen Kurs ein zweiseitiges Blatt mit einer Zusammenfassung heraus gegeben, das (bei installiertem pdf-Reader) hier mit Linksklick in einem neuen Fenster gelesen werden oder nach Rechtsklick auf den eigenen Computer herunter geladen werden kann:
Java-Programme IF EF-Bi 2017.pdf (ca 80 kB)
Dort findet man als abgebildete Beispiel-Oberfläche schon ein Programm, das viele Grüße in einem mehrzeiligen Ausgabefeld vom Typ JTextArea zulässt. Mit diesem Programm sollen die auf dem Blatt bereits aufgeführten Wiederholungen als weitere Kontrollstrukturen eingeführt werden. Mit Wiederholungen bieten sich viele schöne weitere Programmier-Möglichkeiten an, z.B. ein Kassenautomat mit Wechselgeldrückgabe, ein Quiz-Programm im 'Wer-wird-Millionär'-Format oder natürlich wieder diverse Autorennen. Dazu aber mehr auf meiner kommenden Seite if-java-b2_2017.htm ff
zurück zur Informatik-Hauptseite
weiter zur nächsten Seite (Programme P5 bis P9) Seite b2)_2017
zur P9-Vertiefungsseite (Lösung zu Aufgabe 3 von P9) Seite b3)_2018
zur letzten Seite dieser Serie (Programme P10 bis P12) Seite b4)_2018