Klausurvorbereitung¶
Alle wesentlichen Konzepte, die wir für die Klausur benötigen, haben wir jetzt in diesem und im vergangenen Semester gelernt. Wir wollen diese Kenntnisse nun durch mehrere Anwendungen vertiefen. Ganz vereinzelt wird dabei auch etwas Neues auftauchen. Dabei handelt es sich aber nicht um ein neues Konzept, sondern bspw. um einen Listener, den wir noch nicht hatten. Solche "Überraschungen" werden Sie aber in der Klausur nicht erleben.
Dieses Kapitel wird hier so aufgebaut sein, dass es immer eine Aufgabe (eine Probeklausur) gibt und die dazugehörige Lösung, die aber "aufgeklappt" werden muss. Sie können ja immer zuerst probieren, die Lösung selbständig zu entwickeln. Wenn Sie am Ende alle Aufgaben selbständig lösen können, sind Sie mit Sicherheit sehr gut auf die Klausur vorbereitet.
Wir beginnen aber noch kurz mit allgemeinen Betrachtungen zum Grundgerüst. Eines der nachfolgend vorgestellten Grundgerüste werden Sie mit Sicherheit bei der Klausur verwenden können.
Mögliche Grundgerüste¶
Wie Sie wissen, ist das Fenster (JFrame) im BorderLayout. Das bedeutet, dass das Fenster in fünf Bereiche (Container) unterteilt ist. Sie müssen sich bei der Konfiguration überlegen,
- ob "gezeichnet" werden soll, d.h. ob das
JPanelimCENTERdes Fensters eine eigene Klasse ist, oder ob es mithilfe einer Methode erzeugt werden kann und - welche der fündf Bereiche Sie benötigen.
Wir erläutern das an Beispielen und beginnen mit einem Grundgerüst, welches wir für das Zeichnen verwenden können, welches also eine Canvas enthält.
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 | |
In diesem Grundgerüst werden alle fünf Bereiche des Fensters mit JPanel befüllt. Das CENTER wird mit einem Objekt der Klasse Canvas befüllt, welche von JPanel erbt. Diese Klasse wird benötigt, um in die paintComponent()-Methode zu zeichnen. Sollten Sie z.B. kein Panel im EAST- und im WEST-Bereich des Fensters benötigen, dann löschen Sie einfach die Zeilen 23 und 24 sowei die beiden Methoden initEast() und initWest().
Ein Grundgerüst ohne Canvas, also ohne Zeichnen, könnte dann so aussehen:
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 | |
Beachten Sie, dass JPanel ohne Angabe eines LayoutManagers im FlowLayout sind. Das können Sie ändern und die einzelnen JPanel auch noch weiter verschachteln.
Eines dieser beiden Grundgerüste können wir nun stets verwenden. Es muss dann jeweils "nur" noch angepasst werden. Wir beginnen mal mit einer Aufgabe.
Quadrat¶
Aufgabe Quadrat
-
Schreiben Sie eine Klasse
Quadrat, die folgendes Fenster darstellt:
- Es gibt also eine Zeichenfläche (
Canvas) und zwei Buttonsnewundfill.
- Es gibt also eine Zeichenfläche (
-
Nach dem Klicken des Buttons
newsoll ein schwarzes unausgefülltes Quadrat mit der Strichstärke3.0so in der Zeichenfläche erscheinen, dass es-
mittig in der Zeichenfläche angeordnet ist und
-
der kürzere der beiden Abstände (links/rechts oder oben/unten) ca.
10%der Zeichenfläche groß ist. -
Nach dem Klicken des Buttons
newsteht darin nicht mehrnew, sondernrefresh.

-
-
Nach Klicken des Buttons
fillsoll das Quadrat mit einer zufällig gewählten Farbe befüllt werden. Es soll aber trotzdem noch der schwarze Rand bleiben (am einfachsten: einmal mit der zufällig gewählten Farbe befüllt und danach nochmal unbefüllt schwarz malen).- Der Button wechselt den Text auf
unfill. Nach dem Klicken aufunfillsoll das Quadrat wieder unausgefüllt sein.

- Der Button wechselt den Text auf
-
Die zufällig erzeugte Farbe soll so lange die Farbe zum Ausfüllen bleiben, bis der
refresh-Button geklickt wird, d.h.-
durch mehrmaliges Klicken des Button
fillundunfilländert sich die Farbe zum Befüllen des Quadrats nicht, -
erst durch Klicken des Button
refreshwird eine neue Farbe erzeugt, die dann wieder bis zum nächsten Klicken vonrefreshbleibt.
-
-
Implementieren Sie den
MouseListenerund denMouseMotionListenerso, dass Sie bei gedrückter Maustaste die Größe des Quadrates ändern.-
Wenn Sie mit der Maus auf eine Kante des Quadrates (
+/-10px) klicken und dann die Maus gedrückt halten, ändert sich die Größe des Quadrates entsprechend. -
Wenn Sie weit weg von (also mehr als
10pxvon der Kante entfernt) einer Kante klicken (egal, ob im Quadrat oder außerhalb), passiert gar nichts. -
Die Größenänderungen sollen stets sofort sichtbar sein. Nach Loslassen der Maustaste bleibt das Quadrat in der eingestellten Größe.
-
Das Quadrat bleibt stets möglichst mittig.
-
Tipp: An welche Kante Sie klicken, ist egal. Klicken Sie auf eine vertikale Kante (links oder rechts), brauchen Sie nur die Änderungen des x-Wertes zu berücksichtigen, klicken Sie auf eine horizontale Kante (oben oder unten), brauchen Sie nur die Änderungen der y-Werte zu berücksichtigen, um die neue Größe des Quadrates zu ermitteln.
-
Lösung Quadrat
-
Zuerst überlegen wir uns, welches Grundgerüst wir verwenden und wie wir es anpassen. Wir müssen zeichnen, also das Grundgerüst mit
Canvas. Von den Nord-, Süd-, Ost-, West-Bereichen benötigen wir nur den Süden (dort kommen die 'new'- undfill-Buttons rein). Also ist das unser Ausgangspunkt, in das wir schonmal die Buttons eingefügt haben: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
import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame{ Canvas canvas; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar // hier koennen wir zeichnen } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); JButton fillBtn = new JButton("fill"); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } }- Von den
initXXX()-Methoden benötigen wir auch nur nochinitSouth(). Die anderen drei sind deshalb gelöscht.
- Von den
-
Die Implementierung des
ActionListenerfür dennew-Button erledigen wir mit einer anonymen Klasse. Wir müssen uns überlegen, ob es notwendig ist, für das Quadrat eine eigene Datenstruktur (z.B. Klasse) anzulegen. Zunächst machen wir das einmal ohne, sondern geben nur in einer globalen VariablenzeigeQuadratan, ob das Quadrat gezeichnet werden soll oder nicht.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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame{ Canvas canvas; boolean zeigeQuadrat = false; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar if(Quadrat.this.zeigeQuadrat) { g2.setStroke(new BasicStroke(3.0f)); int canvasWidth = this.getWidth(); int canvasHeight = this.getHeight(); if(canvasHeight > canvasWidth) { int abstand = (int)(canvasWidth * 0.1); int seiteQuadrat = (int)(canvasWidth * 0.8); int oben = (canvasHeight - seiteQuadrat) / 2; g2.drawRect(abstand, oben, seiteQuadrat, seiteQuadrat); } else { int abstand = (int)(canvasHeight * 0.1); int seiteQuadrat = (int)(canvasHeight * 0.8); int links = (canvasWidth - seiteQuadrat) / 2; g2.drawRect(links, abstand, seiteQuadrat, seiteQuadrat); } } } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); newBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Quadrat.this.zeigeQuadrat = true; JButton thisBtn = (JButton)e.getSource(); // kann nur der newBtn sein! thisBtn.setText("refresh"); Quadrat.this.canvas.repaint(); } }); JButton fillBtn = new JButton("fill"); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } }-
Wenn die Variable
zeigeQuadratdurch den Buttonklick auftruegesetzt wurde (Zeile74), dann wird in derpaintComponent()-Methode das Quadrat dargestellt (Zeile40). -
Zur korrekten Darstellung des Quadrates ist es von Bedeutung, ob das Fenster (die
Canvas) höher als breit ist oder umgedreht (Zeile45). -
Ist es höher als breit, dann wird es in die Breite der
Canvaseingepasst, d.h. der Abstand nach links ist 1/10 derCanvas-Breite (Zeile47) und die Seitenlänge des Quadrates ist 8/10 der Breite (Zeile48). Der Abstand des Quadrates nach oben muss entsprechend berechnet werden (Zeile49). -
Ist es breiter als hoch, dann wird es in die Höhe der
Canvaseingepasst, d.h. der Abstand nach oben ist 1/10 derCanvas-Höhe (Zeile54) und die Seitenlänge des Quadrates ist 8/10 der Höhe (Zeile55). Der Abstand des Quadrates nach links muss entsprechend berechnet werden (Zeile56). -
In der
actionPerformed()-Methode für den Button wird nicht nurzeigeQuadratauftruegesetzt, sondern dieCanvasauch neu gezeichnet (Zeile77)! -
Die Quelle des
ActionEventkann nur der Button selbst sein, da wir eine anonyme Klasse zur Implementierung desActionListenerverwenden. Deshalb können wir die Quelle ohne Prüfung in einenJButtonkonvertieren (Zeile75). Mithilfe dersetText()-methode fürJButtonsetzen wir den neuen Buttontext aufrefresh(Zeile76).
-
-
Die Implementierung des
ActionListenerfür denfill-Button erledigen wir erneut mit einer anonymen Klasse. Wir gehen ähnlich vor, wie beimnew-Button und erstellen eine globale VariablefuelleQuadrat. Diese gibt an, ob das Quadrat befüllt gezeichnet werden soll oder nicht.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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame{ Canvas canvas; boolean zeigeQuadrat = false; boolean fuelleQuadrat = false; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar if(Quadrat.this.zeigeQuadrat) { g2.setStroke(new BasicStroke(3.0f)); int canvasWidth = this.getWidth(); int canvasHeight = this.getHeight(); Random r = new Random(); int rot = r.nextInt(256); int gruen = r.nextInt(256); int blau = r.nextInt(256); g2.setColor(new Color(rot, gruen, blau)); if(canvasHeight > canvasWidth) { int abstand = (int)(canvasWidth * 0.1); int seiteQuadrat = (int)(canvasWidth * 0.8); int oben = (canvasHeight - seiteQuadrat) / 2; if(fuelleQuadrat) { g2.fillRect(abstand, oben, seiteQuadrat, seiteQuadrat); } g2.setColor(Color.BLACK); g2.drawRect(abstand, oben, seiteQuadrat, seiteQuadrat); } else { int abstand = (int)(canvasHeight * 0.1); int seiteQuadrat = (int)(canvasHeight * 0.8); int links = (canvasWidth - seiteQuadrat) / 2; if(fuelleQuadrat) { g2.fillRect(links, abstand, seiteQuadrat, seiteQuadrat); } g2.setColor(Color.BLACK); g2.drawRect(links, abstand, seiteQuadrat, seiteQuadrat); } } } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); newBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Quadrat.this.zeigeQuadrat = true; JButton thisBtn = (JButton)e.getSource(); // kann nur der newBtn sein! thisBtn.setText("refresh"); Quadrat.this.canvas.repaint(); } }); JButton fillBtn = new JButton("fill"); fillBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("fill")) { Quadrat.this.fuelleQuadrat = true; thisBtn.setText("unfill"); } else { Quadrat.this.fuelleQuadrat = false; thisBtn.setText("fill"); } Quadrat.this.canvas.repaint(); } }); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } }-
Die Farbe zum Ausfüllen erzeugen wir zufällig mithilfe von
Random(Zeilen48-52). -
Es wird jeweils zuerst aus das ausgefüllte Quadrat gezeichnet (Zeile
61bzw.73) fallsfuelleQuadratden Werttruehat (Zeile59bzw.71). -
Nach dem Zeichnen des befüllten Quadrates wird die Zeichenfarbe wieder auf schwarz gestellt (Zeile
63bzw.75) und dann das nichtausgefüllte Quadrat gezeichnet. -
In der
actionPerformed()_Methode müssen wir unterscheiden, ob im Buttonfillsteht oderunfill(zeile108). Je nachdem wirdfuelleQuadratauftrueoderfalsegesetzt und der Text im Button entsprechend geändert.
-
-
Um nicht bei jeden Aufruf von
paintComponent()eine neue Füllfarbe zufällig zu erzeugen, muss das Erzeugen der Farbe ausgelagert werden. Da auf die Farbe sowohl inpaintComponent()(View) als auch inactionPerfomed()(Controller) zugegriffen wird, muss diese als globale Referenz deklariert werden.- Wahrscheinlich ist es gut, die Farbe beim ersten Mal zu erzeugen, wenn der
new-Button geklickt wird und dann immer durch das Klicken desrefresh-Buttons.
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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame{ Canvas canvas; boolean zeigeQuadrat = false; boolean fuelleQuadrat = false; Color aktuelleFuellfarbe = Color.WHITE; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar if(Quadrat.this.zeigeQuadrat) { g2.setStroke(new BasicStroke(3.0f)); int canvasWidth = this.getWidth(); int canvasHeight = this.getHeight(); g2.setColor(Quadrat.this.aktuelleFuellfarbe); if(canvasHeight > canvasWidth) { int abstand = (int)(canvasWidth * 0.1); int seiteQuadrat = (int)(canvasWidth * 0.8); int oben = (canvasHeight - seiteQuadrat) / 2; if(fuelleQuadrat) { g2.fillRect(abstand, oben, seiteQuadrat, seiteQuadrat); } g2.setColor(Color.BLACK); g2.drawRect(abstand, oben, seiteQuadrat, seiteQuadrat); } else { int abstand = (int)(canvasHeight * 0.1); int seiteQuadrat = (int)(canvasHeight * 0.8); int links = (canvasWidth - seiteQuadrat) / 2; if(fuelleQuadrat) { g2.fillRect(links, abstand, seiteQuadrat, seiteQuadrat); } g2.setColor(Color.BLACK); g2.drawRect(links, abstand, seiteQuadrat, seiteQuadrat); } } } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); newBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("new")) { Quadrat.this.zeigeQuadrat = true; thisBtn.setText("refresh"); } Random r = new Random(); int rot = r.nextInt(256); int gruen = r.nextInt(256); int blau = r.nextInt(256); Quadrat.this.aktuelleFuellfarbe = new Color(rot, gruen, blau); Quadrat.this.canvas.repaint(); } }); JButton fillBtn = new JButton("fill"); fillBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("fill")) { Quadrat.this.fuelleQuadrat = true; thisBtn.setText("unfill"); } else { Quadrat.this.fuelleQuadrat = false; thisBtn.setText("fill"); } Quadrat.this.canvas.repaint(); } }); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } } - Wahrscheinlich ist es gut, die Farbe beim ersten Mal zu erzeugen, wenn der
-
Für eine geeignete Implementierung des
MouseListenerund desMouseMotionListenerbenötigen wir globalen Zugriff auf die Größe und die Position des Quadrates. Wir müssen ja mit der Maus erkennen können, ob wir eine Kante des Quadrates getroffen haben. Wir lagern deshalb einige Werte des Quadrates global aus (und Umbenennungen):int quadratLinks, quadratRechts, quadratOben, quadratUnten, quadratLaenge;- In
paintComponent()werden diese globalen Variablen nun mit Werten belegt:
if(canvasHeight > canvasWidth) { Quadrat.this.quadratLinks = (int)(canvasWidth * 0.1); Quadrat.this.quadratLaenge = (int)(canvasWidth * 0.8); Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratOben = (canvasHeight - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } else { Quadrat.this.quadratOben = (int)(canvasHeight * 0.1); Quadrat.this.quadratLaenge = (int)(canvasHeight * 0.8); Quadrat.this.quadratLinks = (canvasWidth - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); }- Nun implementieren wir
MouseListenerundMouseMotionListenerund meldencanvasdaran an.
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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame implements MouseListener, MouseMotionListener { Canvas canvas; boolean zeigeQuadrat = false; boolean fuelleQuadrat = false; Color aktuelleFuellfarbe = Color.WHITE; int quadratLinks, quadratRechts, quadratOben, quadratUnten, quadratLaenge; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.canvas.addMouseListener(this); this.canvas.addMouseMotionListener(this); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar if(Quadrat.this.zeigeQuadrat) { g2.setStroke(new BasicStroke(3.0f)); int canvasWidth = this.getWidth(); int canvasHeight = this.getHeight(); g2.setColor(Quadrat.this.aktuelleFuellfarbe); if(canvasHeight > canvasWidth) { Quadrat.this.quadratLinks = (int)(canvasWidth * 0.1); Quadrat.this.quadratLaenge = (int)(canvasWidth * 0.8); Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratOben = (canvasHeight - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } else { Quadrat.this.quadratOben = (int)(canvasHeight * 0.1); Quadrat.this.quadratLaenge = (int)(canvasHeight * 0.8); Quadrat.this.quadratLinks = (canvasWidth - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } } } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); newBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("new")) { Quadrat.this.zeigeQuadrat = true; thisBtn.setText("refresh"); } Random r = new Random(); int rot = r.nextInt(256); int gruen = r.nextInt(256); int blau = r.nextInt(256); Quadrat.this.aktuelleFuellfarbe = new Color(rot, gruen, blau); Quadrat.this.canvas.repaint(); } }); JButton fillBtn = new JButton("fill"); fillBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("fill")) { Quadrat.this.fuelleQuadrat = true; thisBtn.setText("unfill"); } else { Quadrat.this.fuelleQuadrat = false; thisBtn.setText("fill"); } Quadrat.this.canvas.repaint(); } }); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } @Override public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mousePressed x=" + x + ", y=" + y); } @Override public void mouseDragged(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseDragged x=" + x + ", y=" + y); } @Override public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseReleased x=" + x + ", y=" + y); } @Override public void mouseMoved(MouseEvent e) {} @Override public void mouseClicked(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} } - In
-
Für das eigentliche Verschieben rächt sich jetzt, dass wir uns anfangs keine gute Datenstruktur überlegt hatten. Es wird jetzt alles ein wenig komplizierter, da wir mehrere Unterscheidungen treffen müssen:
-
Wir müssen unterscheiden, ob das Quadrat nun gerade bewegt wird oder nicht.
-
Wir müssen unterscheiden, ob wir die linke Kante bewegen, oder die rechte oder die untere oder die obere.
-
Für alle dieses Unterscheidungen erstellen wir uns globale Variablen.
if(canvasHeight > canvasWidth) { Quadrat.this.quadratLinks = (int)(canvasWidth * 0.1); Quadrat.this.quadratLaenge = (int)(canvasWidth * 0.8); Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratOben = (canvasHeight - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } else { Quadrat.this.quadratOben = (int)(canvasHeight * 0.1); Quadrat.this.quadratLaenge = (int)(canvasHeight * 0.8); Quadrat.this.quadratLinks = (canvasWidth - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); }- Nun implementieren wir
MouseListenerundMouseMotionListenerund meldencanvasdaran an.
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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Quadrat extends JFrame implements MouseListener, MouseMotionListener { Canvas canvas; boolean zeigeQuadrat = false; boolean fuelleQuadrat = false; Color aktuelleFuellfarbe = Color.WHITE; int quadratLinks, quadratRechts, quadratOben, quadratUnten, quadratLaenge; boolean bewegt = false; boolean linkeKante = false; boolean rechteKante = false; boolean obereKante = false; boolean untereKante = false; int warX = 0; int warY = 0; public Quadrat() { super(); this.setTitle("Quadrat"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.canvas.addMouseListener(this); this.canvas.addMouseMotionListener(this); this.getContentPane().add(this.canvas, BorderLayout.CENTER); this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar if(Quadrat.this.zeigeQuadrat) { g2.setStroke(new BasicStroke(3.0f)); int canvasWidth = this.getWidth(); int canvasHeight = this.getHeight(); g2.setColor(Quadrat.this.aktuelleFuellfarbe); if(!Quadrat.this.bewegt) { if(canvasHeight > canvasWidth) { Quadrat.this.quadratLinks = (int)(canvasWidth * 0.1); Quadrat.this.quadratLaenge = (int)(canvasWidth * 0.8); Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratOben = (canvasHeight - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } else { Quadrat.this.quadratOben = (int)(canvasHeight * 0.1); Quadrat.this.quadratLaenge = (int)(canvasHeight * 0.8); Quadrat.this.quadratLinks = (canvasWidth - Quadrat.this.quadratLaenge) / 2; Quadrat.this.quadratRechts = Quadrat.this.quadratLinks + Quadrat.this.quadratLaenge; Quadrat.this.quadratUnten = Quadrat.this.quadratOben + Quadrat.this.quadratLaenge; if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } } else // bewegt { if(fuelleQuadrat) { g2.fillRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } g2.setColor(Color.BLACK); g2.drawRect(Quadrat.this.quadratLinks, Quadrat.this.quadratOben, Quadrat.this.quadratLaenge, Quadrat.this.quadratLaenge); } } } } private JPanel initSouth() { JPanel south = new JPanel(); JButton newBtn = new JButton("new"); newBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("new")) { Quadrat.this.zeigeQuadrat = true; thisBtn.setText("refresh"); } Random r = new Random(); int rot = r.nextInt(256); int gruen = r.nextInt(256); int blau = r.nextInt(256); Quadrat.this.aktuelleFuellfarbe = new Color(rot, gruen, blau); Quadrat.this.canvas.repaint(); } }); JButton fillBtn = new JButton("fill"); fillBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JButton thisBtn = (JButton)e.getSource(); if(thisBtn.getText().equals("fill")) { Quadrat.this.fuelleQuadrat = true; thisBtn.setText("unfill"); } else { Quadrat.this.fuelleQuadrat = false; thisBtn.setText("fill"); } Quadrat.this.canvas.repaint(); } }); south.add(newBtn); south.add(fillBtn); return south; } public static void main(String[] args) { new Quadrat(); } @Override public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mousePressed x=" + x + ", y=" + y); int linksX = this.quadratLinks; int rechtsX = this.quadratRechts; int obenY = this.quadratOben; int untenY = this.quadratUnten; if(x > linksX-10 && x < linksX+10) { this.bewegt = true; this.linkeKante = true; this.warX = x; } else if(x > rechtsX-10 && x < rechtsX+10) { this.bewegt = true; this.rechteKante = true; this.warX = x; } else if(y > obenY-10 && y < obenY+10) { this.bewegt = true; this.obereKante = true; this.warY = y; } else if(y > untenY-10 && y < untenY+10) { this.bewegt = true; this.untereKante = true; this.warY = y; } } @Override public void mouseDragged(MouseEvent e) { if(this.bewegt) { int x = e.getX(); int y = e.getY(); System.out.println("mouseDragged x=" + x + ", y=" + y); if(this.linkeKante) { int diffX = x - this.warX; this.quadratLinks = this.quadratLinks + diffX; this.quadratOben = this.quadratOben + diffX; this.quadratLaenge = this.quadratLaenge - 2 * diffX; this.warX = x; } else if(this.rechteKante) { int diffX = x - this.warX; this.quadratLinks = this.quadratLinks - diffX; this.quadratOben = this.quadratOben - diffX; this.quadratLaenge = this.quadratLaenge + 2 * diffX; this.warX = x; } else if(this.obereKante) { int diffY = y - this.warY; this.quadratLinks = this.quadratLinks + diffY; this.quadratOben = this.quadratOben + diffY; this.quadratLaenge = this.quadratLaenge - 2 * diffY; this.warY = y; } else if(this.untereKante) { int diffY = y - this.warY; this.quadratLinks = this.quadratLinks - diffY; this.quadratOben = this.quadratOben - diffY; this.quadratLaenge = this.quadratLaenge + 2 * diffY; this.warY = y; } Quadrat.this.canvas.repaint(); } } @Override public void mouseReleased(MouseEvent e) { this.bewegt = false; Quadrat.this.canvas.repaint(); } @Override public void mouseMoved(MouseEvent e) {} @Override public void mouseClicked(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }-
In
mousePressed()bestimmen wir zunächst, ob wir in der Nähe der linken oder der rechten oder der oberen oder unteren Kante geklickt haben. Wenn ja, dann setzen wir die entsprechenden Variablen und merken uns, wo (warXoderwarY) wir hingeklickt hatten. -
In
mouseDragged()wird zunächst ermittelt, ob wir überhaupt imbewegt-Modus sind. Wenn ja, ermitteln wir, wie weit und wohin wir uns seit dem letzten Aufruf vonmouseDragged()bewegt haben. Dementsprechend werden die Variablen für das Quadrat neu gesetzt. -
Wenn wir die Maus wieder loslassen (
mouseReleased()) wirdbewegtwieder auffalsegesetzt und das Quadrat wird somit wieder "normal" durch diepaintComponent()-Methode dargestellt.
-
Schachbrett¶
Aufgabe Schachbrett
-
Schreiben Sie eine Klasse
Schachbrett, die folgendes Fenster darstellt:
- Es werden 8x8 Rechtecke dargestellt – abwechselnd mit grauem und weißem Hintergrund
- bitte beachten:
- die dargestellten Rechtecke sind gezeichnet, es handelt sich nicht um einzelne JPanels!!! (ist für später wichtig)
- das 8x8-Feld der Rechtecke füllt die Zeichenfläche vollständig, d.h. wenn Sie die Größe des Fensters verändern, passt sich auch das 8x8-Feld entsprechend an
- bitte beachten:
- Es werden 8x8 Rechtecke dargestellt – abwechselnd mit grauem und weißem Hintergrund
-
Implementieren Sie den
MausListenerso, dass Sie durch Mausklick auf die Zeichenfläche einen ausgefüllten Kreis in das Rechteck malen, in das Sie geklickt haben.
- Die Abbildung zeigt das Schachbrett nach einem Mausklick auf das Rechteck in der 2. Zeile und 4. Spalte.
- Versuchen Sie, die Kreise möglichst zentriert in die Rechtecke zu malen. Eine gute Größe für den Durchmesser der Kreise ist ⅓ der Breite oder Höhe der Rechtecke.
- Die Abbildung zeigt das Schachbrett nach einem Mausklick auf das Rechteck in der 2. Zeile und 4. Spalte.
-
Klicken Sie mehrmals mit der Maus, werden entsprechend mehrere Kreise dargestellt.

-
Fügen Sie dem Süden des Fensters ein
JPanelhinzu. DiesesJPanelenthält einenJButton'clear field'. Nach Drücken dieses Buttons werden alle roten Kreise wieder gelöscht.
-
Erweitern Sie Ihre Implementierung nun so, dass durch einen Mausklick nicht nur ein roter Kreis gezeichnet wird, sondern auch noch eine horizontale und eine vertikale Linie in der Zeile und der Spalte, in der der Kreis gezeichnet wird.

- in der Zeile und Spalte des Kreises erscheint jeweils mittig eine rote Linie

- Abbildung zeigt Schachbrett nach mehreren Mausklicks
- Die Linien müssen nicht, wie im Bild dargestellt, in der Mitte der Rechtecke anfangen. Sie können auch komplett durchgezeichnet werden, d.h. vom Beginn bis zum Ende einer Zeile bzw. Spalte.
-
Erweitern Sie Ihre Implementierung nun so, dass es nicht mehr möglich ist, dass 2 Kreise in der gleichen Spalte oder in der gleichen Zeile erscheinen. Wenn Sie ein Rechteck anklicken, in dessen Zeile oder Spalte bereits ein anderer Kreis ist, so soll kein neuer Kreis gezeichnet werden, sondern das Rechteck als grünes Rechteck erscheinen.

- In das grüne Rechteck wurde geklickt - es erscheint kein Kreis, da in der Zeile bereits ein Kreis ist – stattdessen ein grünes Rechteck.

- In das grüne Rechteck wurde geklickt - es erscheint kein Kreis, da in der Zeile bereits ein Kreis ist - stattdessen ein grünes Rechteck.
- Das Rechteck bleibt nur bis zum nächsten Klick markiert (grün).
-
Zusatzaufgabe: Erweitern Sie die Schritte 5 und 6 auch auf die Diagonalen, d.h. es sollen auch die jeweiligen Diagonalen der Punkte eingezeichnet werden und keine zwei Punkte dürfen in der gleichen Diagonalen liegen.
Lösung Schachbrett
-
Zuerst überlegen wir uns, welches Grundgerüst wir verwenden und wie wir es anpassen. Wir müssen zeichnen, also das Grundgerüst mit
Canvas. Von den Nord-, Süd-, Ost-, West-Bereichen benötigen wir nur den Süden (dort kommt der 'clear field'-Button rein). Also ist das unser Ausgangspunkt: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
import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JFrame; import javax.swing.JPanel; public class Schachbrett extends JFrame{ Canvas canvas; public Schachbrett() { super(); this.setTitle("Schachbrett"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); // NORTH, EAST, WEST geloescht this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 300); this.setLocation(300,200); this.setVisible(true); } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar // hier koennen wir zeichnen } } private JPanel initSouth() { JPanel south = new JPanel(); // hier das JPanel fuer SOUTH befuellen return south; } public static void main(String[] args) { new Schachbrett(); } }Von den
initXXX()-Methoden benötigen wir auch nur nochinitSouth(). Die anderen drei sind deshalb gelöscht. -
Jetzt können wir mit dem Zeichnen des Schachbretts beginnen. Dazu könnten wir "einfach" 64 gleich große Rechtecke in die
Canvaszeichnen. Wir sollten hier aber unbedingt dafür ein Model, d.h. eine Datenstruktur erstellen und diese dann in der View darstellen. Für eine solche Datenstruktur (für ein Model) gibt es drei Gründe:- wir wollen später erkennen, in welches Feld geklickt wurde und
- wir wollen erkennen können, in welches Feld bereits geklickt wurde und
- wir wollen erkennen können, welche Felder Nachbarfelder sind.
Da die Dimensionen hier fest sind (8x8), kann man das in einem (zweidimensionalen) Array erledigen. Als Typ jedes einzelnen Elementes sollte
booleangenügen - markiert oder nicht markiert. Wenn Sie sich nicht sicher sind, ob zwei Zustände genügen, können Sie auch eineenumals Typ verwenden. Wir machen das mal, dann bleibt die Lösung flexibler und wir übenenumnochmal.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
import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Graphics2D; import javax.swing.JFrame; import javax.swing.JPanel; public class Schachbrett extends JFrame{ Canvas canvas; enum State { MARKIERT, UNMARKIERT }; State[][] field; public Schachbrett() { super(); this.setTitle("Schachbrett"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.initField(); this.canvas = new Canvas(); this.getContentPane().add(this.canvas, BorderLayout.CENTER); // NORTH, EAST, WEST geloescht this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 300); this.setLocation(300,200); this.setVisible(true); } private void initField() { this.field = new State[8][8]; for(int row=0; row<this.field.length; row++) { for(int col=0; col<this.field[row].length; col++) { this.field[row][col] = State.UNMARKIERT; } } } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar // hier koennen wir zeichnen } } private JPanel initSouth() { JPanel south = new JPanel(); // hier das JPanel fuer SOUTH befuellen return south; } public static void main(String[] args) { new Schachbrett(); } }- Wir hätten das
fieldauch im Konstruktor erzeugen und befüllen können, lagern es aber in eine eigene Methode aus und rufen diese im Konstruktor auf. Alles, was wir funktional in eine eigene Methode auslagern können, sollten wir auch auslagern - liest sich viel besser!
-
Jetzt überlegen wir, wie wir das Schachbrett darstellen (die View). Die Darstellung erfolgt am besten mit der
fillRect()-Methode. Diese erwartet die Koordinaten des linken oberen Punktes und die Breite und die Höhe des Rechtecks. Breite und Höhe ergeben sich aus der Breite und Höhe derCanvas, jeweils geteilt durch8. Aus der Position des zu zeichnenden Rechtecks in einer Zeile ergibt sich dann derx-Wert für den linken oberen Punkt und aus der Position des zu zeichnenden Feldes in der Spalte ergibt sich dery-Wert des linken oberen Punktes. Wir ändern nun nur diepaintComponent()-Methode (die View), da es nur um die Darstellung geht: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
private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); int heightRect = canvasHeight / 8; int widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit gleichen Farbe anfangen, wie aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } g2.fillRect(x, y, widthRect, heightRect); } } } }- Wir laufen also durch das
field-Array und ermitteln für die aktuelle Zeile deny-Wert und für die jeweils aktuelle Spalte denx-Wert.- Damit wir immer zwischen Grau und Weiß umschalten, erstellen wir uns eine boole'sche Variable
grey, die abwechselndtrueundfalsewird, je nachdem, ob wir als nächstes ein graues oder ein weißes Feld zeichnen wollen. - Nach jeder Zeile ändert sich die Farbe jedoch nicht (wir fangen in der neuen Zeile mit der gleichen Farbe an, wie wir in der alten Zeile aufgehört haben). Deshalb mussten wir noch die Anweisung in Zeile
61hinzufügen. - Das Schachbrett wird nun so gezeichnet, wie wir das wollten. Wenn das Fenster vergrößert oder verkleinert wird, dann passen sich die Felder entsprechend an, da wir alles abhängig von der Breite und Höhe der Canvas berechnen.
- Wir können aber die Fenstergröße aber gleich etwas quadratischer gestalten (z.B.
this.setSize(400, 400);).
- Damit wir immer zwischen Grau und Weiß umschalten, erstellen wir uns eine boole'sche Variable
- Wir laufen also durch das
-
Jetzt kümmern wir uns um die roten Punkte inmitten des Rechtecks, falls wir mit der Maus darauf geklickt haben. Dazu benötigen wir den
MouseListener, denn es geht um einen Mausklick (also entwedermouseClicked()odermousePressed()) und nicht um eine Bewegung der Maus. Wir entscheiden uns fürmouseClicked()- das ist nun also unser Controller. Wir implementieren zunächst denMouseListenerund prüfen, ob damit alles funktioniert (ob wir z.B. nicht vergessen haben, an denMouseListeneranzumelden.)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
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JFrame; import javax.swing.JPanel; public class Schachbrett extends JFrame implements MouseListener{ Canvas canvas; enum State { MARKIERT, UNMARKIERT }; State[][] field; public Schachbrett() { super(); this.setTitle("Schachbrett"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.initField(); this.canvas = new Canvas(); this.canvas.addMouseListener(this); this.getContentPane().add(this.canvas, BorderLayout.CENTER); // NORTH, EAST, WEST geloescht this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private void initField() { this.field = new State[8][8]; for(int row=0; row<this.field.length; row++) { for(int col=0; col<this.field[row].length; col++) { this.field[row][col] = State.UNMARKIERT; } } } private class Canvas extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); int heightRect = canvasHeight / 8; int widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit einer anderen Farbe anfangen, als aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } g2.fillRect(x, y, widthRect, heightRect); } } } } private JPanel initSouth() { JPanel south = new JPanel(); // hier das JPanel fuer SOUTH befuellen return south; } public static void main(String[] args) { new Schachbrett(); } @Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }-
Wir fügen das
implements MouseListenerim Klassenkopf ein und importieren denMouseListeneraus demjava.awt.event-Paket (lassen wir natürlich Eclipse erledigen). -
Wir lassen Eclipse durch
Add unimplemented methodsdie Methoden aus demMouseListenerhinzufügen. Dabei wird auchMouseEventimportiert. -
Da wir uns für die Implementierung von
mouseClicked()entschieden haben, können wir die anderen Methoden ein wenig verkleinern, um Übersicht zu bewahren. -
In
mouseClicked()können wir schonmal die Koordinaten desMouseEventabfragen und eine Ausgabe auf die Konsole durchführen, um zu kontrollieren, ob alles funktioniert. -
Unbedingt müssen wir natürlich
canvasan denMouseListeneranmelden (Zeile25). -
mouseClicked()wird aufgerufen, wenn wir in das Feld klicken - die Koordinaten des Klicks in dieCanvaswerden auf der Konsole ausgegeben.
-
-
Nun müssten wir uns überlegen, auf welches Rechteck aus dem
field-Array wir geklickt haben. Dieses Rechteck wollen wir alsMARKIERTspeichern. Angenommen, wir kennen die Koordinatenxundydes Mausklicks in dieCanvas. Um nun zu wissen, in welche "Spalte" oder "Zeile" wir geklickt haben, müssen wir die Breite und Höhe der Rechtecke kennen. Diese kennen wir aber inmouseClicked()nicht, da die Breite und Höhe der Rechtecke jeweils von der Breite und Höhe derCanvasabhängig ist (welche sich ändern können). Die Höhe und Breite der Rechtecke ist somit eine Eigenschaft derCanvasund um auf diese Eigenschaft zuzugreifen, müssen wir sie global verfügbar machen: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
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JFrame; import javax.swing.JPanel; public class Schachbrett extends JFrame implements MouseListener{ Canvas canvas; enum State { MARKIERT, UNMARKIERT }; State[][] field; public Schachbrett() { super(); this.setTitle("Schachbrett"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.initField(); this.canvas = new Canvas(); this.canvas.addMouseListener(this); this.getContentPane().add(this.canvas, BorderLayout.CENTER); // NORTH, EAST, WEST geloescht this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private void initField() { this.field = new State[8][8]; for(int row=0; row<this.field.length; row++) { for(int col=0; col<this.field[row].length; col++) { this.field[row][col] = State.UNMARKIERT; } } } private class Canvas extends JPanel { int heightRect = 0; int widthRect = 0; @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); this.heightRect = canvasHeight / 8; this.widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit einer anderen Farbe anfangen, als aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } g2.fillRect(x, y, this.widthRect, this.heightRect); } } } } private JPanel initSouth() { JPanel south = new JPanel(); // hier das JPanel fuer SOUTH befuellen return south; } public static void main(String[] args) { new Schachbrett(); } @Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); int spalte = x / this.canvas.widthRect; int zeile = y / this.canvas.heightRect; this.field[zeile][spalte] = State.MARKIERT; System.out.println("mouseClicked: [zeile = " + zeile + ", spalte = " + spalte + "]"); this.canvas.repaint(); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }-
Die Variablen
heightRectundwidthRectwerden in den Zeilen50und51als global deklariert. -
In den Zeilen
61und62bekommen diese Variablen ihren Wert abhängig von der Größe derCanvas. -
Nun können wir auf diese Werte in der
mouseClicked()-Methode über diecanvas-Referenz zugreifen (Zeilen105und106). Für die Berechnung derzeileundspalteverwenden wir die Integer-Division (wie oft passt ein Rechteck inxbzw. iny?). -
Das entsprechende Feld setzen wir auf
MARKIERT(Zeile108). -
Zur Kontrolle noch eine Konsolenausgabe (Zeile
109). -
Damit wir es nicht vergessen, sicherheitshalber schonmal das
repaint()dercanvas(Zeile110).
-
-
In alle als
MARKIERTmarkierten Felder zeichnen wir nun einen roten Kreis. Gezeichnet wird immer in derpaintComponent():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
@Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); this.heightRect = canvasHeight / 8; this.widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit einer anderen Farbe anfangen, als aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } g2.fillRect(x, y, this.widthRect, this.heightRect); if(Schachbrett.this.field[row][col] == State.MARKIERT) { int abstandX = this.widthRect / 3; int abstandY = this.heightRect / 3; int durchmesser = this.widthRect / 3; g2.setColor(Color.RED); g2.fillOval(x+abstandX, y+abstandY, durchmesser, durchmesser); } } } }-
Da die linke obere "Ecke" des Kreises weiter rechts und weiter unten als die linke obere Ecke des Rechtecks ist, fügen wir noch einen
abstandXund einenabstandYhinzu, der sich jeweils aus dem Drittel der Rechtecksbreite und der Recjteckshöhe ergibt (Zeilen84und85). -
Der Durchmesser des Kreises ist dann ebenfalls ein Drittel (somit bleibt das dritte Dtrittel als Abstand rechts - der Kreis ist recht mittig) - Zeile
86. -
Wir setzen die Farbe auf rot und zeichnen den Kreis (Zeilen
87und88).

-
-
Die roten vertikalen und horizontalen Linien könnten wir nun auch noch gleich zeichnen:
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
@Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); this.heightRect = canvasHeight / 8; this.widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit einer anderen Farbe anfangen, als aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } g2.fillRect(x, y, this.widthRect, this.heightRect); } } for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; if(Schachbrett.this.field[row][col] == State.MARKIERT) { int abstandX = this.widthRect / 3; int abstandY = this.heightRect / 3; int durchmesser = this.widthRect / 3; g2.setColor(Color.RED); g2.fillOval(x+abstandX, y+abstandY, durchmesser, durchmesser); g2.setStroke(new BasicStroke(3.0f)); g2.drawLine((this.widthRect / 2), y + (this.heightRect / 2), canvasWidth - (this.widthRect / 2), y + (this.heightRect / 2)); g2.drawLine(x+ (this.widthRect / 2), (this.heightRect / 2), x + (this.widthRect / 2), canvasHeight - (this.heightRect / 2)); } } } }-
Das Zeichnen passiert jetzt in zwei
for-Schleifen. Zunächst werden die grauen und weißen Rechtecke gezeichnet und erst danach die roten Kreise und Linien. Hätten wir auch die Linien in der erstenfor-Schleife gezeichnet, wären sie teilweise von den nachfolgenden Rechtecken abgedeckt und somit nur teilweise sichtbar gewesen. -
Das Zeichnen der beiden Linien (vertikal und horizontal) erfolgt in den Zeilen
99-101. Zunächst wird die Strichstärke etwas erhöht (auf3.0fpx). -
In Zeile
100wird die horizontale Linie gezeichnet. Diese Linie könnte auch beix=0beginnen und beicanvasWidthaufhören. Um sie in der Mitte des ersten Rechtecks beginnen zu lassen, wird stattx=0x=(this.rectWidth/2)gewählt. Um sie in der Mitte des letzten Rechtecks enden zu lassen, wird stattx=canvasWidthx=canvasWidth-(this.rectWidth/2)gewählt. In welcher Zeile diese Linie gezeichnet wird, ergibt sich aus dem Wert vony. Dieser Wert bestimmt aber die oberste Kante der Zeile. Um die Linie in die Mitte der Zeile zu zeichnen, wird jeweilsthis.heightRect/2zuyaddiert. -
Das Zeichnen der vertikalen Linie in Zeile
101ist ganz ähnlich, nur dass der Wert vonxbestimmt, in welche Spaqlte die Linie gezeichnet wird und diey-Werte jeweils so angepasst werden, dass die Linie nicht ganz oben beginnt und ganz oben endet, sondern jeweils mittig im Rechteck.

