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
JPanel
imCENTER
des 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 Buttonsnew
undfill
.
- Es gibt also eine Zeichenfläche (
-
Nach dem Klicken des Buttons
new
soll ein schwarzes unausgefülltes Quadrat mit der Strichstärke3.0
so 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
new
steht darin nicht mehrnew
, sondernrefresh
.
-
-
Nach Klicken des Buttons
fill
soll 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 aufunfill
soll 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
fill
undunfill
ändert sich die Farbe zum Befüllen des Quadrats nicht, -
erst durch Klicken des Button
refresh
wird eine neue Farbe erzeugt, die dann wieder bis zum nächsten Klicken vonrefresh
bleibt.
-
-
Implementieren Sie den
MouseListener
und denMouseMotionListener
so, 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
10px
von 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
ActionListener
fü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 VariablenzeigeQuadrat
an, 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
zeigeQuadrat
durch den Buttonklick auftrue
gesetzt 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
Canvas
eingepasst, 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
Canvas
eingepasst, 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 nurzeigeQuadrat
auftrue
gesetzt, sondern dieCanvas
auch neu gezeichnet (Zeile77
)! -
Die Quelle des
ActionEvent
kann nur der Button selbst sein, da wir eine anonyme Klasse zur Implementierung desActionListener
verwenden. Deshalb können wir die Quelle ohne Prüfung in einenJButton
konvertieren (Zeile75
). Mithilfe dersetText()
-methode fürJButton
setzen wir den neuen Buttontext aufrefresh
(Zeile76
).
-
-
Die Implementierung des
ActionListener
fü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
61
bzw.73
) fallsfuelleQuadrat
den Werttrue
hat (Zeile59
bzw.71
). -
Nach dem Zeichnen des befüllten Quadrates wird die Zeichenfarbe wieder auf schwarz gestellt (Zeile
63
bzw.75
) und dann das nichtausgefüllte Quadrat gezeichnet. -
In der
actionPerformed()
_Methode müssen wir unterscheiden, ob im Buttonfill
steht oderunfill
(zeile108
). Je nachdem wirdfuelleQuadrat
auftrue
oderfalse
gesetzt 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
MouseListener
und desMouseMotionListener
benö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
MouseListener
undMouseMotionListener
und meldencanvas
daran 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
MouseListener
undMouseMotionListener
und meldencanvas
daran 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 (warX
oderwarY
) 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()
) wirdbewegt
wieder auffalse
gesetzt 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
MausListener
so, 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
JPanel
hinzu. DiesesJPanel
enthä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
Canvas
zeichnen. 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
boolean
genügen - markiert oder nicht markiert. Wenn Sie sich nicht sicher sind, ob zwei Zustände genügen, können Sie auch eineenum
als Typ verwenden. Wir machen das mal, dann bleibt die Lösung flexibler und wir übenenum
nochmal.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
field
auch 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 abwechselndtrue
undfalse
wird, 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
61
hinzufü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 denMouseListener
und prüfen, ob damit alles funktioniert (ob wir z.B. nicht vergessen haben, an denMouseListener
anzumelden.)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 MouseListener
im Klassenkopf ein und importieren denMouseListener
aus demjava.awt.event
-Paket (lassen wir natürlich Eclipse erledigen). -
Wir lassen Eclipse durch
Add unimplemented methods
die Methoden aus demMouseListener
hinzufügen. Dabei wird auchMouseEvent
importiert. -
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 desMouseEvent
abfragen und eine Ausgabe auf die Konsole durchführen, um zu kontrollieren, ob alles funktioniert. -
Unbedingt müssen wir natürlich
canvas
an denMouseListener
anmelden (Zeile25
). -
mouseClicked()
wird aufgerufen, wenn wir in das Feld klicken - die Koordinaten des Klicks in dieCanvas
werden auf der Konsole ausgegeben.
-
-
Nun müssten wir uns überlegen, auf welches Rechteck aus dem
field
-Array wir geklickt haben. Dieses Rechteck wollen wir alsMARKIERT
speichern. Angenommen, wir kennen die Koordinatenx
undy
des 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 derCanvas
abhängig ist (welche sich ändern können). Die Höhe und Breite der Rechtecke ist somit eine Eigenschaft derCanvas
und 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
heightRect
undwidthRect
werden in den Zeilen50
und51
als global deklariert. -
In den Zeilen
61
und62
bekommen 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 (Zeilen105
und106
). Für die Berechnung derzeile
undspalte
verwenden wir die Integer-Division (wie oft passt ein Rechteck inx
bzw. 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
MARKIERT
markierten 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
abstandX
und einenabstandY
hinzu, der sich jeweils aus dem Drittel der Rechtecksbreite und der Recjteckshöhe ergibt (Zeilen84
und85
). -
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
87
und88
).
-
-
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.0f
px). -
In Zeile
100
wird die horizontale Linie gezeichnet. Diese Linie könnte auch beix=0
beginnen und beicanvasWidth
aufhören. Um sie in der Mitte des ersten Rechtecks beginnen zu lassen, wird stattx=0
x=(this.rectWidth/2)
gewählt. Um sie in der Mitte des letzten Rechtecks enden zu lassen, wird stattx=canvasWidth
x=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/2
zuy
addiert. -
Das Zeichnen der vertikalen Linie in Zeile
101
ist ganz ähnlich, nur dass der Wert vonx
bestimmt, 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
MARKIERT
gesetzt werden darf. Dazu muss für eine gegebenezeile
und eine gegebenespalte
geprüft werden, ob sich darin bereits einMARKIERT
esfield
befindet. 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 bereitsMARKIERT
esfield
in derzeile
oder in derspalte
existiert (Zeile134
). -
In den Zeilen
135-138
wird geprüft, ob das Feld selbst inzeile
undspalte
bereitsMARKIERT
ist. -
In den Zeilen
140-146
wird geprüft, ob in derspalte
ein bereitsMARKIERT
esfield
existiert. -
In den Zeilen
148-154
wird geprüft, ob in derzeile
ein bereitsMARKIERT
esfield
existiert. -
Nur, wenn keine der drei Bedingungen erfüllt sind, kann das
field[zeile][spalte]
aufMARKIERT
gesetzt werden. -
Somit wird die Anforderung , dass keine zwei Felder in einer Zeile bzw. in einer Spalte auf
MARKIERT
gesetzt 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
State
hinzuzufügen, z.B.GRUEN
. Wir können uns aber auch zwei globale VariablengruenZeile
undgruenSpalte
erzeugen 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 derActionListener
implementiert. Das kann alles innerhalb derinitSouth()
-Methode passieren. Das Löschen des Feldes besteht darin, alle Felder aufUNMARKIERT
zu setzen. Das erledigt bereits dieinitField()
-Methode. Diese Methode muss also nur aufgerufen werden und danach diecanvas
neu 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 bereitsMARKIERT
esfield
in derzeile
oder in derspalte
existiert (Zeile134
). -
In den Zeilen
135-138
wird geprüft, ob das Feld selbst inzeile
undspalte
bereitsMARKIERT
ist. -
In den Zeilen
140-146
wird geprüft, ob in derspalte
ein bereitsMARKIERT
esfield
existiert. -
In den Zeilen
148-154
wird geprüft, ob in derzeile
ein bereitsMARKIERT
esfield
existiert. -
Nur, wenn keine der drei Bedingungen erfüllt sind, kann das
field[zeile][spalte]
aufMARKIERT
gesetzt werden. -
Somit wird die Anforderung , dass keine zwei Felder in einer Zeile bzw. in einer Spalte auf
MARKIERT
gesetzt 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
State
hinzuzufügen, z.B.GRUEN
. Wir können uns aber auch zwei globale VariablengruenZeile
undgruenSpalte
erzeugen 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 Koordinatenzeile1
undspalte1
auf einer Diagonalen des Feldeszeile
undspalte
liegt.-
Nehmen wir an,
zeile=3
undspalte=4
. Dann wären z.B.zeile1=0
undspalte1=1
auf einer Diagonalen und auchzeile1=1
undspalte1=2
und auchzeile1=2
undspalte1=3
und auchzeile1=4
undspalte1=5
und auchzeile1=5
undspalte1=6
und auchzeile1=6
undspalte1=7
. -
Für die andere Diagonale gilt
zeile1=2
undspalte1=5
und auchzeile1=1
undspalte1=6
und auchzeile1=0
undspalte1=7
, aber auchzeile1=4
undspalte1=3
und auchzeile1=5
undspalte1=2
und auchzeile1=6
undspalte1=1
und auchzeile1=7
undspalte1=0
. -
Es gilt also, dass
| zeile1 - zeile | == | spalte1 - spalte |
, damit die Felder inzeile, spalte
undzeile1, spalte1
in 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
über1
links)
-
obige Abbildung zeigt den Endzustand des Puzzles (
1
über2
links) -
Das Puzzle besteht aus 5 „Kacheln“, 3 gelben und 2 roten. Die roten Kacheln sind mit einer
1
und einer2
nummeriert. 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 der2
steht, 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
Neustart
setzt 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
Canvas
und 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
rectangle
erscheint (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
rectangle
ein 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
rectangle
ein weiteres Mal gedrückt werden. Es erscheint ein gelbes Rechteck. Das gelbe Rechteck soll in das linke untere Feld. -
Nun kann der Button
rectangle
ein 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
clear
wird 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 |
|