The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.

Introduction to Programming Java Language

Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by ปิติ เกตุ, 2021-03-22 04:12:52

Introduction to Programming Java Language

Introduction to Programming Java Language

บทที่ 9: Thread intro. to Java (FEU.faa)

ผอู านจะสังเกตเห็นวาเราไดตัดเอา code ของการเปรยี บเทียบ boolean ท่ีวา !occupied ออก
จาก while/loop และจากการกาํ หนดหลงั จากนนั้ ซึ่งทําให thread ทัง้ สองกลุม (deposit และ
withdraw) ไมส ามารถทีจ่ ะทาํ งานในสวนของตนตอ ไปได เกดิ การรอคอยซ่ึงกนั และกัน เราเรยี ก
สิ่งทีเ่ กดิ ขึน้ น้ีวา deadlock กระบวนการในการแกป ญหาเร่อื ง deadlock เปน หัวขอ ที่สําคัญ
อันหนงึ่ ในเร่ืองของระบบปฏบิ ตั กิ าร การ synchronize เปน กระบวนการท่สี ามารถนาํ มาใช
แกปญ หาของ deadlock ได

สรุป

เราไดพ ูดถึงการสรา ง thread ในทัง้ สองรปู แบบตลอดจนถึงการใช thread ในงานทางดา น
การเงนิ ซ่ึงโดยรวมแลว เราไดพ ดู ถึง

9 การสรา ง thread จาก Runnable และจาก Thread
9 การประสานเวลาของ thread
9 ตวั อยา งการประสานเวลาของ thread

แบบฝก หัด

1. จงเขียนโปรแกรมทดสอบการสราง thread จํานวนสิบตัว

2. จงหาขอมลู ของการประสานเวลาระหวา ง producer และ consumer ในการผลิตและบรโิ ภค
ขอมลู ชนิดหนึ่ง (อะไรก็ได) ซ่งึ สามารถหาไดจ ากหนังสอื ทเ่ี กีย่ วกบั เรอ่ื งระบบปฏบิ ตั กิ าร
(Operating System) เมอื่ ไดแ ลวใหเ ขียนโปรแกรมแกป ญ หาเร่อื ง deadlock ระหวาง
producer 1 คนและ consumer 1 คน

3. จากโจทยในขอสอง จงเขยี นโปรแกรมแกปญหาถา จํานวนของ producer และ consumer
มมี ากกวาหนึ่งคน

4. การแกปญหาในเร่อื งของการฝากและถอนเงินของเราท่ีแสดงไวในบทน้ี ยงั ไมสมบรูณ
ทเี ดยี วนัก กลา วคอื การถอนเงินจะทําไมไดถา มเี งนิ ไมค รบในบัญชี โดยเรากําหนดให
โปรแกรมยตุ ิการทํางานทันที ถา เหตกุ ารณด งั กลา วเกิดขนึ้ จงปรบั ปรงุ โปรแกรมดงั กลา วให
สลับการถอนเงินมาเปน การฝากเงิน ถามเี งินในบญั ชีนอยกวาท่ีตอ งถอน

5. สมมติวา เราเปลี่ยนกระบวนการภายในโปรแกรม BankAccountTest.java ใหเปน

class BankAccountTest1 {
public static void main(String[] args) {
out.printf("%15s%15s%15s%n", "Deposit",
"Withdrawal", "Balance");
//set up a shared account
BankAccount sharedAccount = new BankAccount(10000);

//create accounts
Account1 []acc1 = new Account1[3];
Account2 []acc2 = new Account2[3];
for(int i = 0; i < 3; i++) {

acc1[i] = new Account1(sharedAccount);
acc1[i].setPriority((int)(Math.random()*10 + 1));

acc2[i] = new Account2(sharedAccount);
acc2[i].setPriority((int)(Math.random()*10 + 1));

acc1[i].start();
acc2[i].start();
}
}
}

จงหาผลลัพธท ี่ไดจ ากการ run พรอมทัง้ อธิบายถึงการทาํ งานทีเ่ กิดขน้ึ (ทําไมจึงได
ผลลพั ธดังกลา ว) จงอธิบายผลลพั ธข องการ run ถาเราเอาการกาํ หนด priority ออกทงั้
สองที่

297

เริม่ ตน การเขยี นโปรแกรมดวย Java intro. to Java (FEU.faa)

6. จงปรบั ปรุง หรือออกแบบโปรแกรมการฝากและถอนเงนิ ใหม โดยกําหนดใหม กี ารถา ยโอน
เงนิ (transfer) ระหวา งบญั ชีตา ง ๆ ทมี่ อี ยใู นธนาคาร โดยกาํ หนดใหม ีจาํ นวนบญั ชที เี่ ปดไว
ไมน อยกวา 5 บญั ชี ใหท ําการทดสอบการโอนเงนิ ระหวางบัญชดี ว ยการสุมจาํ นวนการโอน
และสมุ จํานวนเงนิ ท่ีตอ งการโอนระหวางบัญชี โดยโปรแกรมจะตอ งแสดงจํานวนเงนิ ที่มีการ
โอน พรอมทั้งยอดเงนิ ท้ังหมดในเวลาตาง ๆ ของการโอนดังกลาว

7. Java มี method ชื่อ join() ทใ่ี ชทาํ งานเกี่ยวกบั thread จงอธบิ ายถงึ การทํางานของ join()
พรอ มทง้ั ยกตวั อยา งประกอบ

8. Java มี method ชือ่ yield() ท่ีใชทาํ งานเกย่ี วกบั thread จงอธบิ ายถึงการทํางานของ
yield() พรอ มท้ังยกตัวอยางประกอบ

9. Java มี method ช่ือ interrupt() ท่ีใชทาํ งานเกี่ยวกบั thread จงอธิบายถึงการทาํ งานของ
interrupt() พรอมทั้งยกตวั อยา งประกอบ

298

ในบทน้เี ราจะมาดูถงึ วธิ กี ารวาดรปู ทรงสองมติ ติ าง ๆ ดว ย method ที่มอี ยูใ น class Graphicsheight = 200
และ class Graphics2D รวมไปถึงการเคลอื่ นยายภาพ การหมุนภาพ การใชล ูกเลนตาง ๆ กบั
ภาพทีเ่ ราตอ งการแสดงออกทางหนาจอ intro. to Java (FEU.faa)
หลงั จากจบบทเรียนน้แี ลว ผอู า นจะไดท ราบถงึ

o การสรา ง frame
o การสง ขอ ความลงบน frame
o การวาดรปู ทรงตาง ๆ เชน สีเ่ หลีย่ ม (rectangle) วงรี หรือวงกลม (ellipse) เสนตรง

(line)
o การกําหนดสใี นรปู ทรงตา ง ๆ
o การเคลอ่ื นยายภาพ (translation)
o การหมนุ ภาพ (rotation)
o การปรบั เปล่ียนรปู (scale และ shear)
o การสรา งภาพเคล่อื นไหว (animation)
10.1 Simple Window
หนาตา ง (window) ที่เราเห็นกันอยูในโปรแกรมท่ัวไปมชี อื่ เรยี กวา frame ใน Java เราจะเริม่ ตน
สราง frame ตวั แรกของเราทม่ี เี พยี งแค title ดังท่ีเหน็ น้ี

title

width = 300
ภาพท่ี 10.1 หนาตาง (top-level window)

และ code ของการสรา งหนา ตา งตวั อยางมดี งั น้ี

เร่ิมตนการเขียนโปรแกรมดว ย Java intro. to Java (FEU.faa)

1: /**
2: Simple frame
3: */
4:
5: import javax.swing.*;
6:
7: class TestSimpleFrame {
8: public static void main(String[] args) {
9: SimpleFrame frame = new SimpleFrame();
10: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
11: frame.setVisible(true);
12: }
13: }
14:
15: class SimpleFrame extends JFrame {
16: private static final int WIDTH = 300;
17: private static final int HEIGHT = 200;
18:
19: public SimpleFrame() {
20: setTitle("Simple Frame");
21: setSize(WIDTH, HEIGHT);
22: }
23: }

โปรแกรมสรา งหนาตา งท่เี ราเขยี นขน้ึ ใช frame ของ Swing ดงั นน้ั เราจงึ ตอ ง import class ตาง
ๆ ท่ีมีอยใู น Swing package ทเ่ี ราตองการใชเขาสูโปรแกรม ดว ยคาํ ส่ัง import javax.Swing.*
โดยการกาํ หนดแลว frame ใน Java มขี นาดเปน 0 x 0 pixels เราจงึ ตอ งกาํ หนดให frame ที่
เราสรา งข้ึนมีขนาดเปน 300 x 200 pixels ผอู า นจะเหน็ วา เรามี class อยสู อง class คอื
TestSimpleFrame และ SimpleFrame

Class SimpleFrame เปน subclass ทเี่ ราสรางขน้ึ จาก class JFrame ซึง่ หนา ตา งทุกบานทเ่ี รา
ตองการสรา ง จะตอ งมาจาก class น้ี และเราไดกําหนดให frame ของเรามี title เปน Simple
Frame ดว ยการเรียกใช setTitle() สว นขนาดของ frame ก็ถกู กาํ หนดดวยคาํ ส่งั setSize() ดังที่
แสดงในบรรทดั ที่ 20 และ 21

เราสรา ง frame ใน class TestSimpleFrame ดว ยคําสง่ั

SimpleFrame frame = new SimpleFrame();

หลงั จากนนั้ เราก็กําหนดใหโ ปรแกรมยุตกิ ารทาํ งานถาผใู ชกดปมุ เพ่อื ปดหนา ตา ง (มมุ ขวาบน)
ดว ยคาํ สั่ง

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

หนา ตา งเมอื่ มีการสรา งเสร็จแลว จะไมแสดงตัวเองออกมา เราตอ งบงั คบั ให frame แสดงตัวดวย
การเรียกใช method setVisible() ของ class JFrame

หนา ตา งทเี่ ราสรา งขึ้นนี้เปน หนา ตา งทถ่ี ูกสรา งข้นึ จากระบบปฏิบัตกิ าร (Windows) ไมใชก าร
สรา งจาก Swing เพราะฉะน้นั ถา เรา run โปรแกรมตัวนบ้ี นระบบปฏบิ ตั กิ ารอนื่ เราก็จะไดห นา ตาง
ทมี่ หี นา ตาแปลกออกไป ตามขอกาํ หนดของระบบปฏิบตั ิการนน้ั ๆ (look and feel) แตถ า เรา
ตองการใหห นา ตา งของเรามีขอ กาํ หนดของ Java look and feel เราก็สามารถทําไดด ังน้ี

1: /**
2: Simple frame
3: */
4:
5: import javax.swing.*;
6:
7: class TestSimpleFrame {
8: public static void main(String[] args) {
9: //thread safe app
10: javax.swing.SwingUtilities.invokeLater(new Runnable() {
11: public void run() {
12: new SimpleFrame();
13: }
14: });

300

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

15: }
16: }
17:
18: class SimpleFrame extends JFrame {
19: private static final int WIDTH = 300;
20: private static final int HEIGHT = 200;
21:
22: public SimpleFrame() {
23: //make sure we have nice Java's look and feel window
24: setDefaultLookAndFeelDecorated(true);
25:
26: JFrame frame = new JFrame();
27: frame.setTitle("Simple Frame");
28: frame.setSize(WIDTH, HEIGHT);
29: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
30: frame.setVisible(true);
31: }
32: }

เราเปล่ยี นตวั method main() ใหท าํ การสรา ง object จาก class SimpleFrame ผานทาง
method run() และ method invokeLater() เพอ่ื ไมใ หเ กิดปญหาของการเรยี กใช thread (เรา
เรยี กการเรยี กแบบนว้ี าเปน การเรียกผาน event-dispatching thread) สวนสาํ คญั อกี ตวั หนึ่งก็คือ
คาํ ส่งั ในบรรทดั ท่ี 24 ซ่ึงเปน คําสั่งใหกับ Java ใหเ ลือกหนา ตา งทถ่ี กู ตอ งตามทเ่ี ราตองการ และ
ผลลัพธท ี่เราไดคือ

ภาพท่ี 10.2 หนาตางในรปู แบบของ Java

10.2 การวางตาํ แหนง ของหนาตา งบนจอ

การกําหนดตาํ แหนง ของหนา ตางทีเ่ ราสรา งขึน้ เพื่อใหป รากฏบนจอน้นั ตอ งเรียกใช method ที่
เก่ยี วขอ งหลายตวั พอสมควร เชน ถา เราตอ งการวางหนา ตา งตรงกลางจอ เรากต็ องรขู นาดของ
จอวา มขี นาดเทา ใด ขนาดของหนา ตา งทเี่ ราสรางขึ้นมสี ดั สวนเปน เทา ใดของจอ หนาตางสามารถ
ทจี่ ะเปลย่ี นแปลงขนาดจากผใู ชไดห รือไม เราจะลองเขียนโปรแกรมทที่ าํ งานอยา งทว่ี าดู

1: /**
2: Another simple frame
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import javax.swing.*;
8:
9: class TestAnotherFrame {
10: public static void main(String[] args) {
11: AnotherFrame frame = new AnotherFrame("Another Frame");
12: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13: frame.setVisible(true);
14: }
15: }
16:
17: class AnotherFrame extends JFrame {

301

เรมิ่ ตนการเขยี นโปรแกรมดว ย Java

18: public AnotherFrame(String title) { intro. to Java (FEU.faa)
19: //set title via super class
20: super(title);
21:
22: //get screen size
23: Toolkit kit = Toolkit.getDefaultToolkit();
24: Dimension screenSize = kit.getScreenSize();
25: int height = screenSize.height;
26: int width = screenSize.width;
27:
28: //set frame width and height
29: setSize(width / 2, height / 2);
30:
31: //set position of the frame
32: setLocation(width / 4, height / 4);
33:
34: //set icon
35: Image img = kit.getImage("990.gif");
36: setIconImage(img);
37: }
38: }

ผลลพั ธท ่ีเราไดจ ากการ run โปรแกรมกค็ ือหนา ตา งทมี่ ขี นาดเทากบั หนงึ่ ในสขี่ องจอ และอยู
กง่ึ กลางของจอ แตท่เี ปลยี่ นแปลงอีกอันหน่ึงก็คอื icon ของตวั หนา ตา ง

Icon ทถ่ี ูกเปลีย่ นจากเดมิ

ภาพที่ 10.3 สวนหนึง่ ของหนา ตางทีถ่ ูกเปลย่ี น icon

Constructor ของ class AnotherFrame เปนที่ที่เรากําหนดรปู แบบของหนา ตา งที่เราตองการ
แสดง โดยเราเปล่ยี นการกาํ หนด title ของ frame จากการเรียกใช setTitle() มาเรยี กใช
constructor ของ super class ของ JFrame ใหทาํ หนา ที่แทน และเราหาขนาดของจอดว ยการ
เรียกใช getDefaultToolkit() ของ class Toolkit ซ่ึงเมื่อไดแลวเราจะเก็บขนาดไวใ น
screenSize (object จาก class Dimension – ดบู รรทดั ที่ 23 และ 24) หลงั จากน้ันเราก็ดงึ เอา
ความกวางและความสูงของหนา ตา งมาเก็บไวในตวั แปร width และ height ตามลําดับ ในการ
กาํ หนดตาํ แหนง ของหนา ตา ง เรากเ็ รียกใช method setLocation() ดว ยคา ของ width / 4 และ
height / 4

สวน icon ท่อี ยใู นมมุ บนซายของหนาตางกถ็ กู เปลี่ยนดวยการเรียกใช setIconImage() หลงั จาก
ทเี่ ราไดท ําการเก็บ icon ทีต่ อ งการแสดงไวในรปู แบบของ GIF ไฟล (บรรทดั ท่ี 35)

302

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

10.3 การแสดงขอ มูลในหนาตางดว ยการวาด (Drawing)

การแสดงขอความท่เี ราตอ งการออกทางหนาจอนนั้ มีหลายวธิ ี วธิ ีที่เราจะแสดงใหด คู อื การเขียน
ลงในหนา ตา งที่ตอ งการแสดงผา นทาง Panel

เราจะเริ่มตนดวยการเขียนโปรแกรมแสดงผลกอนทเี่ ราจะอธบิ ายถงึ วิธกี าร

1: /**
2: Drawing onto window
3: */
4:
5: import java.awt.*;
6: import javax.swing.*;
7:
8: class WelcomeWindow {
9: public static void main(String[] args) {
10: WelcomeFrame frame = new WelcomeFrame();
11: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12: frame.setVisible(true);
13: }
14: }
15:
16: //frame contains message panel
17: class WelcomeFrame extends JFrame {
18: private static final int WIDTH = 300;
19: private static final int HEIGHT = 200;
20:
21: public WelcomeFrame() {
22: setTitle("Welcome Window");
23: setSize(WIDTH, HEIGHT);
24:
25: //add to panel
26: WelcomePanel panel = new WelcomePanel();
27: add(panel);
28: }
29: }
30:
31: //panel for message to be drawn on
32: class WelcomePanel extends JPanel {
33: private static final int X = 75;
34: private static final int Y = 100;
35:
36: public void paintComponent(Graphics g) {
37: super.paintComponent(g);
38: g.drawString("Welcome to Java Window", X, Y);
39: }
40: }