-
-
Um zu verhindern, dass zwei rote Punkte in derselben Zeile bzw. in derselben Spalte sind, müssen wir im Controller eine weitere Bedingung einbauen, die prüft, ob das angeklickte Feld überhaupt auf
MARKIERTgesetzt werden darf. Dazu muss für eine gegebenezeileund eine gegebenespaltegeprüft werden, ob sich darin bereits einMARKIERTesfieldbefindet. Es wird also diemouseClicked()-Methode angepasst: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
@Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); int spalte = x / this.canvas.widthRect; int zeile = y / this.canvas.heightRect; boolean bereitsMarkiert = false; if(Schachbrett.this.field[zeile][spalte] == State.MARKIERT) { bereitsMarkiert = true; } for(int row=0; row<Schachbrett.this.field.length && !bereitsMarkiert; row++) { if(Schachbrett.this.field[row][spalte] == State.MARKIERT) { bereitsMarkiert = true; } } for(int col=0; col<Schachbrett.this.field[zeile].length && !bereitsMarkiert; col++) { if(Schachbrett.this.field[zeile][col] == State.MARKIERT) { bereitsMarkiert = true; } } if(!bereitsMarkiert) { this.field[zeile][spalte] = State.MARKIERT; } System.out.println("mouseClicked: [zeile = " + zeile + ", spalte = " + spalte + "]"); this.canvas.repaint(); }-
Wir erstellen uns eine Variable
bereitsMarkiert, in der wir uns merken wollen, ob ein bereitsMARKIERTesfieldin derzeileoder in derspalteexistiert (Zeile134). -
In den Zeilen
135-138wird geprüft, ob das Feld selbst inzeileundspaltebereitsMARKIERTist. -
In den Zeilen
140-146wird geprüft, ob in derspalteein bereitsMARKIERTesfieldexistiert. -
In den Zeilen
148-154wird geprüft, ob in derzeileein bereitsMARKIERTesfieldexistiert. -
Nur, wenn keine der drei Bedingungen erfüllt sind, kann das
field[zeile][spalte]aufMARKIERTgesetzt werden. -
Somit wird die Anforderung , dass keine zwei Felder in einer Zeile bzw. in einer Spalte auf
MARKIERTgesetzt sein dürfen, erfüllt. Die Prüfung geschieht "nur" im Controller!
-
-
Wie kann nun dafür gesorgt werden, dass das Feld grün erscheint, wenn es zwar angeklickt wird, aber bereits ein markiertes Feld in der Zeile bzw. der Spalte existiert? Eine einfache Möglichkeit wäre, einfach einen neuen
Statehinzuzufügen, z.B.GRUEN. Wir können uns aber auch zwei globale VariablengruenZeileundgruenSpalteerzeugen und wenn diese jeweils einen Wert besitzen, der innerhalb desfield-Bereiches liegt, dann solll dieses Feld einen grünen Hintergrund bekommen. Es gibt viele Möglichkeiten, wir nehmen mal die erste: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
import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JFrame; import javax.swing.JPanel; public class Schachbrett extends JFrame implements MouseListener{ Canvas canvas; enum State { MARKIERT, UNMARKIERT, GREEN }; State[][] field; public Schachbrett() { super(); this.setTitle("Schachbrett"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.initField(); this.canvas = new Canvas(); this.canvas.addMouseListener(this); this.getContentPane().add(this.canvas, BorderLayout.CENTER); // NORTH, EAST, WEST geloescht this.getContentPane().add(this.initSouth(), BorderLayout.SOUTH); this.setSize(400, 400); this.setLocation(300,200); this.setVisible(true); } private void initField() { this.field = new State[8][8]; for(int row=0; row<this.field.length; row++) { for(int col=0; col<this.field[row].length; col++) { this.field[row][col] = State.UNMARKIERT; } } } private class Canvas extends JPanel { int heightRect = 0; int widthRect = 0; @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // Implementierung von JPanel aufrufen Graphics2D g2 = (Graphics2D)g; // Methoden von Graphics2D nutzbar int canvasHeight = this.getHeight(); int canvasWidth = this.getWidth(); this.heightRect = canvasHeight / 8; this.widthRect = canvasWidth / 8; boolean grey = true; for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; // y-Wert des linken oberen Punktes grey = !grey; // mit einer anderen Farbe anfangen, als aufgehoert for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; // x-Wert des linken oberen Punktes if(grey) { g2.setColor(Color.LIGHT_GRAY); grey = false; } else { g2.setColor(Color.WHITE); grey = true; } if(Schachbrett.this.field[row][col] == State.GREEN) { g2.setColor(Color.GREEN); Schachbrett.this.field[row][col] = State.UNMARKIERT; } g2.fillRect(x, y, this.widthRect, this.heightRect); } } for(int row=0; row<Schachbrett.this.field.length; row++) { int y = row * this.heightRect; for(int col=0; col<Schachbrett.this.field[row].length; col++) { int x = col * this.widthRect; if(Schachbrett.this.field[row][col] == State.MARKIERT) { int abstandX = this.widthRect / 3; int abstandY = this.heightRect / 3; int durchmesser = this.widthRect / 3; g2.setColor(Color.RED); g2.fillOval(x+abstandX, y+abstandY, durchmesser, durchmesser); g2.setStroke(new BasicStroke(3.0f)); g2.drawLine((this.widthRect / 2), y + (this.heightRect / 2), canvasWidth - (this.widthRect / 2), y + (this.heightRect / 2)); g2.drawLine(x+ (this.widthRect / 2), (this.heightRect / 2), x + (this.widthRect / 2), canvasHeight - (this.heightRect / 2)); } } } } } private JPanel initSouth() { JPanel south = new JPanel(); // hier das JPanel fuer SOUTH befuellen return south; } public static void main(String[] args) { new Schachbrett(); } @Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); int spalte = x / this.canvas.widthRect; int zeile = y / this.canvas.heightRect; boolean bereitsMarkiert = false; if(Schachbrett.this.field[zeile][spalte] == State.MARKIERT) { bereitsMarkiert = true; } for(int row=0; row<Schachbrett.this.field.length && !bereitsMarkiert; row++) { if(Schachbrett.this.field[row][spalte] == State.MARKIERT) { bereitsMarkiert = true; } } for(int col=0; col<Schachbrett.this.field[zeile].length && !bereitsMarkiert; col++) { if(Schachbrett.this.field[zeile][col] == State.MARKIERT) { bereitsMarkiert = true; } } if(!bereitsMarkiert) { this.field[zeile][spalte] = State.MARKIERT; } else { this.field[zeile][spalte] = State.GREEN; } System.out.println("mouseClicked: [zeile = " + zeile + ", spalte = " + spalte + "]"); this.canvas.repaint(); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} }- Jetzt wird der
clear field-Button erzeugt und für diesen Button derActionListenerimplementiert. Das kann alles innerhalb derinitSouth()-Methode passieren. Das Löschen des Feldes besteht darin, alle Felder aufUNMARKIERTzu setzen. Das erledigt bereits dieinitField()-Methode. Diese Methode muss also nur aufgerufen werden und danach diecanvasneu gezeichnet:
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
@Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); int spalte = x / this.canvas.widthRect; int zeile = y / this.canvas.heightRect; boolean bereitsMarkiert = false; if(Schachbrett.this.field[zeile][spalte] == State.MARKIERT) { bereitsMarkiert = true; } for(int row=0; row<Schachbrett.this.field.length && !bereitsMarkiert; row++) { if(Schachbrett.this.field[row][spalte] == State.MARKIERT) { bereitsMarkiert = true; } } for(int col=0; col<Schachbrett.this.field[zeile].length && !bereitsMarkiert; col++) { if(Schachbrett.this.field[zeile][col] == State.MARKIERT) { bereitsMarkiert = true; } } if(!bereitsMarkiert) { this.field[zeile][spalte] = State.MARKIERT; } System.out.println("mouseClicked: [zeile = " + zeile + ", spalte = " + spalte + "]"); this.canvas.repaint(); }-
Wir erstellen uns eine Variable
bereitsMarkiert, in der wir uns merken wollen, ob ein bereitsMARKIERTesfieldin derzeileoder in derspalteexistiert (Zeile134). -
In den Zeilen
135-138wird geprüft, ob das Feld selbst inzeileundspaltebereitsMARKIERTist. -
In den Zeilen
140-146wird geprüft, ob in derspalteein bereitsMARKIERTesfieldexistiert. -
In den Zeilen
148-154wird geprüft, ob in derzeileein bereitsMARKIERTesfieldexistiert. -
Nur, wenn keine der drei Bedingungen erfüllt sind, kann das
field[zeile][spalte]aufMARKIERTgesetzt werden. -
Somit wird die Anforderung , dass keine zwei Felder in einer Zeile bzw. in einer Spalte auf
MARKIERTgesetzt sein dürfen, erfüllt. Die Prüfung geschieht "nur" im Controller!
- Jetzt wird der
-
Wie kann nun dafür gesorgt werden, dass das Feld grün erscheint, wenn es zwar angeklickt wird, aber bereits ein markiertes Feld in der Zeile bzw. der Spalte existiert? Eine einfache Möglichkeit wäre, einfach einen neuen
Statehinzuzufügen, z.B.GRUEN. Wir können uns aber auch zwei globale VariablengruenZeileundgruenSpalteerzeugen und wenn diese jeweils einen Wert besitzen, der innerhalb desfield-Bereiches liegt, dann solll dieses Feld einen grünen Hintergrund bekommen. Es gibt viele Möglichkeiten, wir nehmen mal die erste:117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
private JPanel initSouth() { JPanel south = new JPanel(); JButton clearBtn = new JButton("clear field"); clearBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Schachbrett.this.initField(); Schachbrett.this.canvas.repaint(); } }); south.add(clearBtn); return south; } -
Die Überprüfung eines "Konfliktfalls" für die Diagonalen kann ebenfalls in der
mouseClicked()-Methode als zusätzliche Bedingung eingefügt werden. Dazu müssen wir nur die Frage beantworten, wie wir ermitteln können, ob ein Feld mit den Koordinatenzeile1undspalte1auf einer Diagonalen des Feldeszeileundspalteliegt.-
Nehmen wir an,
zeile=3undspalte=4. Dann wären z.B.zeile1=0undspalte1=1auf einer Diagonalen und auchzeile1=1undspalte1=2und auchzeile1=2undspalte1=3und auchzeile1=4undspalte1=5und auchzeile1=5undspalte1=6und auchzeile1=6undspalte1=7. -
Für die andere Diagonale gilt
zeile1=2undspalte1=5und auchzeile1=1undspalte1=6und auchzeile1=0undspalte1=7, aber auchzeile1=4undspalte1=3und auchzeile1=5undspalte1=2und auchzeile1=6undspalte1=1und auchzeile1=7undspalte1=0. -
Es gilt also, dass
| zeile1 - zeile | == | spalte1 - spalte |, damit die Felder inzeile, spalteundzeile1, spalte1in einer Diagonalen liegen.
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
@Override public void mouseClicked(MouseEvent e) { int x = e.getX(); int y = e.getY(); System.out.println("mouseClicked: [x=" + x + ", " + y + "]"); int spalte = x / this.canvas.widthRect; int zeile = y / this.canvas.heightRect; boolean bereitsMarkiert = false; if(Schachbrett.this.field[zeile][spalte] == State.MARKIERT) { bereitsMarkiert = true; } for(int row=0; row<Schachbrett.this.field.length && !bereitsMarkiert; row++) { if(Schachbrett.this.field[row][spalte] == State.MARKIERT) { bereitsMarkiert = true; } } for(int col=0; col<Schachbrett.this.field[zeile].length && !bereitsMarkiert; col++) { if(Schachbrett.this.field[zeile][col] == State.MARKIERT) { bereitsMarkiert = true; } } // Diagonalen for(int row=0; row<Schachbrett.this.field.length && !bereitsMarkiert; row++) { for(int col=0; col<Schachbrett.this.field[row].length && !bereitsMarkiert; col++) { if(!(col==spalte && row==zeile)) // nicht das Feld selbst betrachten { if((Math.abs(col-spalte) == Math.abs(row-zeile)) && Schachbrett.this.field[row][col] == State.MARKIERT) { bereitsMarkiert = true; } } } } if(!bereitsMarkiert) { this.field[zeile][spalte] = State.MARKIERT; } else { this.field[zeile][spalte] = State.GREEN; } System.out.println("mouseClicked: [zeile = " + zeile + ", spalte = " + spalte + "]"); this.canvas.repaint(); } -
Schiebepuzzle¶
Aufgabe Schiebepuzzle
-
Implementieren Sie folgendes Puzzle:

- obige Abbildung zeigt den Anfangszustand des Puzzles (
2über1links)

-
obige Abbildung zeigt den Endzustand des Puzzles (
1über2links) -
Das Puzzle besteht aus 5 „Kacheln“, 3 gelben und 2 roten. Die roten Kacheln sind mit einer
1und einer2nummeriert. Am Anfang steht die rote Kachel mit der2über der roten Kachel mit der1. (siehe erste Abb.) Ziel des Puzzles ist es, die beiden roten Kacheln zu vertauschen. Wenn links die1über der2steht, ist das Puzzle beendet. Wo die gelben Kacheln im Endzustand sind, ist egal (siehe zweite Abb.). Ein Feld ist stets frei (grau dargestellt). In dieses Feld kann eine benachbarte Kachel geschoben werden. -
Zeigen Sie oben die Anzahl der Züge an. Wenn der Endzustand erreicht ist, kann nicht mehr gezogen werden! Der Button
Neustartsetzt alles wieder auf Anfang. -
Die Programmierung bleibt ganz Ihnen überlassen. Zum Schieben einer Kachel auf das leere Feld, klickt man auf die Kachel selbst. Diese „bewegt“ sich dann auf das leere (graue) Feld und hinterlässt ein leeres (graues) Feld (siehe folgende Abb.)

- obige Abbildung zeigt den Anfangszustand des Puzzles (
-
Tipps:
-
Sie benötigen keine
Canvasund kein Überschreiben derpaintComponent()-Methode! -
Am einfachsten bekommt man ein Label in die Mitte eines Panels gesetzt, wenn das Panel im
GridLayout(1,1)(oder sogar parameterlos) ist und Sie für das Labellabel.setHorizontalAlignment(JLabel.CENTER);definieren -
die (Schrift-)Farbe eines Labels setzt man mit
label.setForeground(Color); -
die (Hintergrund-Farbe eines Panels setzt man mit
panel.setBackground(Color); -
für das Identifizieren des entsprechenden Panels, auf das man geklickt hat, könnte es hilfreich sein, sich dafür eine eigene Klasse zu schreiben (die von JPanel erbt). Muss aber nicht.
-
wahrscheinlich ist es am einfachsten, ein zweidimensionales Array 2x3 als Datenstruktur für Ihr Puzzle zu verwenden
-
Lösung Schiebepuzzle
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 | |
Rechtecke anordnen¶
Aufgabe Rechtecke anordnen
-
Implementieren Sie folgende GUI:

- die Abbildung zeigt die Ausgangssituation

- Die Zeichenfläche ist in 4 gleichgroße Teile unterteilt. Diese Unterteilung wird durch Linien dargestellt.
-
Durch Drücken des Buttons
rectangleerscheint (beim ersten Mal ein grünes) Rechteck. Dieses Rechteck kann dann mit gedrückter Maustaste verschoben werden. Sobald es im linken oberen Feld ist, wird es dort fixiert und kann nicht mehr bewegt werden.
- die obige Abbildung zeigt die Situation, in der das grüne Rechteck fixiert ist und nicht mehr bewegt werden kann
-
Nachdem das grüne (erste) Rechteck fixiert ist, kann der Button
rectangleein weiteres Mal gedrückt werden. Es erscheint ein rotes Rechteck. Das rote Rechteck soll in das rechte obere Feld.
-
Nachdem auch das rote Rechteck fixiert ist, kann der Button
rectangleein weiteres Mal gedrückt werden. Es erscheint ein gelbes Rechteck. Das gelbe Rechteck soll in das linke untere Feld.
-
Nun kann der Button
rectangleein weiteres Mal gedrückt werden. Es erscheint ein dunkelgraues Rechteck. Das dunkelgraue Rechteck soll in das rechte untere Feld.
-
Wenn auch das graue Rechteck fixiert ist, ist die Endesituation erreicht (siehe Label oben in der folgenden Abbildung):

-
Nach Klick auf den Button
clearwird wieder die Ausgangssituation hergestellt:
Lösung Rechtecke anordnen
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 | |
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 | |