Γιορτινή κάρτα
Ολοκληρωμένο παράδειγμα
- Κληρονομικότητα, δυναμική διεκπεραίωση
- Συλλογές
- Νήματα
- Γραφικά AWT
- Ήχος MIDI
- Τεκμηρίωση με JavaDoc
Διάγραμμα των κλάσεων
XmasCard
package gr.aueb.xmascard;
import java.awt.Rectangle;
/**
* The Christmas Card program main class.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @depend - - - gr.aueb.xmascard.DrawPanel
* @depend - <instantiate> - gr.aueb.xmascard.MidiPlayer
* @depend - - - gr.aueb.xmascard.Tree
* @depend - - - gr.aueb.xmascard.PointSnowFlake
* @depend - - - gr.aueb.xmascard.SlashSnowFlake
*/
public class XmasCard {
/** Number of trees */
private static final int numTrees = 30;
/** Number of snowflakes */
private static final int numSnowFlakes = 1500;
/** Minimum tree width. */
private static final int treeWidth = 30;
/** Minimum tree height. */
private static final int treeHeight = 100;
/** Additional variation to tree height and width */
private static final int treeWobble = 100;
/** Song to play. */
private static String musicFile = "jbelrock.mid";
public static void main(String[] args) {
// Create a window and the canvas to draw onto.
DrawPanel d = new DrawPanel();
// Create randomly-positioned trees.
for (int i = 0; i < numTrees; i++) {
Rectangle treeBox = new Rectangle(
(int)(Math.random() * DrawPanel.WIDTH),
(int)(Math.random() * DrawPanel.HEIGHT),
treeWidth + (int)(Math.random() * treeWobble),
treeHeight + (int)(Math.random() * treeWobble));
Tree t = new Tree(d.getCanvas(), treeBox);
d.addDrawObject(t);
}
// Start playing music
MidiPlayer m = new MidiPlayer(musicFile);
// Create the snowflakes.
for (int i = 0; i < numSnowFlakes; i++) {
switch (i % 6) {
case 0:
case 1:
d.addDrawObject(new PointSnowFlake(d.getCanvas(), '.', 15));
break;
case 2:
d.addDrawObject(new PointSnowFlake(d.getCanvas(), 'o', 10));
break;
case 3:
d.addDrawObject(new PointSnowFlake(d.getCanvas(), '*', 5));
break;
case 4:
case 5:
d.addDrawObject(new SlashSnowFlake(d.getCanvas()));
break;
}
try {
// Allow existing snowflakes to fall a bit, before adding more
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}
DrawPanel
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Color
;
import java
.awt
.Dimension
;
import java
.awt
.event
.WindowAdapter
;
import java
.awt
.event
.WindowEvent
;
import java
.util
.Vector
;
import javax
.swing
.JFrame
;
import javax
.swing
.JPanel
;
/**
* The program's main window.
* Extends JFrame to display the window where the
* trees and snow are drawn. Implements the {@link java.lang.Runnable Runnable}
* interface so as to create a thread that repeatedly calls the
* {@link gr.aueb.card.Drawable#draw() draw}method.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @opt nodefillcolor lightblue
* @assoc 1 drawablePanel 1 DrawablePanel
*/
public class DrawPanel
extends JFrame
implements Runnable
{
/** The window's width. */
public static final int WIDTH
= 1024;
/** The window's height. */
public static final int HEIGHT
= 768;
/** The window's background color (blue). */
public static final Color BACKGROUND_COLOR
= new Color(0, 153, 204);
/* A table that holds the objects to be drawn */
private Vector
<Drawable
> drawObjects
= null;
/* The drawing thread (not serializable) */
private transient Thread thread
;
/* The canvas to draw onto */
private DrawablePanel drawablePanel
= null;
/** Serial number of persistant data.
* Required, because JFrame implements serializable.
*/
static final long serialVersionUID
= 1L;
/**
* Constructor to initialize an object with the minimal required state.
* The constructor is private, as the full initialization is done
* in the getInstance method.
*/
private DrawPanel() {
super("Holiday Card");
}
/**
* Field initialization based on a constructed instance
*/
private void initialize() {
drawObjects
= new Vector
<Drawable
>();
initializeGraphics();
initializeThread();
}
/**
* Initialize, display the window, and start the animation.
* The code here is separate from the constructor in order to
* avoid the resulting "this escape".
*/
public static DrawPanel
getInstance() {
DrawPanel instance
= new DrawPanel();
instance
.initialize();
return instance
;
}
/** Initialize the main window. */
private void initializeGraphics() {
// Make our window look nice
JFrame
.setDefaultLookAndFeelDecorated(true);
// Create our drawing canvas
drawablePanel
= new DrawablePanel(this);
drawablePanel
.setBackground(BACKGROUND_COLOR
);
drawablePanel
.setPreferredSize(new Dimension(WIDTH
, HEIGHT
));
setContentPane(drawablePanel
);
// Handle termination
setDefaultCloseOperation(
javax
.swing
.WindowConstants
.DISPOSE_ON_CLOSE
);
// Exit when the window is closed
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e
) {
System
.exit(0);
}
});
// Our size
setSize(WIDTH
, HEIGHT
);
// Force the parent window to expand the canvas to all available space
pack();
//Display the window
setVisible(true);
}
/** Start the execution of the drawing thread. */
private void initializeThread() {
if (thread
== null) {
thread
= new Thread(this);
thread
.setPriority(Thread
.MIN_PRIORITY
);
thread
.start();
}
}
/** Add a component to be drawn. */
public void addDrawObject(Drawable drawObject
) {
drawObjects
.add(drawObject
);
}
/** Return a copy of the component list to be drawn */
public Vector
<Drawable
> getDrawables() {
return new Vector
<Drawable
>(drawObjects
);
}
/**
* The method to be executed by the running thread. Executes the
* {@link DrawablePanel#repaint()}method periodically.
*/
public void run() {
Thread me
= Thread
.currentThread();
// Allow termination by setting thread to null
while (thread
== me
) {
// tell drawablePanel to repaint its contents
drawablePanel
.repaint();
try {
Thread
.sleep(250);
} catch (InterruptedException e
) {
}
}
thread
= null;
}
/**
* Get the canvas's drawing panel
*
* @return javax.swing.JPanel
*/
public JPanel
getCanvas(){
return drawablePanel
;
}
}
Drawable
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import javax
.swing
.JPanel
;
import java
.awt
.Graphics
;
import java
.awt
.Graphics2D
;
import java
.awt
.Rectangle
;
/**
* An abstract representation of a self-drawable object.
*
* @author Giorgos Gousios, Diomidis Spinellis
*/
public abstract class Drawable
{
/**
* The canvas to draw the object onto
*/
protected Graphics2D canvas
;
/**
* The canvas's bounds
*/
protected Rectangle bounds
;
/**
* Create drawable item
*
* @param panel The panel to draw the object onto
*/
public Drawable(JPanel panel
) {
bounds
= panel
.getBounds();
canvas
= (Graphics2D
)panel
.getGraphics();
}
/**
* Draws the object onto the canvas
*
*/
public abstract void draw(Graphics g
);
}
DrawablePanel
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Color
;
import java
.awt
.Graphics
;
import java
.util
.Vector
;
import javax
.swing
.JPanel
;
/**
* The Holiday Card program main class.
*
* @author Georgios Zouganelis
* Draw components from this object to reduce flickering.
*/
public class DrawablePanel
extends JPanel
{
/** The DrawPanel this DrawablePanel is attached to **/
private DrawPanel controller
= null;
/** Serial number of persistent data.
* Required, because JPanel implements serializable.
*/
private static final long serialVersionUID
= 1L;
/**
* Constructor to initialize the DrawablePanel with it's controller
*
*/
public DrawablePanel(DrawPanel panel
) {
controller
= panel
;
}
/**
* Perform all drawing operations
* By overriding the JPanel method and initiating all the drawing
* from this place we take advantage of JPanel's double-buffering
* capability.
*/
@Override
public void paintComponent(Graphics g
){
super.paintComponent(g
);
setBackground(DrawPanel
.BACKGROUND_COLOR
);
// Ask our controller for a copy of items to draw
Vector
<Drawable
> toPaint
= controller
.getDrawables();
for (Drawable d
: toPaint
)
d
.draw(g
);
}
}
Tree
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Color
;
import java
.awt
.Graphics
;
import java
.awt
.Polygon
;
import java
.awt
.Rectangle
;
import javax
.swing
.JPanel
;
/**
* A self-drawable tree. Uses a box to specify the tree's bounds (the dimensions
* constructor parameter). The trunk is placed in the middle of the bottom side
* of the box, having a width equal to the 8% of the total width of the tree and
* a height equal to the 20% of the total height of the bounding box. The main
* body is represented as an isosceles triangle with a height of 80% of the
* height of the bounding box.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @opt nodefillcolor green
*/
public class Tree
extends Drawable
{
/** Tree trunk width as % of the bounding rectangle width */
private final double TRUNK_WIDTH_FACTOR
= 0.08;
/** Tree trunk height as % of the bounding rectangle height */
private final double TRUNK_HEIGHT_FACTOR
= 0.2;
/** Tree body height as % of the bounding rectangle height */
private final double BODY_HEIGHT_FACTOR
= 0.8;
/** Trunk's color (RGB) */
private final Color BROWN
= new Color(204, 102, 0);
/** Body's color (RGB) */
private final Color GREEN
= new Color(0, 254, 0);
/** Tree balls' color (RGB) */
private final Color RED
= new Color(250, 0, 0);
/** The tree's bounding rectangle */
private Rectangle dimensions
;
/**
* Creates a tree from the specified bounding box
*
* @param panel The panel to draw the object onto
* @param dimensions The bounding box dimensions.
*/
public Tree(JPanel panel
, Rectangle dimensions
) {
super(panel
);
this.dimensions
= dimensions
;
}
/**
* Draws the tree.
*
* @param g The Graphics object on which we will paint
*/
@Override
public void draw(Graphics g
) {
drawTrunk(g
);
drawBody(g
);
}
/**
* Draws the trunk. For details on how the lengths are calculated
*
* @param g The Graphics object on which we will paint
* @see gr.aueb.Tree the class description.
*/
private void drawTrunk(Graphics g
) {
/* Calculate the trunk rectangle first */
Rectangle r
= new Rectangle();
r
.x
= (int) (dimensions
.x
+ (dimensions
.width
- dimensions
.width
* TRUNK_WIDTH_FACTOR
) / 2);
r
.y
= (int) (dimensions
.y
+ dimensions
.height
* BODY_HEIGHT_FACTOR
);
r
.width
= (int) (dimensions
.width
* TRUNK_WIDTH_FACTOR
);
r
.height
= (int) (dimensions
.height
* TRUNK_HEIGHT_FACTOR
);
/* Draw it! */
g
.drawRect(r
.x
, r
.y
, r
.width
, r
.height
);
/* Fill it with brown color */
Color c
= g
.getColor();
g
.setColor(BROWN
);
g
.fillRect(r
.x
, r
.y
, r
.width
, r
.height
);
g
.setColor(c
); //Revert paint color to default
}
/**
* Draws the body. For details on how the lengths are calculated
*
* @param g The Graphics object on which we will paint
* @see gr.aueb.Tree the class description.
*/
private void drawBody(Graphics g
) {
/* Create the polygon (triangle) to draw */
Polygon p
= new Polygon();
p
.addPoint(dimensions
.x
+ dimensions
.width
/ 2, dimensions
.y
);
p
.addPoint(dimensions
.x
,
(int) (dimensions
.y
+ dimensions
.height
* BODY_HEIGHT_FACTOR
));
p
.addPoint(dimensions
.x
+ dimensions
.width
,
(int) (dimensions
.y
+ dimensions
.height
* BODY_HEIGHT_FACTOR
));
/* Draw the body */
g
.drawPolygon(p
);
/* Fill it with green color */
Color c
= g
.getColor();
g
.setColor(GREEN
);
g
.fillPolygon(p
);
g
.setColor(c
); // Revert paint color to default
/* Set Ornaments to the body. */
drawTreeOrnaments(g
);
}
/**
* Draws the ornaments of the tree.
* @param g The Graphics object on which we will paint
* @param x The Abscissa of the part of the body to draw the ornament
* @param y The Ordinate of the part of the body to draw the ornament
*/
private void addTreeOrnament(Graphics g
, int x
, int y
) {
/* Draw Tree Ornament. */
g
.drawOval(x
, y
, 10, 10);
/* Set color to Red. */
g
.setColor(RED
);
/* Fill Tree Ornament with color. */
g
.fillOval(x
, y
, 10, 10);
}
/**
* Calls addTreeOrnament for specific locations on
* the tree body.
* @param g The Graphics object on which we will paint
*/
private void drawTreeOrnaments(Graphics g
) {
/* yAxis of the body. */
int yAxis
= (int) (dimensions
.y
+ dimensions
.height
* BODY_HEIGHT_FACTOR
);
/* Add ornament to down left. */
addTreeOrnament(g
, dimensions
.x
- 2, yAxis
- 2);
/* Add ornament to down right. */
addTreeOrnament(g
, dimensions
.x
+ dimensions
.width
- 1, yAxis
- 1);
/* Add ornament to up left. */
addTreeOrnament(g
, dimensions
.x
+ dimensions
.width
/ 2 - 5 - 20, yAxis
- 25);
/* Add ornament to up right. */
addTreeOrnament(g
, dimensions
.x
+ dimensions
.width
/ 2 - 5 + 20, yAxis
- 25);
/* Add ornament to middle. */
addTreeOrnament(g
,dimensions
.x
+ dimensions
.width
/ 2 - 5, yAxis
- 65);
}
}
SnowFlake
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Color
;
import java
.awt
.FontMetrics
;
import java
.awt
.Graphics
;
import javax
.swing
.JPanel
;
/**
* A self-drawable 'snowflake' represented by a character. The move pattern and
* character to be displayed is determined by subclasses.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @opt nodefillcolor white
*/
public abstract class SnowFlake
extends Drawable
{
/** The snowflake's background color. */
private static final Color WHITE
= new Color(255, 255, 255);
/**
* The 'x' current coordinate of the snowflake.
*/
protected int coordX
;
/**
* The 'y' current coordinate of the snowflake.
*/
protected int coordY
;
/**
* The character to be displayed as a snowflake
*/
protected char displayChar
;
/**
* Create a snowflake represented by a point-like character.
*
* @param panel The panel to draw the object onto
*/
public SnowFlake(JPanel panel
) {
super(panel
);
coordX
= (int) (bounds
.width
* Math
.random()) + bounds
.x
;
coordY
= 0;
}
/**
* Draw the snowflake and wrap around.
*
* @param g The Graphics object on which we will paint
*/
@Override
public void draw(Graphics g
) {
// Go back to the top when hitting the bottom
if (coordY
>= bounds
.height
+ bounds
.y
)
coordY
= 0;
// Draw the character in white
g
.setColor(WHITE
);
g
.drawString((Character
.valueOf(displayChar
)).toString(),
coordX
, coordY
);
}
}
SlashSnowFlake
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Graphics
;
import javax
.swing
.JPanel
;
/**
* A class that animates a slash on a canvas.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @opt nodefillcolor white
*/
public class SlashSnowFlake
extends SnowFlake
{
/**
* Create a snowflake represented by a slash.
*
* @param panel The panel to draw the object onto
*/
public SlashSnowFlake(JPanel panel
) {
super(panel
);
displayChar
= '/';
}
/**
* Display the slash on the drawing canvas. The slash alternates between
* forward slash and backslash depending on the current 'y' coordinate.
*
* @param g The Graphics object on which we will paint
*/
@Override
public void draw(Graphics g
) {
/* / on even lines, \ on odd lines */
displayChar
= ((coordY
% 2) == 0) ?
'/' : '\\';
/* Move by 0 to 10 pixels down*/
coordY
+= (int) (Math
.random() * 10);
// Draw it through the superclass
super.draw(g
);
}
}
PointSnowFlake
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import java
.awt
.Graphics
;
import javax
.swing
.JPanel
;
/**
* A class that animates a point-like character on a canvas.
* The character can be e.g. a . or a * or an o.
*
* @author Giorgos Gousios, Diomidis Spinellis
* @opt nodefillcolor white
*/
public class PointSnowFlake
extends SnowFlake
{
/** The wieght of the snowflake. */
int weight
;
/**
* Create a snowflake represented by a point-like character.
*
* @param panel The panel to draw the object onto
* @param c The character to draw
* @param w The snowflake's weight
*/
public PointSnowFlake(JPanel panel
, char c
, int w
) {
super(panel
);
displayChar
= c
;
weight
= w
;
}
/**
* Display the star onto the canvas. The star changes its 'x' coordinate,
* depending on the 'y' coordinate.
*
* @param g The Graphics object on which we will paint
*/
@Override
public void draw(Graphics g
) {
// Move the snowflake left and right
switch (coordY
% 3) {
case 1:
coordX
= coordX
- 5;
break;
case 2:
coordX
= coordX
+ 5;
break;
default:
break;
}
// Move down, based on the weight
coordY
+= (int)(Math
.random() * weight
);
// Draw it through the superclass
super.draw(g
);
}
}
MidiPlayer
/*-
* Copyright 2005-2018 Diomidis Spinellis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gr
.aueb
.card
;
import javax
.sound
.midi
.*;
import java
.io
.InputStream
;
import java
.io
.IOException
;
/**
* Play the specified MIDI file
* Note:
* For this to work you must ensure that the computer's mixer
* is configured to play the software synhtesizer output.
*
* @author Diomidis Spinellis
*/
public class MidiPlayer
{
/** The sequencer we are using to play the MIDI data. */
static Sequencer sequencer
= null;
/** Constructor for playing the specified file. */
MidiPlayer(String file
) {
playFile(file
);
}
/** Play the specified file. */
public void playFile(String file
) {
InputStream midiFile
= getClass().getResourceAsStream(file
);
try {
if (sequencer
== null)
sequencer
= MidiSystem
.getSequencer();
else
end();
sequencer
.setSequence(MidiSystem
.getSequence(midiFile
));
sequencer
.open();
sequencer
.start();
} catch(MidiUnavailableException e
) {
System
.err
.println("Midi device unavailable:" + e
);
} catch(InvalidMidiDataException e
) {
System
.err
.println("Invalid MIDI data:" + e
);
} catch(IOException e
) {
System
.err
.println("I/O error:" + e
);
}
}
/** Return true if the music is still playing. */
public boolean isPlaying() {
return sequencer
.isRunning();
}
/* Stop playing. */
public void end() {
sequencer
.stop();
sequencer
.close();
sequencer
= null;
}
}
Τεκμηρίωση
Τα σχόλια μέσα στον κώδικα επιτρέπουν την αυτόματη δημιουργία της
τεκμηρίωσης, με τη χρήση του προγράμματος
javadoc.
Προαιρετική άσκηση
Με βάση τον κώδικα της κάρτας υλοποιήστε ένα παιγνίδι
(π.χ. απόφυγε το χιόνι, ή μάζεψε τις μπάλες, ή πιάσε τον τάρανδο, κ.λπ.).
- Η υλοποίηση αυτή μπορεί να γίνει από ομάδες αποτελούμενες από μέχρι
δύο άτομα.
- Για να υπολογιστεί η προσπάθειά σας στο βαθμό του μαθήματος
θα πρέπει να μπορείτε να επιδείξετε το αποτέλεσμα και τον
αντίστοιχο πηγαίο κώδικα στο τέλος του μαθήματος της Δευτέρας
8-1-2024.
- Μαζί σας θα πρέπει να έχετε και μια σελίδα με το όνομα και τον ΑΜ
του μέλους ή των μελών της ομάδας,
όπου θα αναφέρετε περιληπτικά τι προσπαθήσατε να επιτύχετε και τι
αλλαγές κάνατε στον κώδικα.
Πληροφορίες για την άσκηση
-
Μπορείτε να κατεβάσετε τον πηγαίο κώδικα του παραδείγματος από
εδώ (https://github.com/dspinellis/holiday-card).
- Οδηγίες μεταγλώττισης και εκτέλεσης υπάρχουν στο αρχείο README.md
-
Μπορείτε να δείτε πατήματα πλήκτρων του πληκτρολογίου μέσω της κλάσης
java.awt.event.KeyEvent
.
Περιεχόμενα