ส่ิงทเ่ี ราทาํ แตกตางจากตวั อยางกอนหนา น้ี กค็ ือเราไดส ราง panel สาํ หรบั การเขียนขอ ความ
ขน้ึ มาหนึง่ อัน จาก class WelcomePanel โดยต้ังช่อื วา panel ในบรรทดั ท่ี 26 และเราทาํ การ
แปะ panel ตวั นเี้ ขา กบั frame ดว ยการเรยี กใช method add()

ขอ ความท่เี ราเขยี นขึ้นเปนการเขยี นผา น method paintComponent() ซงึ่ เปน method ที่มอี ยู
ใน class JComponent และ paintComponent() มี parameter 1 ตัวคอื object ที่เปน
Graphics และ object ตวั น้ีจะมขี อ มลู ทีเ่ กยี่ วขอ งกับการวาดภาพที่เปน ขอ ความ หรอื รปู เชน
ชนดิ ของตวั อักษร สที ใ่ี ช เปนตน การวาดใน Java จะตองทาํ ผา น method ของ object ทม่ี า
จาก class Graphics เทา นั้น เชน method drawstring() ท่เี ราใชใ นบรรทดั ที่ 38

g.drawString("Welcome to Java Window", X, Y);

สิ่งทีเ่ ราสงใหก บั drawString() ก็คอื ขอ ความทตี่ อ งการแสดง (String) และตาํ แหนง ของ
ขอความ (X และ Y) ผูอ านจะสงั เกตเหน็ วา เราไมไ ดเ รียก paintComponent() โดยตรง ทงั้ น้ีก็
เพราะวา ทุก ๆ ครั้งทมี่ ีการรอ งขอการแสดงหนาตาง ตวั ควบคุมเหตกุ ารณ (event handler) จะ
ทาํ การบอกให component รถู งึ การรอ งขอดงั กลา ว ทาํ ให method ตาง ๆ ของ
paintComponent() ถกู เรยี กขน้ึ มาทาํ งานโดยอัตโนมตั ิ การแสดงขอ ความจงึ เกดิ ขึ้น

303

เร่ิมตน การเขียนโปรแกรมดวย Java frame panel

ตาํ แหนงเรม่ิ ตนของ frame จะเร่ิมตนทีด่ านบน intro. to Java (FEU.faa)
ซาย (top-left) ของพื้นท่ี ทเ่ี ราตองการวาด
(0, 0)

X = 75, Y = 100
X

Y

ภาพท่ี 10.4 การวาดขอ ความลงบนหนาตาง

กระบวนการทจ่ี าํ เปนสาํ หรบั การวาดลงบน panel มีขนั้ ตอนสาํ คญั ดงั นค้ี อื
• สราง class ที่ตอ งการใช (ใชค าํ ส่ัง extends) จาก class JPanel
• Override method paintComponent() ใน class นัน้

เนื่องจากวา class ทเี่ ราสรางขึ้นเพ่ือทาํ การวาดนัน้ มาจาก class JPanel ซง่ึ มีกระบวนการในการ
จดั การกับการวาดภาพพ้ืนหลงั ของ panel เองดังน้นั เราจึงตอ งเรียก super.paintComponent()
ใหทาํ สวนทตี่ ัวเองตองทําใหเ สรจ็ กอนทเี่ ราจะทาํ การวาดสวนตา ง ๆ ของเราเอง (เชน ทไ่ี ดท าํ ใน
บรรทดั ท่ี 37 ของโปรแกรม Welcome.java)
10.3.1 การวาดเสน (Line)
การวาดเสนใน Java เราใชคําสง่ั

drawLine(int x1, int y1, int x2, int y2);

โดยท่ี (x1, y1) เปน จดุ เริม่ ตน ของเสนและ (x2, y2) เปน จดุ สน้ิ สดุ ของเสนดังทแี่ สดงใหด ใู น
ตัวอยางท่ีเหน็ นี้

(0, 0) (getWidth(), 0)

