บทที่ 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