(0, getHeight() (getWidth(), getHeight())

ภาพท่ี 10.5 การวาดเสนตรงสองเสน

เราวาดเสนทแยงมุมสองเสน จากการใชค าํ สงั่

304

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

g.drawLine(0, 0, getWidth(), getHeight());
g.drawLine(0, getHeight(), getWidth(), 0);

10.3.2 การวาดรปู สี่เหล่ียม
การวาดรูปสีเ่ หลย่ี มมี method อยูท้ังหมดหกตวั คือ
drawRect(int x, int y, int w, int h)
fillRect(int x, int y, int w, int h)
drawRoundRect(int x, int y, int w, int h, int aw, int ah)
fillRoundRect(int x, int y, int w, int h, int aw, int ah)
draw3DRect(int x, int y,int w, int h, boolean raised)
fill3DRect((int x, int y,int w, int h, boolean raised)
ตวั อยา งผลลัพธที่เหน็ นี้ เราใช method 4 ตัวแรกในการสรา ง

ภาพที่ 10.6 รปู ทรงสเ่ี หล่ียม

1: /**
2: Draw rectangles
3: */
4:
5: import java.awt.*;
6: import javax.swing.*;
7:
8: class DrawRectangles {
9: public static void main(String[] args) {
10: RectangleFrame frame = new RectangleFrame();
11: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12: frame.setVisible(true);
13: }
14: }
15:
16: class RectangleFrame extends JFrame {
17: private static final int WIDTH = 300;
18: private static final int HEIGHT = 200;
19:
20: public RectangleFrame() {
21: setTitle("Drawing Rectangles");
22: setSize(WIDTH, HEIGHT);
23:
24: //add to panel
25: DrawingPanel panel = new DrawingPanel();
26: add(panel);
27: }
28: }
29:
30: class DrawingPanel extends JPanel {
31: public void paintComponent(Graphics g) {
32: super.paintComponent(g);

305

เริ่มตนการเขียนโปรแกรมดวย Java

33: intro. to Java (FEU.faa)

34: g.drawRect(5, 5, getWidth() / 2 - 10, getHeight() / 2 - 10);
35:
36: g.setColor(Color.gray);

37: g.fillRect(getWidth() / 2 + 5, 5,
38: getWidth() / 2 - 10, getHeight() / 2 - 10);

39:
40: g.setColor(Color.blue);
41: g.fillRoundRect(5, getHeight() / 2 + 5, getWidth() / 2 - 10,
42: getHeight() / 2 - 10, 20, 20);
43:
44: g.setColor(Color.red);
45: g.drawRoundRect(getWidth() / 2 + 5, getHeight() / 2 + 5,
46: getWidth() / 2 - 10, getHeight() / 2 - 10, 20, 20);
47: }
48: }

การกําหนดสที เ่ี ราตองการใชกท็ าํ ไดด ว ยการเรยี กใช method setColor() ดว ยสที ต่ี องการจากที่
กาํ หนดไวใ น class Color เชน

Color.black หรือ Color.BLACK
Color.blue หรอื Color.BLUE
Color.cyan หรือ Color.CYAN
Color.darkGray หรอื Color.DARK_GRAY
Color.gray หรือ Color.GRAY
Color.green หรือ Color.GREEN
Color.lightGray หรอื Color.LIGHT_GRAY
Color.magenta หรอื Color.MAGENTA
Color.orange หรอื Color.ORANGE
Color.pink หรือ Color.PINK
Color.red หรือ Color.RED
Color.white หรอื Color.WHITE
Color.yellow หรอื Color.YELLOW

การใช drawRect() และ fillRect() เปนการวาดรูปสีเ่ หลย่ี ม และการระบายรูปสเ่ี หลย่ี ม สวนการ
ใช drawRoundRect() และ fillRoundRect() น้นั เปน การวาดรูปสี่เหล่ียม และการระบายรปู
สเี่ หล่ยี มท่มี มี ุมเปนรูปโคง (aw และ ah) คา aw เปนความกวา ง คา ah เปนความสงู ของรปู วงรีท่ี
เปน ตัวกาํ หนดความโคงดังกลา ว ผูอานควรทดลองคาความโคงทต่ี างกันเพอื่ ใหเหน็ ถงึ มมุ ที่
เกดิ ขน้ึ

Method ท่ีเหลอื สองตวั คือ draw3DRect() และ fill3DRect() เปนการวาดรปู ส่เี หลยี่ ม และการ
ระบายรูปส่เี หลย่ี มทีเ่ ปน รูปสามมิติ parameter ตัวสดุ ทายทมี่ ีอยูใน method ท้ังสองตวั เปน
ตวั กาํ หนดวา รปู สีเ่ หลี่ยมทสี่ รา งข้นึ นน้ั จะนูน (raised) หรืออยลู กึ (etched) ลงไปในภาพ

10.3.3 การวาดรปู หลายเหลย่ี ม (Polygon)

Class Polygon เปน class ท่เี อ้ือใหเราวาดรปู หลายเหล่ียมไดงา ยขึน้ class นีม้ ี constructor อยู
สองตัวใหเ ราเรยี กใช คือ Polygon() และ Polygon(int[] xpoints, int[] ypoints, int[]
npoints) เราจะลองสรา งรูปหลายเหลยี่ มจาก class Polygon ดู

หลงั จากทเี่ ราสราง object จาก class Polygon แลวเรากก็ ําหนดจดุ ตา ง ๆ ทม่ี อี ยูใน polygon
นนั้ ดวยการเรียกใช method addPoints() ดงั ทเ่ี ห็นนี้

Polygon poly = new Polygon();
poly.addPoint(75, 50);
poly.addPoint(150, 0);
poly.addPoint(225, 50);
poly.addPoint(225, 150);
poly.addPoint(150, 200);
poly.addPoint(75, 150);

เม่ือเรากําหนดเสรจ็ เราก็เรยี ก drawPolygon() เพ่อื ทําการวาด

g.drawPolygon(poly);

306

บทที่ 10: Graphics Programming

ผลลัพธท ีเ่ ราไดค อื

(150, 0)

(75, 50) (225, 50) intro. to Java (FEU.faa)

(75, 150) (225, 150)

(150, 200)

ภาพท่ี 10.7 การวาดรูปหลายเหลี่ยมดว ย drawPolygon()

Polygon ทีเ่ ราเหน็ นี้สรา งขน้ึ ดว ยการกําหนดจดุ ใหก บั object จาก class Polygon แตเ รา
สามารถสรา ง polygon ไดอกี แบบหน่งึ คอื กาํ หนดจดุ ตา ง ๆ ท่ตี องการใน array หลงั จากนนั้ ก็สง
จุดเหลา นใี้ หก บั method drawPolygon() ดงั ท่ีเห็นนี้

int[] x = {75, 150, 225, 225, 150, 75};
int[] y = {50, 0, 50, 150, 200, 150};
g.drawPolygon(x, y, x.length);

parameter ตวั สดุ ทา ยจะเปนจาํ นวนของจุดทงั้ หมดท่มี ีอยใู น polygon ท่เี ราสรา ง ซ่ึงในกรณี
ตัวอยา งของเรา เราจะใช x.length หรอื y.length กไ็ ด
10.3.4 การวาดรปู วงรี (Oval)
การวาดรูปวงรี หรือวงกลมทาํ ไดดว ยการเรียกใช method drawOval() และ fillOval() ดงั น้ี

g.drawOval(int x, int y, int w, int h);
g.fillOval(int x, int y, int w, int h);

parameter 2 ตวั แรกคอื จกุ เรม่ิ ตน (x, y) ตัวท่สี ามเปน ความกวา ง สวนตัวสดุ ทา ยเปนความสงู
ของรปู วงรที ตี่ องการวาด (ผูอา นควรสังเกตวา ถา width และ height มคี าเทา กนั เราจะไดร ปู
วงกลม)
ผลลัพธท ี่เราไดจ ากการทดลองวาดดว ยคําสง่ั

g.drawOval(0, 0, getWidth(), getHeight());

307

เรม่ิ ตนการเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

ภาพท่ี 10.8 รปู วงรี

10.4 การสรา งรปู ทรงสองมติ จิ าก Graphics2D

การใช method จาก class Graphics นั้นมขี ีดจาํ กดั พอสมควร เชน เราไมส ามารถทจ่ี ะกาํ หนด
ความหนาของเสน ทีเ่ ราวาดได ไมส ามารถทีจ่ ะหมนุ ภาพหรือเคลือ่ นยายภาพได ใน Java 2D เรา
มี class Graphics2D ทีม่ ี method ตาง ๆ ในการทํางานกบั ภาพสองมติ ใิ นรูปแบบตา ง ๆ ไดม าก
ขนึ้ โปรแกรมตวั อยา งตอ ไปนีเ้ ปนการเรียกใช method ตาง ๆ ในการสรางภาพสองมิติ

1: /**
2: Draw shapes with Graphics2D
3: */
4:
5: import java.awt.*;
6: import java.awt.geom.*;
7: import javax.swing.*;
8:
9: class DrawShapes {
10: public static void main(String[] args) {
11: ShapesFrame frame = new ShapesFrame();
12: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13: frame.setVisible(true);
14: }
15: }
16:
17: class ShapesFrame extends JFrame {
18: private static final int WIDTH = 400;
19: private static final int HEIGHT = 400;
20:
21: public ShapesFrame() {
22: setTitle("Drawing 2D Shapes");
23: setSize(WIDTH, HEIGHT);
24:
25: //add to panel
26: DrawingPanel panel = new DrawingPanel();
27: add(panel);
28: }
29: }
30:
31: class DrawingPanel extends JPanel {
32: public void paintComponent(Graphics g) {
33: super.paintComponent(g);
34: Graphics2D g2 = (Graphics2D)g;
35:
36: double x = 100;
37: double y = 100;
38: double width = 200;
39: double height = 150;
40:
41: //draw rectangle
42: Rectangle2D rectangle = new Rectangle2D.Double(x, y, width, height);
43: g2.draw(rectangle);
44:

308

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

45: //draw ellipse
46: Ellipse2D ellipse = new Ellipse2D.Double();
47: ellipse.setFrame(rectangle);
48: g2.draw(ellipse);
49:
50: //draw a circle
51: double centerX = rectangle.getCenterX();
52: double centerY = rectangle.getCenterY();
53: double radius = 150.0;
54:
55: Ellipse2D circle = new Ellipse2D.Double();
56: circle.setFrameFromCenter(centerX, centerY,
57: centerX + radius, centerY + radius);
58: g2.draw(circle);
59:
60: //create line thickness and draw vertical line
61: BasicStroke thickStroke = new BasicStroke(3.0f);
62: g2.setStroke(thickStroke);
63: g2.draw(new Line2D.Double(getWidth()/2, 0,
64: getWidth()/2, getHeight()));
65:
66: //create dashed line and draw horizontal line
67: float dash1[] = {10.0f};
68: BasicStroke dashed = new BasicStroke(3.0f,
69: BasicStroke.CAP_BUTT,
70: BasicStroke.JOIN_MITER,
71: 10.0f, dash1, 0.0f);
72: g2.setStroke(dashed);
73: g2.draw(new Line2D.Double(0, getHeight()/2,
74: getWidth(), getHeight()/2));
75: }
76: }

กระบวนการในการเรยี กใช method ท่ีมอี ยูใน class Graphcis2D กท็ าํ ไดไ มยาก เพยี งแตว า เรา
ตอ งเปล่ยี น object จาก class Graphics ใหเ ปน object ของ class Graphics2D กอนดงั ทีท่ ําไว
ในบรรทดั ที่ 34 และในบรรทดั ที่ 42 และ 43 เปน การสรา งรูปส่เี หลยี่ มทจ่ี ดุ เรม่ิ ตน (x, y) ดวย
ความกวา งและสงู เทา กับ width และ height ตามลําดับ เราเรียกใช method draw() ดวยการ
สง object ท่ีเกิดจาก class Shape ไปให

บรรทดั ที่ 46 ถงึ 48 เปน การวาดรูปวงรีทใี่ ชขอ มลู ของรปู ส่เี หลีย่ มท่ีวาดขึ้นกอนหนา นเี้ ปน
ตัวกาํ หนด ซ่งึ ทําใหรูปวงรนี ี้อยภู ายในรูปสเ่ี หล่ยี ม

บรรทัดท่ี 51 ถงึ 53 เปนการหาจุดกง่ึ กลางของรปู สี่เหลยี่ ม และการกาํ หนดรศั มีของวงกลมทเี่ รา
ตอ งการวาด และในบรรทัดที่ 55 ถึง 58 เปนการกาํ หนดกรอบของวงกลมจากจุดกึง่ กลางทเ่ี ราหา
ไดก อนหนาน้ี พรอ มกับการวาดวงกลมดังกลา ว

บรรทัดที่ 61 ถงึ 62 เปนการกาํ หนดขนาดหรอื ความหนา (thickness) ของเสน ที่เราใชว าด ซึ่ง
เรากาํ หนดใหมขี นาดเทา กับ 3 (เราตองกําหนดใหเ ปน float) เมอ่ื กําหนดเสร็จเราก็วาดเสนตรง
ดว ยการสรา ง object จาก class Line2D โดยใหเปนเสน กึ่งกลางจากบนลงลา ง

บรรทัดที่ 67 ถงึ 73 เปน การกาํ หนดใหเ สนทจี่ ะวาดเปนเสนประทม่ี ีความหนาเทา กบั 3 พรอมกบั
การวาดเสน ตรง (ประ) ดงั กลา ว คาํ สั่งในการกําหนดเสนประดจู ะยุงเยงิ นดิ หนอ ย ท้งั น้กี ็เพราะวา
เราตองกาํ หนดคา ทีเ่ สน ประนี้จะตอ งแสดง ซ่ึงมคี า ตา ง ๆ ดังน้ี

Parameter ตวั ทห่ี น่ึงเปนความหนาของเสน ตวั ทสี่ องเปน ตวั กําหนดรปู รางของจุดจบของเสน
(end) ตัวทสี่ ามเปนตวั กาํ หนดการรวมกันเมอื่ เสนมาเจอกนั (join) ตวั ทส่ี ี่เปน ตวั กาํ หนดการ trim
ของ miter join ตวั ทห่ี า เปน array กาํ หนด pattern ของเสนประ และตวั สุดทายเปน offset ที่
เปน ตวั เริ่มตนของเสนประ

ผลลพั ธท ี่เราไดจากการ run โปรแกรม DrawShape.java คือ

309

เร่ิมตน การเขียนโปรแกรมดวย Java

ภาพที่ 10.9 การวาดรปู ทรงตา ง ๆ ดว ย Graphics2D intro. to Java (FEU.faa)

Class ที่อยใู น package java.awt.geom

Arc2D Ellipse2D QuadCurve2D

Area GeneralPath Rectangle2D

CubicCurve2D Line2D RectangularShape

Dimension2D Point2D RoundRectangle2D

Method ทใี่ ชหาจดุ กง่ึ กลาง คา ตํ่าสุด คา สูงสดุ ของ (x, y) ของรูปส่เี หลยี่ ม
• double getCenterX()
• double getCenterY()
• double getMinX()
• double getMinY()
• double getMax()
• double getMaxY()

method ที่ใชห าความกวาง และความสงู ของรปู สีเ่ หลย่ี ม
• double getWidth()
• double getHeight()

method ท่ใี ชห าคา x และคา y ทีอ่ ยูม ุมบนซา ยของรปู ส่ีเหลย่ี ม
• double getX()

310

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

• double getY()
method ทใี่ ชว าดรูปทรงตาง ๆ

• Rectangle2D.Double(double x, double y, double w, double h)
• Rectangle2D.Float(float x, float y, float w, float h)
• Ellipse2D.Double(double x, double y, double w, double h)
Method ทใ่ี ชส รางจดุ และเสนตรง
• Point2D.Double(double x, double y)
• Line2D.Double(Point2D start, Point2D end)
• Line2D.Double(double startX, double startY, double endX, double endY)
ในการเปลย่ี นสขี องปากกาทใี่ ชวาดเราก็เรียกใช method setPaint() ดวยคา สตี า ง ๆ ท่ไี ดพ ดู ไว
กอ นหนานี้ เชน ถาเราตอ งการใหเ สน ประมสี ีแดงในโปรแกรมของเรา เรากเ็ รียกใชค ําสงั่
g2.setPaint(Color.red) แทรกระหวางบรรทดั ที่ 72 และ 73 แตถาเราตอ งการสที อี่ ยูนอกเหนอื สี
ท่กี าํ หนดเหลานี้เราก็ตอ งสรางขนึ้ ใหม จาก class Color เชน
g2.setPaint(new Color(0, 128, 128));
ซง่ึ เปน การกําหนดคา สที อ่ี ยูระหวา ง 0 – 255 (parameter ทงั้ สามตวั แสดงถึงคาโทนสีของ สี
แดง (r) สเี ขียว (g) และสฟี า (b) ตามลําดบั ) เพราะฉะน้นั ถาอยากไดสีขาวเรากใ็ ช (255, 255,
255) เปน ตน แตถ าเราตอ งการใหม ีโทนสใี นภาพทตี่ อ งการวาด (fill pattern) เราก็ตอ งสรา ง
โทนสจี าก class GradientPaint ดงั ตัวอยา งนเ้ี รากําหนดใหเ กดิ สีจากแดงไปเหลืองโดยมคี วาม
เปนโทนสีอยรู ะหวางสที ั้งสอง

GradientPaint gradient = new GradientPaint(0, 0,
Color.red, 300, 200, Color.yellow);

g2.setPaint(gradient);

ผลลพั ธท ไี่ ดจ ากการกาํ หนดสดี งั กลา วในการวาดรปู วงรีคอื

ภาพที่ 10.10 การใชโทนสใี นการวาดภาพ

[การกาํ หนดรปู แบบ (style) ของเสน]
เรากําหนด style ของเสนจาก class BasicStroke โดยสรา ง object จาก class นแี้ ลว จงึ สง ไป
ให method setStroke() ของ class Graphics2D ดังท่ีเราไดแสดงใหดูกอนหนาน้ี object จาก
class BasicStroke จะมขี อ มลู ท่ีเกี่ยวกับรปู ทตี่ อ งการวาด เชน ขนาดของเสน (line width) style
ของการพบกนั ของเสน (join style) style ของการสน้ิ สดุ ของเสน (end-cap) และ style ของ
เสนประทีก่ าํ หนดข้ึน (dash style)

311

เรม่ิ ตนการเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

Join style มที ง้ั หมดอยู 3 ตัวคอื

1. JOIN_BEVEL
2. JOIN_MITER
3. JOIN_ROUND

สว น end-cap มีอยสู ามตัวเชน กนั คือ

1. CAP_BUTT
2. CAP_ROUND
3. CAP_SQUARE

การกําหนด dash style น้ันเราใช dash array และ dash phase โดยท่ี dash array จะเปน
array ทเ่ี กบ็ ลักษณะของเสน ประ คา ตาง ๆ ใน array จะเปน ตวั กาํ หนดความยาวของเสน ประ
และชอ งวา งระหวา งเสน เชน คา ในตาํ แหนง 0 จะเปน ความยาวของเสน ประตวั แรก คา ใน
ตาํ แหนง 1 จะเปนความยาวของชองวา งตวั แรก เปน ตน (สลบั กันไปในลักษณะนี)้ จากภาพที่
10.6 เราใชค า dash array เพียงคา เดยี วคอื {10,0f} ดงั นนั้ เสนประของเราจงึ มขี นาดเทา กนั ทั้ง
เสน ทึบและชอ งวา ง แตถ าเราเปลี่ยนใหเปน คา อื่น เชน {5, 2, 5, 2} เรากจ็ ะไดชองวา งทีเ่ ลก็ กวา
เสนทึบ

10.4.1 การสรา งเสนโคง (Quadratic and Cubic Curves)

การสรางเสน โคงใน Java ทําไดด ว ยการเรยี กใช class QuadCurve2D และ class
CubicCurve2D

Class QuadCurve2D เปนการสรางเสน โคงทม่ี ีชอ่ื เรยี กวา Quadratic Parametric Curve โดย
เราตองกาํ หนดจุดสองจุดทีเ่ ปน จดุ เรมิ่ ตน และจดุ จบของเสน (end points) และอกี จุดหนึง่ ทเ่ี ปน
ตัวกําหนดความโคง ของเสน (control point)

QuadCurve2D.Double quad = new QuadCurve2D.Double();
Point2D.Double start, end, control;

start = new Point2D.Double();
end = new Point2D.Double();
control = new Point2D.Double();

Dimension d = getSize();
int w = d.width;
int h = d.height;

start.setLocation(w/2-50, h/2);
end.setLocation(w/2+50, h/2);
control.setLocation((int)(start.x)+50, (int)(start.y)-50);
quad.setCurve(start, control, end);
g2.draw(quad);

code ทีเ่ หน็ ดา นบนนีเ้ รมิ่ ดวยการสรา งเสน โคง (quad), จุดเรมิ่ ตน (start), จุดจบ (end) และจุด
ควบคมุ (control) จาก constructor ทเ่ี ก่ียวขอ งทง้ั สต่ี วั เรากาํ หนดตาํ แหนง ของจดุ ท้ังสามใหม ี
ความสมั พันธกบั ขนาดของจอภาพดว ยการใช method setLocation() ซึ่งเมอ่ื ไดจ ุดแลวเราก็
สรางเสนโคง ดว ยการเรยี กใช method setCurve() ดวยจดุ ทง้ั สาม

Class CubicCurve2D เปนการสรางเสน โคง ทม่ี ชี อ่ื เรียกวา Cubic Parametric Curve โดยเรา
ตอ งกาํ หนดจดุ endpoint สองจดุ และจดุ control point อกี สองจุด code ของการสรา งเสนโคง
แบบนก้ี ค็ ลาย ๆ กับแบบแรก เพยี งแตเ พมิ่ จดุ ควบคุมอกี หนง่ึ ตัว เราคงไมแ สดงใหด ูแตจ ะนาํ เอา
code ทง้ั หมดของการสรา งเสน โคง แบบแรกมาใหด ู

1: /**
2: Draw Quadratic parametric curve
3: */
4:

312

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

5: import java.awt.*;
6: import java.awt.Rectangle;
7: import java.awt.geom.*;
8: import javax.swing.*;
9:
10: class ParametricCurves {
11: public static void main(String[] args) {
12: CurveFrame frame = new CurveFrame();
13: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14: frame.setVisible(true);
15: }
16: }
17:
18: class CurveFrame extends JFrame {
19: private static final int WIDTH = 300;
20: private static final int HEIGHT = 200;
21:
22: public CurveFrame() {
23: setTitle("Drawing Curves");
24: setSize(WIDTH, HEIGHT);
25:
26: //add to panel
27: DrawingPanel panel = new DrawingPanel();
28: panel.setBackground(Color.white);
29: add(panel);
30: }
31: }
32:
33: class DrawingPanel extends JPanel {
34: QuadCurve2D.Double quad = new QuadCurve2D.Double();
35: Point2D.Double start, end, control;
36: Rectangle sPoint, ePoint, ctrlPoint;
37:
38: public DrawingPanel() {
39: start = new Point2D.Double();
40: end = new Point2D.Double();
41: control = new Point2D.Double();
42: sPoint = new Rectangle(0, 0, 8, 8);
43: ePoint = new Rectangle(0, 0, 8, 8);
44: ctrlPoint = new Rectangle(0, 0, 8, 8);
45: }
46:
47: public void paintComponent(Graphics g) {
48: super.paintComponent(g);
49: Graphics2D g2 = (Graphics2D)g;
50: Dimension d = getSize();
51: int w = d.width;
52: int h = d.height;
53: BasicStroke thickStroke = new BasicStroke(3.0f);
54: g2.setStroke(thickStroke);
55:
56: start.setLocation(w/2-50, h/2);
57: end.setLocation(w/2+50, h/2);
58: control.setLocation((int)(start.x)+50, (int)(start.y)-50);
59:
60: sPoint.setLocation((int)(start.x)-4, (int)(start.y)-4);
61: ePoint.setLocation((int)(end.x)-4, (int)(end.y)-4);
62: ctrlPoint.setLocation((int)(control.x)-4, (int)(control.y)-4);
63:
64: quad.setCurve(start, control, end);
65:
66: g2.setPaint(Color.black);
67: g2.draw(quad);
68: g2.setPaint(Color.red);
69: g2.fill(sPoint);
70: g2.setPaint(Color.yellow);
71: g2.fill(ePoint);
72: g2.setPaint(Color.blue);
73: g2.fill(ctrlPoint);
74: }
75: }

ผลลพั ธท ไ่ี ดจ ากโปรแกรมคือ

313

เร่ิมตน การเขยี นโปรแกรมดวย Java intro. to Java (FEU.faa)

ภาพท่ี 10.11 เสน โคง

code ในบรรทดั ที่ 55 – 58 เปนการกําหนดจดุ ทง้ั สามของเสนโคง code ในบรรทัดท่ี 60 – 62
เปน การกาํ หนดรปู ส่ีเหลยี่ มเพื่อใชแ สดงจดุ ทงั้ สามของเสนโคง นนั้ หลังจากทก่ี ําหนดเสนโคง ดว ย
setCurve() แลว เราก็วาดเสนโคงพรอ มทง้ั ส่เี หลีย่ มที่อยู ณ จุดทงั้ สาม
10.4.2 การแสดงผลของพน้ื ทีท่ ที่ ับซอนกัน (Area)
Java มกี ระบวนการในการแสดงผลเม่อื พืน้ ท่ขี องรูปทรงเรขาวางซอนกันอยูสต่ี ัวคือ
add จะเปน การรวมเอาพื้นทท่ี งั้ สองเขาดวยกนั โดยพืน้ ท่ีท่เี กิดข้ึนใหมจ ะรวมจดุ ทงั้ หมดของ
พน้ื ทข่ี องตัวแรกหรือของพ้นื ทต่ี ัวทสี่ อง
subtract พืน้ ทที่ เี่ กดิ ข้นึ ใหมจ ะรวมจดุ ทัง้ หมดทอ่ี ยใู นสว นของพ้ืนที่ตวั แรก ไมน บั ตัวทส่ี อง
intersect พ้ืนทท่ี เ่ี กดิ ข้ึนรวมจดุ ทัง้ หมดของทงั้ สองพน้ื ที่
exclusiveOr พนื้ ท่ที ี่เกิดขน้ึ ใหมจะรวมจดุ ของพ้ืนท่ตี ัวแรกหรอื ตัวทส่ี องเทา น้นั แตไ มใ ชทัง้ สอง
ภาพท่ี 10.12 และ 10.13 แสดงการใชกระบวนการท้งั สขี่ อง Area

ภาพท่ี 10.12 การใช add() และ subtract()

314

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

ภาพที่ 10.13 การใช intersect() และ exclusiveOr()

Code ท้ังหมดของ AreaTest.java

1: /**
2: Areas
3: */
4:
5: import java.awt.*;
6: import java.awt.geom.*;
7: import javax.swing.*;
8:
9: class AreaTest {
10: public static void main(String[] args) {
11: JFrame frame = new AreaFrame();
12: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13: frame.setVisible(true);
14: }
15: }
16:
17: class AreaFrame extends JFrame {
18: private static final int WIDTH = 400;
19: private static final int HEIGHT = 400;
20:
21: public AreaFrame() {
22: setTitle("Area Test");
23: setSize(WIDTH, HEIGHT);
24:
25: //add to panel
26: JPanel panel = new AreaPanel();
27: panel.setBackground(Color.white);
28: add(panel);
29: }
30: }
31:
32: class AreaPanel extends JPanel {
33: private Area area, area1, area2;
34: private JPanel panel;
35:
36: public AreaPanel() {
37: area = new Area();
38: area1 = new Area(new Ellipse2D.Double(100, 100, 150, 100));
39: area2 = new Area(new Rectangle2D.Double(150, 150, 150, 100));
40:
41: //perform operations: using each of these, then re-compile
42: //add, subtract, intersect, exclusiveOr
43: area.add(area1);
44: area.exclusiveOr(area2);
45: //you actually don't need the following two lines
46: //panel = new JPanel();
47: //add(panel);
48: }
49:
50: public void paintComponent(Graphics g) {
51: super.paintComponent(g);
52: Graphics2D g2 = (Graphics2D)g;

315

เร่ิมตน การเขียนโปรแกรมดว ย Java intro. to Java (FEU.faa)

53: g2.draw(area1);
54: g2.draw(area2);
55: g2.setPaint(new GradientPaint(0, 0, Color.red, 300,

100, Color.yellow));
56: if(area != null) g2.fill(area);
57: }
58: }

10.4.3 การยา ยภาพ (Translation)
การยา ยภาพไปยงั ตําแหนง ใหม เราตองอาศยั class AffineTransform และ method
translate(), transform() ดังท่ีแสดงใหดใู นสว นของ code ที่วาดรูปส่ีเหลย่ี มนี้

Rectangle2D rectangle = new Rectangle2D.Double(x, y, width, height);
g2.draw(rectangle);
AffineTransform save = g2.getTransform();
AffineTransform at = new AffineTransform();
at.translate(50, 50);
g2.transform(at);
g2.setPaint(Color.blue);
g2.draw(rectangle);
g2.setTransform(save);

หลงั จากท่ีเราวาดรูปสี่เหลย่ี มตวั แรกแลว เรากําหนดให at (object จาก class
AffineTransform) เปนเสมอื นหนาตางใหมส ําหรับการวาด เรากท็ ําการยา ยหนา ตางของการวาด
ภาพใหมด ว ยคา (x=50, y=50) หลงั จากน้ันเราก็เรยี ก method transform() เพื่อกําหนดให
Gaphics2D ใชห นา ตา งใหมน ี้ (เราเรยี กข้ันตอนการทํางานนว้ี า Coordinate Transformation)
และเม่อื เราเปลย่ี นสใี หเ ปน สีนาํ้ เงนิ แลว เรากว็ าดใหมด วยคา เดิมของสี่เหลยี่ ม

(100, 100)

(150, 150)

ภาพที่ 10.14 การยายภาพ

เรารูวาจดุ เร่ิมตน ของพ้ืนทใ่ี นการวาดภาพนัน้ อยูที่ (0, 0) และเราวาดรปู สเี่ หลย่ี มท่ีมมี ุมซา ยบนอยู
ท่ี (100, 100) เราตอ งการทจ่ี ะยา ยรปู สี่เหล่ียมน้ีใหไ ปอยูในตําแหนงใหมท ่ี (150, 150) เราก็
อาจทาํ ไดด ว ยการวาดรปู สี่เหล่ียมใหมด ว ยตาํ แหนงเรม่ิ ตนท่ี (150, 150) ก็ได แตส ่งิ ทท่ี าํ ไดง าย
กวา ก็คอื ยายพน้ื ที่การวาดภาพออกไปที่ (50, 50) แลว ทาํ การวาดรปู สเี่ หลย่ี มตวั เดมิ โดยมี
จุดเรมิ่ ตนท่ี (100, 100) เชนเดิม ซึง่ ทาํ ใหเกดิ ภาพเสมอื นวา รปู สีเ่ หลีย่ มของเราเกดิ การยา ยท่ี
(เราไมไ ดลบรปู สเ่ี หลยี่ มตวั เดมิ ออก เพื่อใหผูอานเหน็ ตําแหนงทง้ั สอง)
ผูอา นอาจสงสยั วา ทาํ ไมเราถงึ ตอ งมคี ําส่ัง AffineTransform save = new AffineTransform()
กอ นการวาดภาพ และคาํ สัง่ g2.setTransfrom(save) หลงั จากที่วาดภาพไปยงั ตําแหนง ใหม
แลว ทั้งนกี้ ็เพราะวา ถาเราตองการวาดรปู อืน่ ๆ ในตาํ แหนง ทีม่ คี วามสมั พนั ธกบั รปู สเ่ี หลยี่ มตัว

316

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

แรก (พ้ืนทกี่ ารวาด – graphics space) เราตอ งจาํ คา เกาไวก อ น เพื่อใหส ามารถกลบั ไปใช
ตําแหนง ของการวาดภาพเกา น้นั ได ภาพที่ 10.11 แสดงถึงเหตุการณท ีว่ า นี้ รปู ทรงอ่ืน ๆ จะถกู
วาดใน graphics space ของสี่เหลย่ี มตวั แรก สวนรูปสเี่ หลยี่ มตวั ท่ถี ูกยา ยอยูใน graphics space
ของตวั เอง

ภาพท่ี 10.15 Graphics space

10.4.4 การหมุนภาพ (Rotate)
ขัน้ ตอนการหมนุ ภาพท่เี ราทาํ ก็ไมซบั ซอ นอะไรนัก หลงั จากทีเ่ ราวาดวงกลมเสรจ็ เรากย็ าย
จดุ เรม่ิ ตน (origin) ไปที่จุดกึ่งกลางของวงกลมดวยคําสง่ั

g2.translate(centerX, centerY);

หลงั จากนน้ั เรากส็ รา งรูปสเ่ี หลยี่ มทีม่ ขี นาดเลก็ ลง (จากรปู สีเ่ หลยี่ มในตัวอยา งกอ นหนานี)้ ดวย
คําส่ัง

Rectangle2D rec = new Rectangle2D.Double(x-50, y-50, width/4, height/3);

ข้นั ตอนตอ ไปก็เปน การวาดรูปส่ีเหลี่ยมนี้ โดยเราจะกําหนดใหม กี ารใชป ากกาอยูสองสคี ือ สีนํ้า
เงินและสเี หลอื ง ซึ่งเราจะเก็บไวใ น array colors

Color[] colors= {Color.blue, Color.yellow};

เราจะวาดรปู ส่ีเหล่ียมจาํ นวน 32 รปู รอบ ๆ จุดกงึ่ กลางของวงกลม เชน เดียวกับการเคล่ือนภาพ
กอ นหนาน้ี เราตอ งทาํ การหมนุ พ้ืนทกี่ ารวาด ตามองศาทเ่ี ราตอ งการ (ในทนี่ ี้ 15 องศา) หลังจาก
น้นั เราก็เลอื กสพี รอ มกับวาดรปู สีเ่ หลย่ี ม

for(int i = 0; i < 32; i++) {
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(15));
g2.transform(at);
g2.setPaint(colors[i % 2]);
g2.draw(rec);

}

สวนจดุ กึ่งกลางของวงกลมเราก็วาดวงกลมอกี วงดวยคาํ สัง่

Ellipse2D c = new Ellipse2D.Double();
c.setFrameFromCenter(centerX, centerY, centerX + 1, centerY + 1);

317

เรม่ิ ตนการเขยี นโปรแกรมดวย Java intro. to Java (FEU.faa)

g2.draw(c);

แตวงกลมวงนตี้ อ งวาดกอ นทเ่ี ราจะวาดรูปส่เี หลี่ยมสามสิบสองตัว (กอ นการยาย origin) เพราะ
ไมเ ชน นั้นแลว วงกลมวงนก้ี จ็ ะไปอยใู น graphics space ทีเ่ ราไดกาํ หนดใหม code ท้ังหมดของ
การหมุนภาพมดี ังนี้

ภาพท่ี 10.16 การหมุนภาพ

1: /**
2: Rotation with Graphics2D
3: */
4:
5: import java.awt.*;
6: import java.awt.geom.*;
7: import javax.swing.*;
8:
9: class Rotate {
10: public static void main(String[] args) {
11: ShapesFrame frame = new ShapesFrame();
12: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
13: frame.setVisible(true);
14: }
15: }
16:
17: class ShapesFrame extends JFrame {
18: private static final int WIDTH = 400;
19: private static final int HEIGHT = 400;
20:
21: public ShapesFrame() {
22: setTitle("Rotation");
23: setSize(WIDTH, HEIGHT);
24:
25: //add to panel
26: DrawingPanel panel = new DrawingPanel();
27: panel.setBackground(Color.white);
28: add(panel);
29: }
30: }
31:
32: class DrawingPanel extends JPanel {
33: public void paintComponent(Graphics g) {
34: super.paintComponent(g);
35: Graphics2D g2 = (Graphics2D)g;
36:
37: double x = 100;
38: double y = 100;
39: double width = 200;
40: double height = 150;

318

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

41:
42: //create a rectangle
43: Rectangle2D rectangle = new Rectangle2D.Double(x, y, width, height);
44:
45: //create line thickness
46: BasicStroke thickStroke = new BasicStroke(3.0f);
47: g2.setStroke(thickStroke);
48:
49: double centerX = rectangle.getCenterX();
50: double centerY = rectangle.getCenterY();
51: double radius = 100.0;
52:
53: //draw a circle
54: Ellipse2D circle = new Ellipse2D.Double();
55: circle.setFrameFromCenter(centerX, centerY,
56: centerX + radius, centerY + radius);
57: g2.draw(circle);
58:
59: //draw small cicle
60: Ellipse2D c = new Ellipse2D.Double();
61: c.setFrameFromCenter(centerX, centerY, centerX + 1, centerY + 1);
62: g2.draw(c);
63:
64: //translate origin to center of the circle
65: g2.translate(centerX, centerY);
66:
67: //create a smaller rectangle
68: Rectangle2D rec = new Rectangle2D.Double(x-50,

y-50, width/4, height/3);
69: Color[] colors= {Color.blue, Color.yellow};
70:
71: //draw rectangles around origin of the circle
72: for(int i = 0; i < 32; i++) {
73: AffineTransform at = new AffineTransform();
74: at.rotate(Math.toRadians(15));
75: g2.transform(at);
76: g2.setPaint(colors[i % 2]);
77: g2.draw(rec);
78: }
79: }
80: }

10.4.5 การปรบั เปลี่ยนขนาด (Scale) และการเปล่ียนรปู (Shear)

การปรบั ขนาดเราเรียกใช method scale() ดว ยคาทีเ่ ราตอ งการปรับสองคา โดยทค่ี า แรกจะเปน
ตัวกาํ หนดการเปล่ียนขนาดบนแกน x สวนคา ท่ีสองจะเปน ตวั กาํ หนดการเปลย่ี นขนาดบนแกน y
การเรียกใช method scale() ตองทาํ กบั object ทีม่ าจาก class AffineTransform เชน สมมติ
วาเรามี object จาก class AffineTransform ชอ่ื at เรากเ็ รยี ก method scale() ดังน้ี

at.scale(0.5, 05);

การเปลี่ยนรปู (shear) น้ันเรากเ็ รียกใช method shear() เชน

at.shear(0.5, 0.0);

การเปลย่ี นรูปดว ยการเรยี กใช method shear() นนั้ กต็ อ งกําหนดคา สองตวั เชน เดยี วกนั คาแรก
เปน ตัวกําหนดการเปลีย่ นรปู ทางแกน x สวนคา ทสี่ องเปน ตวั กาํ หนดการเปล่ยี นรปู ทางแกน y

319

เรม่ิ ตนการเขยี นโปรแกรมดว ย Java

รูปสเี่ หลี่ยมกอนการ เรยี ก method scale() เรียก method shear()
transform ดว ยคา 0.5 และ 0.5 ดวยคา 0.5 และ 0.0

ภาพที่ 10.17 การใช scale() และ shear() intro. to Java (FEU.faa)

โปรแกรม TransformShape.java ไดร วมเอาการหมนุ การปรบั ขนาด (scale) และการเปลยี่ นรปู
(shear) ไวใ นโปรแกรมเดยี วกนั โดยใหผ ใู ชเ ปน ผูเ ลือกวาจะทาํ อะไรกบั รูปส่ีเหล่ยี มท่อี ยูบ นจอ

1: /**
2: Shape transformation
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import java.awt.geom.*;
8: import javax.swing.*;
9:
10: class TransformShape {
11: public static void main(String[] args) {
12: TransformFrame frame = new TransformFrame();
13: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14: frame.setVisible(true);
15: }
16: }
17:
18: class TransformFrame extends JFrame implements ActionListener, ItemListener {
19: private static final int WIDTH = 300;
20: private static final int HEIGHT = 300;
21: DrawingPanel panel;
22: JButton doit;
23: JComboBox trans;
24:
25: public TransformFrame() {
26: setTitle("Shape Transformation");
27: setSize(WIDTH, HEIGHT);
28:
29: //list of transformations
30: trans = new JComboBox(new Object[] {"identity",

"rotate", "scale", "shear"});
31: trans.addItemListener(this);
32:
33: //activation button
34: doit = new JButton("Transform Now");
35: doit.addActionListener(this);
36:
37: //panel for list and button
38: JPanel top = new JPanel();
39: top.add(trans);
40: top.add(doit);
41: add(top, BorderLayout.NORTH);
42:
43: //drawing panel
44: panel = new DrawingPanel();
45: panel.setBackground(Color.white);
46: add(panel, BorderLayout.CENTER);
47: }
48:
49: //when button is hit, get item in the list and repaint
50: public void actionPerformed(ActionEvent e) {
51: panel.setTrans(trans.getSelectedIndex());
52: panel.render();
53: }

320

บทที่ 10: Graphics Programming

54:

55: //do nothing

56: public void itemStateChanged(ItemEvent e) {}

57: }

58:
59: //drawing panel

60: class DrawingPanel extends JPanel {
61: AffineTransform at = new AffineTransform();
intro. to Java (FEU.faa)
62: Rectangle2D rectangle;

63: boolean firstTime = true;
64: int width = 100, height = 100;

65: int w, h;

66:
67: //create a rectangle

68: public DrawingPanel() {

69: rectangle = new Rectangle2D.Double(0, 0, width, height);
70: }

71:

72: //repaint graphics
73: public void render() {

74: repaint();

75: }

76:

77: //get transformation

78: public void setTrans(int index) {

79: switch(index) {

80: case 0: at.setToIdentity();

81: at.translate(w/2, h/2); break;

82: case 1: at.rotate(Math.toRadians(45)); break;

83: case 2: at.scale(0.5, 0.5); break;

84: case 3: at.shear(0.5, 0.0); break;

85: }

86: }

87:

88: public void paintComponent(Graphics g) {

89: super.paintComponent(g);

90: Graphics2D g2 = (Graphics2D)g;

91: Dimension d = getSize();

92: w = d.width;

93: h = d.height;

94:

95: //create line thickness

96: BasicStroke thickStroke = new BasicStroke(3.0f);

97: g2.setStroke(thickStroke);

98:

99: //first running

100: if(firstTime) {

101: at.setToIdentity();

102: at.translate(w/2, h/2);
103: firstTime = false;

104: }

105: //save setting
106: AffineTransform save = g2.getTransform();

107: //get the center of the panel

108: AffineTransform toCenter = new AffineTransform();
109: //concatenate center to "at"

110: toCenter.concatenate(at);

111: toCenter.translate(-width/2, -height/2);
112: g2.transform(toCenter);

113: g2.draw(rectangle);
114:

115: //return to previous setting

116: g2.setTransform(save);
117: }

118: }

ส่ิงทผี่ อู านจะเห็นจากการ run โปรแกรม TransformShape.java ก็คือหนา ตางทมี่ ี drop-down
list ใหเ ลือกอยูส่ตี วั คอื identity, rotate, scale, และ shear ซงึ่ เปน ตัวเลอื กในการเปล่ยี นรปู ของ
ส่เี หลยี่ มทีอ่ ยใู นหนา ตา ง เมื่อ user กดปุม Transform Now1

1 เราจะพดู ถึงการสราง GUI ในบทตอ ไป

321

เร่มิ ตนการเขียนโปรแกรมดว ย Java intro. to Java (FEU.faa)

ภาพท่ี 10.18 การ run โปรแกรม TransformShape.java

10.4.6 การกาํ หนดความโปรง แสง (Transparency)
การทาํ ใหภ าพใดภาพหนึ่งทีอ่ ยบู นอกี ภาพหนึ่งใหสามารถมองเห็นภาพที่อยูดา นหลงั ไดเ ราตอ ง
ใช class AlphaComposite ชว ย ดังเชน ท่เี ราแสดงใหด ูในสว นของ code ทเี่ ราไดด ัดแปลงมา
จากการหมุนภาพกอ นหนา น้ี

int k = 1;
for(int i = 0; i < 32; i++) {

Composite original = g2.getComposite();
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(15));
g2.transform(at);
g2.setComposite(AlphaComposite.getInstance(

AlphaComposite.SRC_OVER, k*0.1f));
k++;
if(k > 10) k = 1;
g2.setPaint(colors[i % 2]);
g2.fill(rec);
g2.setComposite(original);
}

ขน้ั ตอนทเี่ ราตอ งทาํ กอ นก็คอื เก็บ composite ของ graphics2D ไวก อ น (เพอ่ื จะไดก ลบั มา
เรียกใชท ีหลัง) ดวยการเรียก

Composite original = g2.getComposite();

หลงั จากน้ันเรากก็ ําหนดคา composite ใหมท ี่เราตอ งการดว ยการเรยี กใช

g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, k*0.1f));

โดยท่เี ราตอ งสง คา SRC_OVER ซงึ่ เปน กฎ (mixing rule designator) กําหนดความโปรงแสง
พรอ มทั้งคาความโปรง แสง (alpha value) ซง่ึ อยูระหวา ง 0.0f ไปจนถงึ 1.0f โดยท่ีคา ศูนย
หมายถึงความโปรงเต็มที่ (transparent) และคา หนง่ึ หมายถึงความไมโปรง (opaque) เมอื่ เรา
วาดเสรจ็ เรากก็ าํ หนดคา composite กลบั ไปสูคา เดมิ ดวยคาํ ส่ัง

322

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)
G2.setComposite(original);

ภาพท่ี 10.19 แสดงถงึ ผลลัพธของการ run โปรแกรม TransparencyDemo.java

ภาพที่ 10.19 การกาํ หนดคา transparency

10.5 การใช Font
ในการกาํ หนดใหม ีการวาดตัวอกั ษรดวย font พเิ ศษนั้นเราจะตอ งสรา ง object จาก class Font
ดว ยชอ่ื ของ font, style ของ font และขนาดของ font นั้น ๆ โดยการกําหนดแลว font ท่ี Java
กําหนดใหเ ปน font ทดแทน font ที่มีอยูในเครื่องตาง ๆ (ซงึ่ อาจเปน font ท่ีมากับเคร่อื งนนั้ ๆ
ในระบบปฏบิ ตั กิ ารตาง ๆ เชน font ทชี่ อ่ื Helvetica กม็ ชี อื่ เปน Arial ใน Windows) มอี ยทู ัง้ หมด
หา ตัวคอื

• SansSerif
• Serif
• Monospaced
• Dialog
• DialogInput
Java จะจัดการกําหนด font ทดแทนเหลานใี้ หก บั font ทม่ี อี ยใู นเครอ่ื งน้นั ๆ เชน SansSerif
แทน Arial เปนตน
ตัวอยา งการกาํ หนด font เชน

Font serif = new Font("Serif", Font.BOLD, 36);

Parameter ในการเรยี กใช constructor ของ class Font มอี ยูสามตัวคือ ชอ่ื ของ font, style ท่ี
ใช และขนาดทใี่ ช โดย Java มี style ทใ่ี ชอยู เชน
Font.PLAIN
Font.BOLD
Font.ITALIC
เราอาจเรียกใช style เหลาน้ีรวมกันได เชน
Font.BOLD + Font.ITALIC
เมอ่ื เราสรา ง font เสรจ็ เราก็กาํ หนดใหพ ืน้ ท่กี ารวาดของเราใช font น้ี เชน

323

เร่ิมตนการเขยี นโปรแกรมดวย Java

Font serif = new Font("Serif", Font.BOLD, 36); intro. to Java (FEU.faa)
g2.setFont(serif);
g2.drawString("Hello Chiang Mai", x, y);

เนอ่ื งจากวา ผูใชอาจกําหนด font ข้ึนใชเองดงั น้ันเพื่อใหการแสดงขอ ความเปน ไปอยางดี
(สวยงาม เชน อยูกงึ่ กลางของพื้นทกี่ ารวาด) เราตอ งหาคณุ ลกั ษณะของ font ทใ่ี ช ซึ่งเราทาํ ได
ดว ยการสรา ง object จาก class FontRenderContext เชน

FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = serif.getStringBounds(message, context);

เมื่อเราได context ของ font แลว เรากส็ งขอความ และ context ดังกลา วไปให method
getStringBounds() ที่มีอยูใน class Font ซึง่ method ตวั น้จี ะใหค า ของกรอบสีเ่ หลย่ี มท่ี
ลอมรอบขอ ความดังกลา ว เราก็สามารถใชข อมลู นใ้ี นการวาดขอความใหไปปรากฏอยใู นหนาตาง
ได

ในการวาดขอ ความนน้ั เราจําเปน ท่จี ะตอ งรูจ กั กบั กรอบสเ่ี หลยี่ มดังกลาววาเกบ็ ขอมลู อะไรไวบา ง

baseline เสนสมมติท่อี ยูใตตวั อกั ษร
ascent ระยะจาก baseline ไปจนถงึ ดา นบนสดุ ของตวั อกั ษร เชน ดา นบนสดุ ของตัวอกั ษร 'b'
descent ระยะจาก baseline ไปจนถงึ ดานลา งสดุ ของตวั อกั ษร เชน ดา นลางสดุ ของ 'g'
leading ระยะหา งระหวา งแถว
height ความสงู จาก baseline ถึง baseline (ascent + leading + descent)

ascent

pocket

height

descent baseline leading

ภาพที่ 10.20 ขอมลู เก่ยี วกบั font

ในการวาดขอ ความใหไปอยตู รงกลางหนา ตา งนัน้ เราตอ งหาจดุ บนซา ยของขอความ และหา
baseline (หาคา ascent บวกกับคา y) เชน

double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;

double ascent = -bounds.getY();
double baseY = y + ascent;

g2.drawString(message, (int)x, (int)baseY);

ถา เราตอ งการหาคา leading และคา descent เราตองใช class LineMetrics ชว ย เชน

LineMetrics metrics = serif.getLineMetrics(message, context);
float descent = metrics.getDescent();
float leading = metrics.getLeading();

ผลลพั ธท ี่เราไดจากการ run โปรแกรม DrawingFont.java คือ

324

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

ภาพท่ี 10.21 ผลลัพธของโปรแกรม DrawingFont.java

และ code ของโปรแกรมมดี ังนี้

1: /**
2: Drawing special font
3: */
4:
5: import java.awt.*;
6: import java.awt.font.*;
7: import java.awt.geom.*;
8: import javax.swing.*;
9:
10: class DrawingFont {
11: public static void main(String[] args) {
12: FontFrame frame = new FontFrame();
13: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14: frame.setVisible(true);
15: }
16: }
17:
18: //frame contains message panel
19: class FontFrame extends JFrame {
20: private static final int WIDTH = 300;
21: private static final int HEIGHT = 200;
22:
23: public FontFrame() {
24: setTitle("Drawing Font");
25: setSize(WIDTH, HEIGHT);
26:
27: //add to panel
28: FontPanel panel = new FontPanel();
29: add(panel);
30: }
31: }
32:
33: //panel for message to be drawn on
34: class FontPanel extends JPanel {
35:
36: public void paintComponent(Graphics g) {
37: super.paintComponent(g);
38: Graphics2D g2 = (Graphics2D)g;
39: String message = "Hello Chiang Mai";
40:
41: //set font
42: Font serif = new Font("Serif", Font.BOLD, 36);
43: g2.setFont(serif);
44:
45: //calculate size of message
46: FontRenderContext context = g2.getFontRenderContext();
47: Rectangle2D bounds = serif.getStringBounds(message, context);
48:
49: //set top-left corner of message
50: double x = (getWidth() - bounds.getWidth()) / 2;

325

เร่ิมตนการเขียนโปรแกรมดว ย Java intro. to Java (FEU.faa)

51: double y = (getHeight() - bounds.getHeight()) / 2;
52:
53: //add ascent to y to get baseline
54: double ascent = -bounds.getY();
55: double baseY = y + ascent;
56:
57: //draw the message
58: g.drawString(message, (int)x, (int)baseY);
59: }
60: }

ถาหากวา เราตอ งการรูวา เครอื่ งที่เราใชอ ยมู ี font อะไรบา งเรากเ็ ขียน code เพือ่ แสดง font
เหลา นน้ั ดังน้ี

String[] names = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();

for(String fontName : names)
System.out.println(fontName);

[การใช class FontMetrics ในการแสดงขอ ความ]

วธิ กี ารอกี อนั หนึง่ ท่เี ราสามารถนํามาใชในการแสดงขอความ กค็ อื การใช class FontMetrics
วธิ ีการกค็ ลาย ๆ กบั การใช class FontRenderContext ทเ่ี ราทํากอ นหนาน้ี

FontMetrics fm = g2.getFontMetrics();

int width = fm.stringWidth(message);
int ascent = fm.getAscent();

int x = getWidth() / 2 - 2 - width / 2;
int y = getHeight() / 2 + ascent / 2;

g.drawString(message, x, y);

ผลลัพธท ี่เราไดกเ็ หมอื นกบั ทเ่ี ราไดในโปรแกรม DrawFont.java

10.6 การแสดง Image

ในการแสดงภาพที่อยใู นรูปแบบตา ง ๆ เชน GIF, JPG และอน่ื ๆ น้ันเราสามารถทจี่ ะทาํ ไดด ว ย
การอา นไฟลท ่ีเก็บ image น้ันดว ย method จาก class Image เชน

Image image = ImageIO.read(new File("cross.gif"));

หรอื ถาไฟลอยบู น Internet เราก็ใช

String URLname = "…";
Image image = ImageIO.read(new URL(URLname));

โปรแกรม DisplayImage.java แสดงภาพของไฟล cross.gif ในรปู แบบทเ่ี รยี กวา tile (เชน การ
แสดงภาพ background ของ Windows)

1: /**
2: Displaying and image
3: */
4:
5: import java.awt.*;
6: import java.io.*;
7: import javax.imageio.*;
8: import javax.swing.*;
9:
10: class DisplayImage {
11: public static void main(String[] args) {
12: ImageFrame frame = new ImageFrame();
13: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14: frame.setVisible(true);
15: }

326

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

16: }
17:
18: //frame contains message panel
19: class ImageFrame extends JFrame {
20: private static final int WIDTH = 300;
21: private static final int HEIGHT = 200;
22:
23: public ImageFrame() {
24: setTitle("Display Image");
25: setSize(WIDTH, HEIGHT);
26:
27: //add to panel
28: ImagePanel panel = new ImagePanel();
29: add(panel);
30: }
31: }
32:
33: //panel for image
34: class ImagePanel extends JPanel {
35: private Image image;
36:
37: public ImagePanel() {
38: try {
39: image = ImageIO.read(new File("cross.gif"));
40: }
41: catch(IOException e) {
42: e.printStackTrace();
43: }
44: }
45:
46: public void paintComponent(Graphics g) {
47: super.paintComponent(g);
48: Graphics2D g2 = (Graphics2D)g;
49:
50: if(image == null) return;
51:
52: //calculate width and height of this image
53: int width = image.getWidth(this);
54: int height = image.getHeight(this);
55:
56: //display image once at (0, 0)
57: g2.drawImage(image, 0, 0, null);
58:
59: //copy that image over the entire window
60: for(int i = 0; i * width <= getWidth(); i++)
61: for(int j = 0; j * height <= getHeight(); j++)
62: if(i + j > 0)
63: g2.copyArea(0, 0, width, height, i*width, j*height);
64: }
65: }

หลงั จากท่เี ราอา นไฟล cross.gif ไดแลว ในบรรทัดที่ 39 เรากส็ ง image น้ีไปให method
drawImage() ในบรรทดั ท่ี 57 ซ่งึ เปนการแสดง image นที้ ตี่ าํ แหนง (0, 0) และเพือ่ ใหร ปู
image ตัวเดียวกันนแ้ี สดงจนเต็มหนา ตา งเราก็เรยี ก method copyArea() ในบรรทดั ท่ี 63
ผลลพั ธท เี่ ราไดค อื

327

เรมิ่ ตน การเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

ภาพท่ี 10.22 การแสดง image ออกทางหนา ตาง

Method copyArea() มี parameter อยูห กตวั คือ
สองตวั แรก (x, y) เปน จดุ พกิ ัดบนซา ยของ image ท่ีมีอยู
ตัวทสี่ ามเปน ความกวางของ image
ตวั ทส่ี เ่ี ปนความสงู ของ image
ตวั ทห่ี า และหกเปนระยะทางจาก image ไปยงั กรอบของหนา ตา ง (หรอื พน้ื ท)่ี ท่ตี อ งการวาด
10.7 การตดั ภาพ (Clipping)
การตดั ภาพใน Java นั้นเราเรียกใชคาํ สัง่ clip() ของ Graphics2D เชน

g2.clip(clipShape);

ภาพตัวอยา งของการ run โปรแกรม Clipping.java ในครงั้ แรกกอนการเรยี ก clip()

ภาพท่ี 10.23 ภาพแสดงขอ ความกอนการ clip

การหา outline ของขอ ความใน Java เราจะใช font render context ชว ยดวยการเรยี ก

FontRenderContext context = g2.getFontRenderContext();

เม่ือกาํ หนด font ไดแ ลว (Font f = new Font("Serif", Font.PLAIN, 80);) เรากส็ รา ง
TextLayout object จาก context, font และ String ทีเ่ กบ็ ขอ ความทเ่ี ราตองการแสดง

TextLayout layout = new TextLayout("Chiang Mai", f, context);

หลงั จากที่ได layout แลวส่ิงทเ่ี ราตองทําตอไปคอื การหา outline ของตวั อักษรทอ่ี ยูใน layout
นัน้ เนือ่ งจากวา method getOutline() สง object ท่ีมาจาก class Shape ดว ยตําแหนง (0, 0)
ซ่งึ เปน ตาํ แหนง ทีไ่ มเ หมาะสมสาํ หรบั การวาดดว ย method ตา ง ๆ ดงั นั้นเราจึงตองยา ยจดุ เริ่มตน
ไปท่ี (0, 80) แทน (เราไมต อ งยา ยก็ไดแ ตภ าพท่ีไดอ าจไมเ ปนไปตามทตี่ ั้งใจไว)

328

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

AffineTransform t = AffineTransform.getTranslateInstance(0, 80);
Shape outline = layout.getOutline(t);

ขัน้ ตอนสดุ ทา ยทเ่ี ราตอ งทํากค็ อื การนําเอา outline ไปแปะไวก ับ clipping shape น้ัน

clipShape.append(outline, false);

กระบวนการดงั กลา วทง้ั หมดเราเขียนอยูภายใน method makeClip() ซึ่งเราจะเรียกใชใน
method paintComponent() ซ่งึ มสี ว นประกอบดังนี้

public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
if(clipShape == null) clipShape = makeClip(g2);
g2.draw(clipShape);
//clip activated with this command
g2.clip(clipShape);
//draw simple line pattern
p = new Point2D.Double(0, 0);
for(int i = 0; i < LINES; i++) {
double x = (2 * getWidth() * i) / LINES;
double y = (2 * getHeight() * (LINES - 1 - i)) / LINES;
q = new Point2D.Double(x, y);
g2.draw(new Line2D.Double(p, q));
}

}

ภาพทไี่ ดจ ากการ run ครง้ั แรกนน้ั เราไมไดเรยี ก g2.clip(clipShape) ดังท่เี หน็ ดานบนนี้ หลงั จาก
ท่ีเรา compile ใหมดว ยการเรยี ก method ดงั กลาวผลลพั ธท ีเ่ ราไดค ือ

ภาพที่ 10.24 ภาพแสดงขอ ความหลังจากการเรยี ก g2.clip(clipShape)

สวนของ code ทส่ี ราง line pattern กอ็ ยูใ น for/loop ทอ่ี ยใู นสวนสดุ ทา ยของ method
paintComponent() ซงึ่ เปนลายงา ย ๆ ท่ใี ชก ารแสดงเสนจาํ นวนเทากับหาสบิ เสนเปน หลกั

Code ทงั้ หมดของ Clipping.java ก็มีดงั นี้

1: /**
2: Clipping
3: */
4:
5: import java.awt.*;
6: import java.awt.geom.*;
7: import java.awt.font.*;
8: import javax.swing.*;
9:
10: class Clipping {
11: public static void main(String[] args) {
12: JFrame frame = new ClipFrame();
13: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

329

เร่ิมตนการเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

14: frame.setBackground(Color.black);
15: frame.setVisible(true);
16: }
17: }
18:
19: class ClipFrame extends JFrame {
20: private static final int WIDTH = 400;
21: private static final int HEIGHT = 150;
22:
23: public ClipFrame() {
24: setTitle("Clip Test");
25: setSize(WIDTH, HEIGHT);
26: JPanel panel = new ClipPanel();
27: panel.setBackground(Color.black);
28: add(panel);
29: }
30: }
31:
32: class ClipPanel extends JPanel {
33: private Point2D p, q;
34: private Shape clipShape;
35: private final int LINES = 50;
36:
37: public void paintComponent(Graphics g) {
38: super.paintComponent(g);
39: Graphics2D g2 = (Graphics2D)g;
40:
41: if(clipShape == null) clipShape = makeClip(g2);
42: g2.draw(clipShape);
43:
44: //clip activated with this command
45: g2.clip(clipShape);
46:
47: //draw simple line pattern
48: p = new Point2D.Double(0, 0);
49: for(int i = 0; i < LINES; i++) {
50: double x = (2 * getWidth() * i) / LINES;
51: double y = (2 * getHeight() * (LINES - 1 - i)) / LINES;
52: q = new Point2D.Double(x, y);
53: g2.draw(new Line2D.Double(p, q));
54: }
55: }
56:
57: //create clip shape
58: private Shape makeClip(Graphics2D g2) {
59: //create font render context and gradient paint
60: FontRenderContext context = g2.getFontRenderContext();
61: g2.setPaint(new GradientPaint(0, 0, Color.red, 300,

100, Color.yellow));
62: Font f = new Font("Serif", Font.PLAIN, 80);
63: //shape to return
64: GeneralPath clipShape = new GeneralPath();
65:
66: //create TextLayout object with font and context provided above
67: TextLayout layout = new TextLayout("Chiang Mai", f, context);
68: //translate base point to (0, 80) and set outline
69: AffineTransform t = AffineTransform.getTranslateInstance(0, 80);
70: Shape outline = layout.getOutline(t);
71: //append outline to the clipping shape
72: clipShape.append(outline, false);
73:
74: return clipShape;
75: }
76: }

10.8 การแสดงภาพเคลือ่ นไหวอยา งงา ย (Simple Animation)

การสรางภาพเคล่ือนไหวใน Java นน้ั ทาํ ไดส องวธิ ีคือ 1) ใช Thread และ 2) ใช Timer เราจะ
เริ่มกนั ดวยการใช Timer เพราะวา เปน การทาํ ที่งา ยทสี่ ดุ

330

บทท่ี 10: Graphics Programming intro. to Java (FEU.faa)

10.8.1 การใช class Timer ควบคมุ การเคลอื่ นท่ีของภาพ

โปรแกรมตัวอยา งท่เี ราทําเปน การแสดงขอความทวี่ ่ิงจากดานหน่งึ ของ frame ไปยงั อกี ดานหนึง่
ซึง่ ในการทําดังกลาวเราตองใชเ คร่ืองมือที่ Java เรียกวา Action Event เปนตวั ควบคุม ลองมาดู
code กนั

1: /**
2: Moving text to the right
3: */
4:
5: import java.awt.*;
6: import java.awt.Font.*;
7: import java.awt.event.*;
8: import javax.swing.*;
9:
10: class MovingText extends JFrame {
11: private static final int WIDTH = 300;
12: private static final int HEIGHT = 100;
13:
14: public MovingText() {
15: //add to panel
16: FontPanel panel = new FontPanel();
17: add(panel);
18:
19: //create timer for panel
20: Timer timer = new Timer(50, panel);
21: timer.start();
22: }
23:
24: public static void main(String[] args) {
25: MovingText frame = new MovingText();
26: frame.setTitle("Moving Text");
27: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
28: frame.setSize(WIDTH, HEIGHT);
29: frame.setVisible(true);
30: }
31: }
32:
33: //panel for message to be drawn on
34: class FontPanel extends JPanel implements ActionListener {
35: private String message = "Hello";
36: private int x = 0;
37: private int y = 40;
38: private Font serif = new Font("Serif", Font.BOLD, 24);
39:
40: //handle event
41: public void actionPerformed(ActionEvent e) {
42: repaint();
43: }
44:
45: //paint message
46: public void paintComponent(Graphics g) {
47: super.paintComponent(g);
48: Graphics2D g2 = (Graphics2D)g;
49:
50: //set font style
51: g2.setFont(serif);
52:
53: //reset x when message passes the right border
54: if(x > getWidth()) x = 0;
55:
56: //incrementing step
57: x += 3;
58:
59: //draw the message
60: g2.drawString(message, x, y);
61: }
62: }

331

เรมิ่ ตนการเขียนโปรแกรมดวย Java intro. to Java (FEU.faa)

หลังจากที่เราสราง panel สําหรับการวาดขอ ความแลว เรากส็ รา ง object จาก class Timer ที่
เปน ตวั ควบคมุ panel ทกุ ๆ ครั้งท่ีเวลาหมดไป 1/25 วนิ าที (บรรทดั ท่ี 20 – 21) panel จะถกู
วาดใหมดว ยระยะเวลาทห่ี างกนั ดงั กลาว

การวาดใหมท กุ ครัง้ เกิดข้ึนจากการเรียก method repaint() ใน method actionPerformed()
ซึ่งจะถกู เรยี กทกุ ๆ 1/25 วนิ าทีดังทก่ี ลาวไวแ ลว และทกุ ครง้ั ของการวาดใหมเ รากําหนดใหค า x
เพ่มิ ขึน้ สามหนว ย (บรรทดั ท่ี 57) และจะกลบั ไปเร่ิมท่จี ดุ เรมิ่ ตนใหมเม่อื คา x มากกวา คา สูงสดุ
ของความกวางของหนาตา ง (บรรทัดท่ี 54)

โปรแกรม MovingText.java ที่เราเขยี นขนึ้ เปนโปรแกรมอยา งงา ยที่ไมซบั ซอนมากมายนกั แต
ถา ผอู า นตอ งการทจ่ี ะใหการเคล่อื นไหวของภาพดีข้ึน ก็ตองใชวธิ กี ารอนื่ เชน การใช Thread กับ
การสรา ง off-screen image

โดยสรปุ แลวกระบวนการที่เกี่ยวขอ งกบั การสรา งภาพเคลือ่ นไหวทด่ี ีน้นั จะมขี ้นั ตอนตาง ๆ ดงั น้ี

• กระบวนการสรา ง loop สาํ หรบั ภาพเคลือ่ นไหว
• กระบวนการสรา งภาพ graphics
• กระบวนการกําจัดการส่นั (flashing, flicker) ของภาพ
• กระบวนการเคลอื่ นยา ยภาพบนหนา จอ

โปรแกรม MovingTextApp.java และโปรแกรม BouncingCrossBall.java จะเปน โปรแกรม
ตวั อยางทที่ าํ กระบวนการสรา งภาพเคลอ่ื นไหวดงั กลา ว เราจะเร่ิมดวยโปรแกรม
MovingTextApp.java

10.8.2 การสราง Off-screen Image และการสรา งภาพเคลอื่ นไหวดวย Thread

การวาดภาพทซ่ี ับซอนหรือซํ้ากันหลาย ๆ หนน้ัน (เชน ภาพการเคลอ่ื นทขี่ องตวั หนังสอื การ
เคลือ่ นที่ของ object ในหนา ตา ง) วิธกี ารทีด่ ที ่ีสดุ ในการรักษาความคมชดั และไมม ีการสนั่ ของ
ภาพ (flashing หรอื flicker) เมอ่ื มีการเคล่ือนไหว กค็ อื การสรา งภาพไวใ นทใี่ ดท่ีหนง่ึ กอ นนาํ มา
แปะไวในจอภาพ ซงึ่ ท่นี ิยมกนั ก็คอื การสราง off-screen image หรือท่เี รยี กวา buffered image

10.8.2.1 การเคลอ่ื นยา ยขอ ความ

โปรแกรม MovingTextApp.java ท่ีเราเขียนขน้ึ นใ้ี ช interface Runnable เปน ตัวสราง thread
ควบคุมการเคลอ่ื นท่ีของขอ ความทถี่ กู ําหนดไว โดยมี class หลกั ๆ อยสู อง class คือ class
MovingTextApp และ class MovingTextPanel ซงึ่ ตัวหลงั จะเปน ตัวสาํ คญั ของการสราง
animation ลองมาดู code ทง้ั หมดกนั

1: /**
2: Move string both directions, starting on the top-left
3: then back from bottom-right, repeatedly.
4: */
5:
6: import java.awt.*;
7: import java.awt.font.*;
8: import java.awt.geom.*;
9: import javax.swing.*;
10:
11: public class MovingTextApp extends JFrame {
12: private final int WIDTH = 300;
13: private final int HEIGHT = 210;
14: MovingTextPanel panel;
15:
16: public MovingTextApp(String text) {
17: setTitle("Moving Text");
18: setSize(WIDTH, HEIGHT);
19: setBackground(Color.black);
20:
21: //add animation panel to frame
22: panel = new MovingTextPanel(text);
23: add(panel);
24:
25: //start animation

332

บทที่ 10: Graphics Programming

26: panel.start();

27: }

28:

29: public static void main(String[] args) {

30: JFrame frame = new MovingTextApp("Hello, Chiang Mai");
31: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

32: frame.setVisible(true);
33: }
intro. to Java (FEU.faa)
34: }

35:
36: class MovingTextPanel extends JPanel implements Runnable {

37: protected Thread thread; //running thread

38: protected Image image; //image
39: protected Graphics offScreen; //off-screen image

40: protected String text; //string to display

41: protected Font font; //font used
42: protected int x, y; //position of string

43: protected int delay = 8; //default delay time

44: protected int offset = 1; //incremental moving step
45: protected Dimension d; //panel's size

46: boolean moveRight; //control moving direction

47:

48: //setup attributes

49: public MovingTextPanel(String message) {

50: setSize(300, 200);

51: font = new Font("Sans-serif", Font.BOLD, 24);

52: this.text = message;

53:

54: //set initial position of the text

55: d = getSize();

56: x = -d.width;

57: y = font.getSize();

58: moveRight = true;

59: }

60:

61: //paint image

62: public void paintComponent(Graphics g) {

63: update(g);

64: }

65:

66: //update image

67: public void update(Graphics g) {

68: super.paintComponent(g);

69: Graphics2D g2 = (Graphics2D)g;

70:

71: //create off-screen image if this is the first time

72: if(image == null) {

73: image = createImage(d.width, d.height);

74: offScreen = image.getGraphics();
75: }

76:

77: //use FontRenderContext to get text's size
78: offScreen.setFont(font);

79: FontRenderContext context = g2.getFontRenderContext();

80: Rectangle2D bounds = font.getStringBounds(text, context);
81:

82: //adjust the position of the text from previous frame

83: if(moveRight)
84: x += offset;

85: else
86: x -= offset;

87:

88: //if the text is completely off to the right end
89: //move the position to the lower right corner

90: if(x > d.width) {

91: x = d.width;
92: y = d.height - (int)bounds.getHeight();

93: moveRight = false;

94: }
95: //if the text is completely off to the left end

96: //move the position back to the upper left corner
97: if(x < (int)-bounds.getWidth()) {

98: x = (int)-bounds.getWidth();

99: y = font.getSize();

333

เรม่ิ ตน การเขยี นโปรแกรมดวย Java

100: moveRight = true; intro. to Java (FEU.faa)
}
101:
102: //set the pen color and draw the background
offScreen.setColor(Color.black);
103: offScreen.fillRect(0, 0, d.width, d.height);

104: //set the pen color and draw the text
105: offScreen.setColor(Color.green);
offScreen.drawString(text, x, y);
106:
107: //copy the off-screen image to the screen
g2.drawImage(image, 0, 0, null);
108: }

109: //start the thread
110: public void start() {

111: thread = new Thread(this);
thread.start();
112: }
113:
//stop the thread
114: public void stop() {

115: thread = null;
116: }

117: //run current thread
public void run() {
118:
119: while(Thread.currentThread() == this.thread) {
try {
120: Thread.currentThread().sleep(delay);
121: }
catch(InterruptedException e) {}
122:
//update image
123: repaint();
124: }
}
125:

126:
127:

128:

129:
130:

131:

132:
133:

134:
135:

136:

137:
138: }

Class MovingTextApp เปน class ท่ีเรากําหนด frame ท่เี ราตอ งการแสดงผลของการเคลอื่ นท่ี
ของขอความท่เี รากําหนดไว (ผานทาง constructor) ซ่ึงภายใน frame นี้เราจะทาํ การ add
panel การทํางานของ animation เขาไป

เรากาํ หนดใหจ ดุ เรม่ิ ตน ของขอความอยทู ี่ (-d.width, font.getSize() – บรรทดั ที่ 56 และ 57)
ทั้งนกี้ เ็ นื่องจากวาเรายงั ไมส ามารถเรยี กใช FontRenderContext ใน constructor ไดเ ราจึง
กาํ หนดจดุ เร่มิ ตน ไวท ีต่ ําแหนงนี้ ผูอา นตองคาํ นึงวา (x, y) เปนจุดเร่ิมตนของขอ ความ (บน-ซา ย)
เพราะฉะนน้ั เราจึงตองใหต ําแหนง นี้เปน ตาํ แหนงทีอ่ ยนู อกกรอบของ panel

หลังจากที่ x เปลี่ยนคา ตาม step ทีก่ ําหนดไวต ัวอักษรท่อี ยหู ลังสดุ ของขอ ความก็จะคอ ย ๆ
แสดงใหเ ราเหน็ ภายใน panel (ในบรรทดั ที่ 84 หรอื 86) สว นการตรวจสอบคาของ x ทงั้ ทีอ่ อก
นอกกรอบทางขวา และทางซา ยเราทําดงั นี้

if(x > d.width) {
x = d.width;
y = d.height - (int)bounds.getHeight();
moveRight = false;

}

if(x < (int)-bounds.getWidth()) {
x = (int)-bounds.getWidth();
y = font.getSize();
moveRight = true;

}

เรารูว า d.width เปน คาสูงสดุ ทางขวาดงั นน้ั ถาคา x ของขอความมากกวา d.width เราก็
กาํ หนดให x มคี าเปน d.width พรอ มกบั กาํ หนดใหคา y เปนคาทม่ี าจาก คา ของ
bounds.getHeight() ลบออกจาก d.height แตถ า x นอ ยกวาความยาวของขอ ความ (-

334

บทที่ 10: Graphics Programming intro. to Java (FEU.faa)

bounds.getWidth()) เราก็กําหนดให x เปน -bounds.getWidth() พรอ มกับกําหนดคา ของ y
ใหเปน font.getSize() ซง่ึ เปน คาทห่ี าไดงายทส่ี ดุ (มีวิธีการอ่ืนที่ดีกวา )
สวนคา ของ moveRight จะเปน ตัวกําหนดทศิ ทางการเคลือ่ นทขี่ องขอความเม่ือการเคลอ่ื นท่ีทาง
ใดทางหนง่ึ สนิ้ สุดลง
การสราง off-screen image กท็ ําไดด ว ยการเรยี ก method drawImage() ซึง่ กอ นทจ่ี ะเรยี กใช
น้นั เราตอ งสราง off-screen image ดวยคาํ ส่ัง

if(image == null) {
image = createImage(d.width, d.height);
offScreen = image.getGraphics();

}

เราสรา ง image ดวย method createImage() ดว ยขนาดของ panel และสรา ง off-screen
image ดวย image ท่ีเราสรางผา นทาง method getGraphics()
off-screen image จะเปนที่ ๆ เราเขยี นขอความตามทีก่ ําหนดไว รวมไปถึงคาตา ง ๆ ท่ีเรา
ตองการแสดง เชน สขี องพื้นหลัง และสีของปากกา เปนตน เมือ่ เราได image แลวเรากส็ ง
image ทไี่ ดไ ปให method drawImage() ของ graphics device ทาํ การแสดงใหเรา

offScreen.setColor(Color.black);
offScreen.fillRect(0, 0, d.width, d.height);
offScreen.setColor(Color.green);
offScreen.drawString(text, x, y);
g2.drawImage(image, 0, 0, null);

ภาพท่ี 10.19 แสดงผลลพั ธทเ่ี กิดขึน้ จากการ run โปรแกรม

ภาพท่ี 10.25 ผลลัพธบ างสว นของโปรแกรม MovingTextApp.java

10.8.2.2 การเคลอื่ นยา ย image (image animation)
ภาพเคลอื่ นไหวเกดิ จากการทเ่ี รานําเอาภาพที่ไดรับการเปลยี่ นแปลงในรูปแบบตา ง ๆ มาแสดงใน
เวลาทตี่ า งกนั ทําใหด ูเสมือนวาภาพเกดิ การเคลอ่ื นที ดังท่เี ราแสดงใหด ใู นโปรแกรม
MovingTextApp.java การแสดงภาพเคล่ือนไหวของ image ก็คลายกนั เพียงแตเราแทน
ขอความดว ย image ที่เราตองการใช กระบวนการทเ่ี ราทาํ ในโปรแกรม
BouncingCrossBall.java คอื
หลงั จากที่เราไดภ าพจากไฟลแ ลว

xball = ImageIO.read(new File("cross.gif"));

เราก็สราง off-screen graphics (offscreen) จาก buffered image (bi) ผอู านควรสังเกตวา เรา
สรา ง buffered image ดว ยขนาดของพ้ืนทก่ี ารวาด ซึง่ คํานวณมาจากขนาดทถี่ กู กําหนดไว

335

เรม่ิ ตน การเขียนโปรแกรมดวย Java intro. to Java (FEU.faa)

(getSize()) ตัวแปรตวั ทสี่ ามใน constructor ของ BufferedImage() เปน คาของสี (alpha
value) [กระบวนการน้ตี างจากการสราง off-screen image ในโปรแกรม MovingTextApp.java
กอ นหนา น้ีเราสรา ง image ดวยการเรยี กใช createImage() หลงั จากนัน้ กก็ าํ หนด off-screen
image จาก image ทีส่ รา งดวยการเรยี ก getGraphics()] สว นกระบวนการทีเ่ หลือกค็ ลา ย ๆ กนั

Dimension d = getSize();
bi = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
offscreen = bi.createGraphics();

หลังจากน้ันเรากส็ ง image ทตี่ องการวาดไปให off-screen graphics context ดวยคําสง่ั
offscreen.drawImage(xball, at, this) โดยที่ at จะเปน พ้นื ที่การวาดภาพใหมท่ีเราไดคํานวณ
ข้ึนดงั code ทแ่ี สดงใหด ูน้ี

x += dx;
y += dy;
if(x < 0 || x > d.width - width)

dx = -dx;
if(y < 0 || y > d.height - height)

dy = -dy;
AffineTransform at = new AffineTransform();
at.setToIdentity();
at.translate(x, y);
offscreen.drawImage(xball, at, this);

คา dx และ dy เปนระยะทางการเปล่ียนตําแหนง ของ image บนแกน x และแกน y โดยเราตอ ง
ตรวจสอบดวู า คา ที่เปล่ยี นไปนน้ั อยใู นกรอบของพืน้ ทท่ี ่เี ราไดก าํ หนดไว ถาเกนิ ไปทางใดทาง
หนึ่งเราก็เปลี่ยนทศิ ทางการเคล่ือนทีข่ อง image ไปในทางตรงกนั ขา ม
เราจะใชคา x และ y ที่หาไดใ หมน ี้เปน คา สําหรบั การยายพนื้ ทขี่ องการวาดออกไป (เหมือนที่เรา
ทํากอ นหนา นใี้ นเรื่องของการยายภาพ) โดยการสราง object จาก class AffineTransform
หลงั จากยา ยเสรจ็ เราก็วาด image ลงบนพ้นื ท่ดี งั กลา ว
หลงั จากนั้นเราก็สง object ไปให graphics context เพ่อื แสดงผล

Graphics2D g2 = (Graphics2D)g;
g2.drawImage(bi, 0, 0, null);

และเพอ่ื เปน การชวยคืนหนว ยความจาํ ใหก ับระบบเรากเ็ รยี กใช method dispose() กับ off-
screen image น้ี

offscreen.dispose();

อีกคร้งั หนง่ึ ข้ันตอนการทาํ ให image ของเราเคล่ือนท่ีนนั้ เกดิ ข้นึ จากการยา ยจอภาพดวยการใช
method translate() เหมอื นกบั ที่เราไดท าํ มากอ นหนา
ภาพท่ี 10.26 แสดงขั้นตอนการเรยี ก method ท่ีเกดิ ขึน้ ในโปรแกรม BouncingCrossBall.java

336

canvas.start(); บทท่ี 10: Graphics Programming

Thread thread = new Thread(this);
thread.start();

run() stop() intro. to Java (FEU.faa)

repaint()

paintComponent()

update()

ภาพที่ 10.26 ข้ันตอนการแสดงภาพเคล่ือนไหว

การทําให flashing หมดไปจากการแสดงผลน้ันทาํ ไดสองวิธีคือ 1) เรยี กใช update() และ 2)
ใช double buffering (off-screen image)

การ override method update() จาํ เปน เพราะวา การเรยี ก method repaint() จะไปเรยี ก
method update() ซง่ึ จะทาํ ใหเกิดการ clear พน้ื หลังของพ้ืนที่ในการวาด ดงั นนั้ การเขยี น
update() ขึน้ ใหมจ งึ ทาํ ใหกระบวนการที่ไมจ าํ เปน เชน clear พ้นื หลังดงั ท่ีกลาวมาแลวหมดไป
แตก็ยังไมก ารนั ตวี า repaint() จะไปเรียก update() โดยตรง(อาจไปเรียก paintComponent()
กไ็ ด) ดังนั้นวธิ กี ารที่จะใหเกดิ การเรยี ก update() ก็คือให paintComponent() เรยี ก update()
แทน

กระบวนการอกี อันหน่ึงที่จะชวยลดความไมส วยงามของภาพเคลือ่ นไหวก็คือการใช double
buffering ซงึ่ โปรแกรม BouncingCrossBall.java ก็ไดนาํ มาชว ยใชเหมอื นกนั วิธกี ารก็คือวาด
frame ท้ังหมดใหมใน buffer แลว จงึ นํามาแปะไวใ น graphics context ดงั ทีไ่ ดอธิบายไวใ น
ขัน้ ตอนของการสราง image กอนหนา นี้

code ของโปรแกรม BouncingCrossBall.java มีดังนี้

1: /**
2: Bouncing cross-ball image
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import java.io.*;
8: import java.awt.image.*;
9: import java.awt.geom.*;
10: import javax.imageio.*;
11: import javax.swing.*;
12:
13: class BouncingCrossBall extends JFrame {
14: private final int WIDTH = 300;
15: private final int HEIGHT = 200;
16: CrossBallCanvas canvas;
17:
18: public BouncingCrossBall() {
19: setTitle("Bouncing ball");
20: setSize(WIDTH, HEIGHT);
21:
22: //add animation canvas to frame
23: canvas = new CrossBallCanvas();
24: canvas.setBackground(Color.white);

337

เรม่ิ ตนการเขยี นโปรแกรมดวย Java

25: add(canvas);

26:

27: //start animation

28: canvas.start();

29: }
30:

31: public static void main(String[] args) {
32: JFrame frame = new BouncingCrossBall();
intro. to Java (FEU.faa)
33: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

34: frame.setVisible(true);
35: }

36: }

37:
38: //animation canvas

39: class CrossBallCanvas extends JPanel implements Runnable {

40: Thread thread; //animation control thread
41: BufferedImage bi; //buffer image

42: Image xball; //image used

43: Graphics2D offscreen; //off screen graphics
44: int x, y; //coordinates

45: int dx, dy; //increment amount on x and y

46: int width, height; //width and height of image

47: long delay = 100; //sleep time

48:

49: public CrossBallCanvas() {

50: //get the image

51: try {

52: xball = ImageIO.read(new File("cross.gif"));

53: }

54: catch(IOException e) {}

55:

56: //incremental units on x and y directions

57: dx = dy = 10;

58: }

59:

60: //paint graphics

61: public void paintComponent(Graphics g) {

62: super.paintComponent(g);

63: update(g);

64: }

65:

66: //update graphics

67: public void update(Graphics g) {

68: super.paintComponent(g);

69: if(xball == null) {

70: System.out.println("no image file");

71: return;

72: }

73:
74: //calculate size of image

75: width = xball.getWidth(this);

76: height = xball.getWidth(this);
77:

78: //get the size of the panel

79: Dimension d = getSize();
80: //create a buffered image

81: bi = new BufferedImage(d.width, d.height,

82: //get graphics context BufferedImage.TYPE_INT_ARGB);

83: offscreen = bi.createGraphics();
84:

85: //update incremental amount

86: x += dx;
87: y += dy;

88:

89: //restrict image within the boundary of the canvas
90: if(x < 0 || x > d.width - width)

91: dx = -dx;

92: if(y < 0 || y > d.height - height)
93: dy = -dy;

94:
95: //transform the image

96: AffineTransform at = new AffineTransform();

97: at.setToIdentity();

338

บทที่ 10: Graphics Programming

98: at.translate(x, y); intro. to Java (FEU.faa)
99: offscreen.drawImage(xball, at, this);
100:
101: //draw buffered image to graphics context
102: Graphics2D g2 = (Graphics2D)g;
103: g2.drawImage(bi, 0, 0, null);
104:
105: offscreen.dispose(); //return memory
106: }
107:
108: //start the thread
109: public void start() {
110:
111: thread = new Thread(this);
112: thread.start();
113: }
114:
115: //stop the thread
116: public void stop() {
117:
118: if(thread != null)
119: thread.interrupt();
120:
121: thread = null;
122: }
123:
124: //run the thread
125: public void run() {
126:
127: long start = System.currentTimeMillis();
while(Thread.currentThread() == thread) {
128:
129: try {
130: start += delay;
131: Thread.currentThread().sleep(
132: Math.max(0, start-System.currentTimeMillis()));
133:
134: }
135: } catch(InterruptedException ie) {}

//update canvas
repaint();
}
}

การกาํ หนดคา delay ภายใน loop ของการแสดงภาพในแตล ะ frame อาจไมอยใู นชวงเวลาที่
เทากนั วิธีการแกก ค็ อื จาํ เวลาเรมิ่ ตน ของ loop ไว ใชค านีบ้ วกกบั คา delay แลว นาํ เอาคา ของ
เวลาทหี่ าไดท กุ ครงั้ ท่ี loop ประมวลผลมาหกั ออก ดงั ที่ไดแ สดงไวใ น method run()

ผลลัพธท ีไ่ ดจ ากโปรแกรมคือ

ภาพที่ 10.27 การเคลือ่ นไหวของ image

ถาเราไมตองการทจ่ี ะเรยี กใช method translate() ของ class AffineTransform ในการยา ยจดุ
วาดภาพเราก็สราง off-screen image ของภาพดว ยตําแหนง (0, 0) ใน method drawImage()

339

เริ่มตนการเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

ดงั น้ี offscreen.drawImage(xball, 0, 0, this); หลงั จากน้นั กส็ ง off-screen image ตัวนีไ้ ปให
graphics context พรอมกับตาํ แหนง ของ (x, y) ดังน้ี g2.drawImage(bi, x, y, null); เชน ทเี่ รา
แสดงใหด ูในสว นของ code น้ี

Dimension d = getSize();
bi = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
offscreen = bi.createGraphics();
offscreen.drawImage(xball, 0, 0, this);

x += dx;
y += dy;

if(x < 0 || x > d.width - width)
dx = -dx;

if(y < 0 || y > d.height - height)
dy = -dy;

Graphics2D g2 = (Graphics2D)g;
g2.drawImage(bi, x, y, null);

การใช AffineTransform นัน้ ทําใหเราสามารถทาํ การเปลย่ี นรปู (transform) ของสิง่ ทเ่ี รา
ตอ งการแสดงออกทางหนา จอไดดีและมลี ูกเลน มากขนึ้ เชน การทําใหเกิดความโปรงแสง การ
ยอหรือขยายภาพ แตถ า เราตอ งการเพียงแคภ าพทเ่ี คลือ่ นไหวแตไ มเ ปลยี่ นรูป การใช off-
screen image กน็ า จะเพยี งพอแลว

[การใช mouse เคล่ือนยา ยภาพ]

การยา ยภาพดว ย mouse ทาํ ไดด วยการคอยฟงเหตุการณ (event listener) ของ mouse วา
เคล่อื นทไ่ี ปอยู ณ ตาํ แหนง ใด เม่อื เราหาตาํ แหนงทวี่ า ไดแลว เราก็ทาํ การวาดใหม ณ ตาํ แหนง น้ี
ซง่ึ ก็คลา ยกับโปรแกรม BouncingCrossBall.java ที่เราทาํ กอ นหนาน้ี ตา งกนั ก็เพยี งแตเราใช
mouse เปนตัวลากภาพไปสตู าํ แหนง ท่ีเราตอ งการเทานั้นเอง

1: /**
2: Bouncing cross-ball image
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import java.io.*;
8: import java.awt.image.*;
9: import java.awt.geom.*;
10: import javax.imageio.*;
11: import javax.swing.*;
12:
13: class MovingCrossBall extends JFrame {
14: private final int WIDTH = 300;
15: private final int HEIGHT = 200;
16: CrossBallCanvas canvas;
17:
18: public MovingCrossBall() {
19: setTitle("Bouncing ball");
20: setSize(WIDTH, HEIGHT);
21:
22: //add animation canvas to frame
23: canvas = new CrossBallCanvas();
24: canvas.setBackground(Color.white);
25: add(canvas);
26: }
27:
28: public static void main(String[] args) {
29: JFrame frame = new MovingCrossBall();
30: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
31: frame.setVisible(true);
32: }
33: }
34:
35: //animation canvas
36: class CrossBallCanvas extends JPanel {
37: BufferedImage bi; //buffer image

340

บทท่ี 10: Graphics Programming

38: BufferedImage xball; //image used
39:
40: Graphics2D offscreen; //off screen graphics
41:
42: int x, y; //coordinates
43:
44: int width, height; //width and height of image
45:
46: public CrossBallCanvas() { intro. to Java (FEU.faa)
47: //get the image
48: try {
49: xball = ImageIO.read(new File("cross.gif"));
50: }
51: catch(IOException e) {}
52:
53: addMouseMotionListener(new MouseMotionHandler());
54:
55: //calculate size of image
56: width = xball.getWidth(this);
57: height = xball.getHeight(this);
58: }
59:
60: //paint graphics
61:
62: public void paintComponent(Graphics g) {
63: super.paintComponent(g);
64:
65: update(g);
66:
67: }
68:
69: //update graphics
70:
71: public void update(Graphics g) {
72: if(xball == null) {
73:
System.out.println("no image file");
74:
75: return;
76: }
77:
78: //get the size of the panel
79: Dimension d = getSize();
80:
81: //create a buffered image
82: bi = new BufferedImage(d.width, d.height,
83:
84: BufferedImage.TYPE_INT_ARGB);
85:
86: //get graphics context
87: offscreen = bi.createGraphics();
88:
89: offscreen.drawImage(xball, 0, 0, this);
90:
91: //control moving area within the canvas
92:
93: if(x < 0) x = 0;
94:
95: if(x > d.width - width) x = d.width - width;
96: if(y < 0) y = 0;
97:
98: if(y > d.height - height) y = d.height - height;
99:
100: } //draw buffered image to graphics context
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(bi, x, y, null);

offscreen.dispose(); //return memory
}

//get new (x, y) and repaint, not sufficient to drag
//image across screen but will do the job
class MouseMotionHandler extends MouseMotionAdapter {

public void mouseDragged(MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();

}
}

Method ทสี่ าํ คญั ทที่ ําใหเ กดิ การวาดภาพใหมก ค็ ือ method mouseDragged() ที่อยใู น inner
class MouseMotionHandler กระบวนการท่เี กดิ ขน้ึ เปนกระบวนการงาย ๆ ทเี่ ราทาํ ข้นึ นน่ั กค็ อื
หาคา (x, y) ณ ตาํ แหนง ท่ี mouse อยูดวย method getX() และ getY() ซึ่งเมอ่ื ําดแ ลว เราก็ทาํ
การวาดใหมด วย repaint() แตเ ราตองบอกใหโ ปรแกรมรเู หตกุ ารณท ่ีเกิดขน้ึ กบั mouse ดว ยการ
เรียก addMouseMotionListener(new MouseMotionHandler()); ในบรรทดั ที่ 50

341

เรม่ิ ตนการเขียนโปรแกรมดวย Java intro. to Java (FEU.faa)

วธิ กี ารเคลอ่ื นยา ยภาพดว ย mouse วธิ ีน้อี าจไมใชวธิ ที ่ดี ที ส่ี ดุ ทั้งน้ีกเ็ พราะวาเราไมไดตรวจสอบ
วา ในขณะทผ่ี ูใชก ดปมุ ซา ยของ mouse น้นั cursor อยูบนภาพท่ีเราตองการเคลอ่ื นยายหรอื ไม
เราเพียงแคต รวจสอบวา มกี ารกดหรอื ไมเทา นน้ั ซงึ่ จะทําใหเกิดการวาดใหมท ุกครัง้ ท่มี ีการกด
อยางไรกต็ ามวธิ กี ารน้เี ปน วิธที งี่ ายทส่ี ดุ
สรุป
ในบทนี้เราไดพดู ถงึ กระบวนของการสรางภาพในรูปทรงเรขาตาง ๆ รวมไปถงึ การเปล่ยี นรูปแบบ
ของภาพ การเคล่ือนยายภาพ และการสรางภาพเคล่ือนไหวอยางงา ย โดยรวมแลวเราไดพ ดู ถงึ
9 การสรางรปู ทรงจาก class Graphics
9 การสรา งรปู ทรงจาก class Groahics2D
9 การปรับเปล่ยี นรูป เชน scale, rotate, shear, และ translate
9 การสรา งภาพเคลอื่ นไหวดว ย Timer และ Thread
9 การเคล่ือนยา ยภาพดวย mouse
แบบฝก หดั
1. จงปรับปรงุ โปรแกรม TransparencyDemo.java ใหแ สดงภาพที่เกิดขึ้นในรปู แบบของ

animation (รปู ตาง ๆ ทว่ี าดจะเคล่ือนทีพ่ รอ มกบั เปลีย่ นคา ความโปรง แสง)
2. จงออกแบบโปรแกรมทที่ ําการยายภาพไปยังตําแหนง ทก่ี าํ หนดใหจ ากผใู ช โดยตาํ แหนง ที่

กาํ หนดใหจ ะอยูในรปู แบบของคา (x, y) โปรแกรมจะแสดงขอความไปยงั ผูใ ชถาตําแหนง
ดงั กลาวไมอยใู นขอบเขตของหนา ตาง
3. จงออกแบบโปรแกรมทเี่ ชือ่ มตอ จุดตา ง ๆ ที่ผใู ชก าํ หนด เชน ถาผใู ชเลอื กตําแหนงใด
ตําแหนงหน่ึงในหนา ตาง โปรแกรมจะแสดงจดุ ณ ตําแหนง นนั้ และเมอ่ื ผใู ชก าํ หนดตาํ แหนง
ท่ีสองโปรแกรมกจ็ ะทําการเชอื่ มจุดทั้งสองดว ยการวาดเสน ระหวางจดุ ท้ังสองน้นั
4. จงออกแบบโปรแกรมที่แสดงคา ความโปรงแสงของรปู สเี หลยี่ มท่ีกาํ หนดไวใ นโปรแกรม จาก
คาเร่ิมตนทก่ี ําหนดไวไ ปจนถงึ คาสนิ้ สดุ ท่กี ําหนดไว เม่อื ผูใ ชกดปุม Enter โดยกําหนดใหค า
ความโปรง แสงเพิ่มขึน้ ทีละหนว ย (ตามความเหมาะสม)
5. จงออกแบบโปรแกรมทแี่ สดงการเคลอ่ื นที่ของ image หรือภาพสองมติ ิ เชน รปู ทรงกลม
สองรูป โดยมีขอ กาํ หนดวา ถา รปู ทรงกลมทง้ั สองชนกนั จะตอ งว่ิงยอนกลับ (สะทอ นออก)
เชนทแี่ สดงไวใ นตัวอยา งเมอ่ื รปู วง่ิ ชนขอบหนา ตาง
6. จงออกแบบโปรแกรมทข่ี ยายภาพสองมติ เิ มอ่ื ผูใชกดปมุ ซายของ mouse และยอขนาดลง
เมอ่ื ปุมขวาถูกกด

342

ในบททผ่ี านมาเราไดเหน็ การสรา ง Frame แบบคราว ๆ เพอ่ื ใชรองรับการสรา งภาพสองมติ แิ บบ intro. to Java (FEU.faa)
ตาง ๆ ในบทนเี้ ราจะมาดูกนั ถงึ สว นประกอบบางสว นของ Graphical User Interface (GUI) ท่ี
Java มใี หโดยเฉพาะการสราง GUI ดว ย Swing พรอ มกันนเ้ี ราจะมาดูกันถงึ วธิ ีการโตตอบกบั
user ในรูปแบบตาง ๆ ท่ี Java มีให เชน การใชปมุ และการสนองตอบเมื่อ user กดปมุ เปนตน
ในอดีตการจดั วางองคประกอบใน UI เปนเรอ่ื งทไ่ี มง า ยเทา ไรเนื่องจากวา Java ไมมตี ัวชว ยการ
จัดวาง (Layout Manager) ทด่ี พี อ user จะตอ งเปน ผกู าํ หนดสว นตาง ๆ ในการจัดวางอยา ง
ละเอยี ด (ไมใ ช Visual Editor ท่ีผใู ชส ามารถลากเอาสวนประกอบมาแปะไวใ น container ได)
แตในปจ จุบนั เรามโี ปรแกรมมากมายทส่ี ามารถชวยใหการสรา ง GUI ใน Java ทําไดงายข้นึ เชน
Netbeans หรอื Eclipse
ในเบ้อื งตนนี้เราจะแสดงถงึ กระบวนการและวธิ กี ารในการสรา งองคประกอบตา ง ๆ อยา งงา ย ๆ
และเมื่อถงึ คราวที่ตองใชเ ครื่องมือชว ย เราก็จะแสดงถึงการใช Netbeans ในการสรา ง
application เหลานนั้
หลงั จากจบบทเรยี นนแ้ี ลว ผอู านจะไดท ราบถงึ

• การกําหนด container สําหรบั การใส component เชน JPanel หรือ JFrame
• การสราง Basic controls เชน JButton, JTextField, JTextArea, JLabel,

JCheckBox, JRadioButton, JComboBox, JSlider, JSpinner, JProgressBar,
ProgressMonitor เปน ตน
• การสนองตอบตอ event ท่เี กดิ ขนึ้ ดวยการใช listener ตา ง ๆ เชน ActionListener,
ChangeListener และ KeyListener
11.1 การสรา ง Swing Application อยา งงา ย
เพอื่ ใหมองเหน็ ภาพในการสราง application อยางงา ยเราจงึ เลอื กทจี่ ะแสดงการสรา งโปรแกรม
ตวั อยา งท่ีแสดงวันที่และเวลาไปยงั หนาจอเมอ่ื ผใู ชกดปุม หรือใชป มุ ALT+T แทน

ภาพท่ี 11.1 การสรางปมุ และการตอบสนองตอ การกดปุม ของ user

เรม่ิ ตน การเขยี นโปรแกรมดว ย Java intro. to Java (FEU.faa)

โปรแกรมตวั อยา งของเรากําหนดใหมปี มุ อยูเ พยี งแคห นึ่งปุม โดยภายในปมุ เรากําหนดใหม ี text
และ icon กระบวนการในการสรางปมุ ตวั นี้มดี งั น้ี

ImageIcon clockIcon = createImageIcon("clock.gif");
button = new JButton("Time", clockIcon);
button.setVerticalTextPosition(AbstractButton.CENTER);
button.setHorizontalTextPosition(AbstractButton.LEADING);
button.setMnemonic(KeyEvent.VK_T);
button.addActionListener(this);

ข้นั ตอนแรกเราหาไฟลท ่ีตองการแสดงบนปมุ ดวยการสราง icon จาก class ImageIcon ดว ย
ไฟล clock.gif หลงั จากนัน้ เรากส็ รางปมุ ดวยการเรยี กใช constructor ของ class JButton ดว ย
parameter 2 ตวั คือ ชือ่ ท่ปี รากฏบนปมุ และ icon ที่อยบู นปมุ ตัวนี้ การกําหนด icon นั้นเราสราง
method createImageIcon() มาชว ย

protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = DateTimeSwingApp.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
}
else {
System.err.println("Couldn't find file: " + path);
return null;
}

}

โดยในตอนแรกเราตอ งหา image URL ดว ยไฟลท ส่ี งมาใหซ ึ่งถามีไฟลอ ยใู นแฟม ทีก่ าํ หนดไว
จรงิ เราก็จะสราง object ดวยการเรียกใช constructor ของ class ImageIcon ดว ย URL นี้ การ
คน หาไฟลท วี่ า นี้เราจะกาํ หนดใหการคน หาอยใู นแฟม ทโ่ี ปรแกรมของเราอยู (ผูอา นควรสงั เกตถึง
คาํ สั่งนี้ java.net.URL imgURL = DateTimeSwingApp.class.getResource(path); ซ่งึ เปน
การบอกถงึ ที่อยูของไฟลท ี่ path ชี้อยู

เมื่อเราได icon แลวเรากต็ อ งกําหนดตาํ แหนง ของ icon และชื่อที่อยบู นปุม ดวยการกาํ หนดให
ช่ือของปุมมกี ารจัดวางอยกู อน icon พรอ มทง้ั อยูกึง่ กลางของปมุ ในแนวต้งั (ดภู าพที่ 11.1
ประกอบ) ดวยคําส่ัง

button.setVerticalTextPosition(AbstractButton.CENTER);
button.setHorizontalTextPosition(AbstractButton.LEADING);

และเพอ่ื ใหผ ใู ชส ามารถใชป ุม ALT กบั ตวั อกั ษร T (combination key stroke) เรากใ็ ชค ําส่ัง

button.setMnemonic(KeyEvent.VK_T);

สว นสาํ คญั ทที่ าํ ใหป มุ มกี ารสนองตอบตอ การกดกค็ ือ คําสงั่ ทกี่ าํ หนดใหม ีการฟงเหตกุ ารณท ี่จะ
เกิดข้ึน

button.addActionListener(this);

ซึ่งเมื่อผใู ชกดปมุ method actionPerform() ก็จะถกู เรยี ก การแสดงผลลพั ธท ่เี รากาํ หนดไวก็
เกดิ ขนึ้ โดยเรากาํ หนดใหแสดงวันที่และเวลาในรปู แบบของภาษาไทย

public void actionPerformed(ActionEvent e) {
Date today;
String dateOut;
DateFormat dateFormatter;
Locale thLocale = new Locale("th", "TH");

dateFormatter = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.SHORT, thLocale);

today = new Date();
dateOut = dateFormatter.format(today);

output.setText(dateOut + " " + thLocale.toString());
}

344

บทที่ 11: GUI และ Event Handling intro. to Java (FEU.faa)

ผอู านจะเหน็ วา method actionPerformed() ของเราเรียกใช class Date() ในการแสดงผลโดย
เราจะกําหนดใหใชว ันและเวลาดว ย locale ทเ่ี ปนไทยใน format ท่เี รากาํ หนดจาก class
DateFormatter ซ่ึงเมอื่ ไดแลว เรากส็ ง ผลลัพธท ่ีไดไ ปให output ซึ่งเปน object ทเ่ี ราสรางขึ้น
จาก class JLabel ดว ยการเรยี กใช method setText() ดว ยคา ของ dateOut และ locale ทเ่ี รา
ใช

Code ท้งั หมดของโปรแกรมตวั อยา งมีดังน้ี

1: /**
2: Date and Time Swing Applicsation
3: */
4:
5: import javax.swing.*;
6: import java.awt.*;
7: import java.awt.event.*;
8: import java.util.*;
9: import java.text.*;
10:
11: class DateTimeSwingApp {
12: public static void main(String[] args) {
13: JFrame.setDefaultLookAndFeelDecorated(true);
14: JFrame frame = new DateTimeFrame();
15: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: frame.setVisible(true);
17: }
18: }
19:
20: class DateTimeFrame extends JFrame {
21: private static final int WIDTH = 400;
22: private static final int HEIGHT = 100;
23:
24: //create a frame and a panel
25: public DateTimeFrame() {
26: setTitle("Date and Time");
27: setSize(WIDTH, HEIGHT);
28: JPanel panel = new DateTimePanel();
29: add(panel);
30: }
31: }
32:
33: class DateTimePanel extends JPanel implements ActionListener {
34: protected JButton button;
35: protected JLabel output;
36:
37: public DateTimePanel() {
38: //get image icon for the button
39: ImageIcon clockIcon = createImageIcon("clock.gif");
40: //create a button
41: button = new JButton("Time", clockIcon);
42: //set text position before icon
43: button.setVerticalTextPosition(AbstractButton.CENTER);
44: button.setHorizontalTextPosition(AbstractButton.LEADING);
45: //alt+t key activation
46: button.setMnemonic(KeyEvent.VK_T);
47: //add a listener to this button
48: button.addActionListener(this);
49:
50: //create a label for displaying date and time
51: output = new JLabel();
52: //add button and label to panel
53: add(button);
54: add(output);
55: }
56:
57: //get image icon file
58: protected static ImageIcon createImageIcon(String path) {
59: //get a path containing this image
60: java.net.URL imgURL = DateTimeSwingApp.class.getResource(path);
61: if (imgURL != null) {
62: return new ImageIcon(imgURL);
63: } else {

345

เรม่ิ ตน การเขยี นโปรแกรมดวย Java

64: System.err.println("Couldn't find file: " + path); intro. to Java (FEU.faa)
return null;
65: }
66: }
67:
//display date and time when button is clicked or alt/t is activated
68: public void actionPerformed(ActionEvent e) {
69: Date today;
String dateOut;
70: DateFormat dateFormatter;
71: Locale thLocale = new Locale("th", "TH");
72:
73: //set date and time format with a given locale above
74: dateFormatter = DateFormat.getDateTimeInstance(
75:
76: DateFormat.LONG, DateFormat.SHORT, thLocale);
77: //get today's date
78: today = new Date();
79: dateOut = dateFormatter.format(today);
80:
81: //send date and time to output
82: output.setText(dateOut + " " + thLocale.toString());
83: }
84:
85:
86: }

จากผลลพั ธใ นภาพท่ี 11.1 ผอู านจะเห็นวาเมอ่ื เรา run โปรแกรมครั้งแรกน้ันปุมของเราจะอยูตรง
กลางของ frame และเมือ่ กดปุมเราก็จะเห็นผลลัพธอ ยูทางดานขวาของปมุ โดยทีป่ มุ จะถกู เลอื่ น
มาอยทู างซายของขอ ความ แตถ าผอู านปรับขนาดของ window ใหใหญข นึ้ หรอื เลก็ ลง ตาํ แหนง
ของปมุ และ label กจ็ ะเปลี่ยนไปดว ย ทัง้ น้ีกเ็ นื่องจากวา JPanel กําหนดใหก ารวางตาํ แหนง ใน
panel เปนการวางตําแหนงแบบที่เรียกวา Flow Layout

11.2 Layout Manager

ใน Swing การวางตาํ แหนงของ component ใน container น้ันตองอาศยั การจัดการของ layout
manager ซึ่งดว ยการกาํ หนดเบื้องตนแลวการวางตาํ แหนง จะเปน flow layout เสมอซง่ึ อาจทาํ
ให object ตา ง ๆ ท่เี ราใชดแู ลว ไมส วยงาม ดงั นน้ั เราจึงตองอาศัย layout manager เปน ตัวจดั
วางจากการกําหนดของเรา Java มี layout งาย ๆ หลายตวั ท่เี ราเรยี กใชไ ด นั่นก็คือ Flow
layout, Border layout และ Grid layout

Flow layout เปน layout manager ท่จี ดั วาง component ตามลาํ ดบั ของการจดั วางโดยจะวาง
component ถดั กนั ไปจากซายไปขวา โดยจะเรม่ิ ตน วางตัวแรกในตาํ แหนง กึง่ กลางของ
container และถาจํานวน component มเี กนิ กวาความกวา งของหนาตาง flow layout ก็จะทาํ
การจดั วาง component ในบรรทดั ถดั ไป ผอู า นควรทดลองดว ยการเปล่ียนขนาดของหนา ตาง
จากโปรแกรมตวั อยางแรก หรอื สรางหนาตา งทม่ี ี component มากกวาหนงึ่ ตวั เพ่ือทดสอบการ
จดั วางดว ย flow layout

11.2.1 Border layout

Border layout เปน layout manager ตวั หนง่ึ ท่ที าํ ใหการจดั วาง component เปนไปตามความ
ตอ งการของเราไดเ ปนอยางดี เราสามารถท่ีจะกําหนดตาํ แหนงของ component ในจุดตาง ๆ
ของ container ไดอยา งงา ย ๆ

ภาพที่ 11.2 แสดงถึงการวางตําแหนงของ object ดว ย border layout

346


Click to View FlipBook Version