You are on page 1of 68

intro. to Java (FEU.

faa)
ในบททีผ ่ านมาเราไดเห็นการสราง Frame แบบคราว ๆ เพื่อใชรองรับการสรางภาพสองมิตแ ิ บบ
ตาง ๆ ในบทนี้เราจะมาดูกันถึงสวนประกอบบางสวนของ 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

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


 เรากําหนดใหมี 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);

intro. to Java (FEU.faa)


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

ผูอานจะเห็นวา method actionPerformed() ของเราเรียกใช class Date() ในการแสดงผลโดย


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

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

intro. to Java (FEU.faa)


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);


65: return null;
66: }
67: }
68:
69: //display date and time when button is clicked or alt/t is activated
70: public void actionPerformed(ActionEvent e) {
71: Date today;

intro. to Java (FEU.faa)


72: String dateOut;
73: DateFormat dateFormatter;
74: Locale thLocale = new Locale("th", "TH");
75:
76: //set date and time format with a given locale above
77: dateFormatter = DateFormat.getDateTimeInstance(
78: DateFormat.LONG, DateFormat.SHORT, thLocale);
79: //get today's date
80: today = new Date();
81: dateOut = dateFormatter.format(today);
82:
83: //send date and time to output
84: output.setText(dateOut + " " + thLocale.toString());
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
บทที่ 11: GUI และ Event Handling

BorderLayout.NORTH BorderLayout.EAST

BorderLayout.CENTER

intro. to Java (FEU.faa)


BorderLayout.WEST BorderLayout.SOUTH

JButton JTextField

ภาพที่ 11.2 การวาง JButton และ JTextField ดวย BorderLayout

เราสามารถกําหนดตําแหนงการวาง object ดวย border layout ไดหาตําแหนงคือ ดานบน


(NORTH), ดานลาง (SOUTH), ดานซาย (WEST), ดานขวา (EAST) และตรงกลาง (CENTER)
เทานั้น โปรแกรมตัวอยางของเราดัดแปลงมาจากโปรแกรม DateTimeSwingApp.java โดยเรา
ไดเพิ่มปุมอีกสองปุม และ text field ที่ใชแสดงขอความอีกสองตัว เราไดกําหนดใหปุมดานซาย
เมื่อผูใชกดแลวเราจะแสดงวันและเวลาเปนภาษาไทยใน text field ดานบน และถากดปุม
ทางขวาเราก็จะแสดงวันและเวลาเปนภาษาอังกฤษใน text field ดานลาง และเราจะทําการลาง
text field ทั้งสองเมื่อผูใชกดปุม clear

การกําหนดใหมีการใช border layout นั้นทําไดดวยการเรียก

setLayout(new BorderLayout());

การนํา object เขาไปใสไวในตําแหนงตาง ๆ ก็ทําไดดว ยการกําหนดตําแหนงของ object นั้น ๆ


เชน

add(clearButton, BorderLayout.CENTER);
add(northOutput, BorderLayout.NORTH);
add(southOutput, BorderLayout.SOUTH);
add(leftButton, BorderLayout.WEST);
add(rightButton, BorderLayout.EAST);

text filed เปนชองทางที่ Swing เอาไวใชรับขอมูล และแสดงขอมูลใหกับผูใช ในโปรแกรม


ตัวอยางของเรา เราสราง text field สองตัวสําหรับแสดงขอมูลเทานั้นคือ northOutput และ
southOutput

northOutput = new JTextField(25);


northOutput.setFont(new Font("Serif", Font.BOLD, 14));
northOutput.setHorizontalAlignment(JTextField.CENTER);
northOutput.setEditable(false);

southOutput = new JTextField(25);


southOutput.setFont(new Font("Serif", Font.BOLD, 14));
southOutput.setHorizontalAlignment(JTextField.CENTER);
southOutput.setEditable(false);

ในการแสดงผลลัพธนั้นเรากําหนดใหขอความปรากฏในตําแหนงกึ่งกลางของ text field ดวยการ


เรียกใช method setHorizontalAlignment(JTextField.CENTER) พรอมทั้งกําหนดใหผใู ชไม
สามารถทําการเปลี่ยนแปลงขอความใน text field ไดดวยการเรียกใช method setEditable()
ดวยคา false

ในการฟงเหตุการณที่อาจเกิดขึ้นในโปรแกรมตัวอยางของเรานั้น เราตองตรวจสอบดูวาระหวาง
ปุมสามปุม ปุมไหนถูกกด (ทําใหเกิด action event) โดยเราตองตรวจสอบเหตุการณเหลานี้ใน

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

method actionPerformed() อยูสองเหตุการณ คือเมื่อปุม rightButton ถูกกดหรือปุม


leftButton ถูกกด สวนปุม clearButton นั้นเราทําในอีกรูปแบบหนึ่งซึ่งเราจะไดอธิบายตอไป

public void actionPerformed(ActionEvent e) {


Date today;
String dateOut;
DateFormat dateFormatter;

intro. to Java (FEU.faa)


if(e.getSource() == leftButton) {
//create a Thai locale
Locale thLocale = new Locale("th", "TH");
//set date and time format with a given locale above
dateFormatter = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.LONG, thLocale);
//get today's date
today = new Date();
dateOut = dateFormatter.format(today);

//send date and time to output


northOutput.setText(dateOut);
}

if(e.getSource() == rightButton) {
//set default locale to US
Locale.setDefault(new Locale("us", "US"));
//set date and time format with a given locale above
dateFormatter = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.SHORT);
//get today's date
today = new Date();
dateOut = dateFormatter.format(today);

//send date and time to output


southOutput.setText(dateOut);
}
}

เราตรวจสอบวาปุมไหนถูกกดจาก parameter e ที่สงเขามายัง method actionPerformed()


ดวยการเรียกใช method getSource() ซึ่งถาแหลงที่มาของเหตุการณเปนอยางใดอยางหนึ่ง
ระหวาง rightButton และ leftButton เราก็จะกําหนดการสนองตอบตอเหตุการณนั้น ๆ เชนถา
ปุม rightButton ถูกกดเราก็แสดงวันและเวลาดวย format ที่กําหนดไวสําหรับประเทศ
สหรัฐอเมริกา ซึ่งเรากําหนดจาก locale ดวยการกําหนดคา default ใหเปนไปตามขอกําหนด
ของประเทศสหรัฐอเมริกา

Locale.setDefault(new Locale("us", "US"));

[สาเหตุที่เราตองกําหนดใหเปน default ก็เพราะวามันอาจถูกเปลี่ยนจากการกดปุม leftButton


ของผูใช]

สําหรับการแสดงขอความเราก็ใชการเรียก method setText() เชนเดียวกันกับโปรแกรมตัวอยาง


กอนหนานี้ เพียงแตวาเราตองเรียกใช text field ที่ถูกตองตามการกําหนดของเราวาตัวไหนจะ
แสดงเมื่อปุมไหนถูกกด

สวนการ clear ขอความใน text field ทั้งสองเราเลือกที่จะเขียน code ณ เวลาที่เราสรางปุม



พรอม ๆ กันไป

clearButton = new JButton("Clear");


clearButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
northOutput.setText("");
southOutput.setText("");
}
});

และการ clear ก็เปนเพียงแคการกําหนดใหขอ  ความใน text field ทั้งสองเปน null เทานั้นเอง


และผลลัพธที่เราไดจากการกดปุมทั้งสองก็มด
ี ังนี้

348
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.3 ผลลัพธของการกดปุม TH Time และปุม US Time

ผูอานจะเห็นวาการใช border layout นั้นทําใหเราสามารถกําหนดตําแหนงของ object ไดแต


ทวามันก็ยังไมคอยจะสมบรูณเ ทาใดนัก ถาสังเกตใหดผ ี ูอานก็จะเห็นวาปุมทีอ่ ยูในตําแหนง
CENTER นั้นจะมีขนาดใหญกวาปุมอื่น ๆ ทั้งนีก ้ ็เพราะวา border layout manager จะวาง
สวนประกอบในตําแหนงอื่นกอน แลวจึงวาง object ในตําแหนง CENTER เปนตัวสุดทาย พรอม
กับเพิ่มขนาดให object ที่อยูใ นตําแหนงนี้โดยอัตโนมัติจนเต็มเนื้อที่ ซึ่งเนื้อที่ที่วา นั้นมาจากการ
กําหนดให text filed มีความจุตามจํานวนของตัวอักษรที่กําหนดไว (เชน 25 ในโปรแกรม
ตัวอยาง) ถาเราทําการขยายหรือลดขนาดของ window เราก็จะเห็นวาขนาดของ object ที่อยู
สวนริมจะไมเปลี่ยนขนาด สวนที่เปลี่ยนก็คือ object ที่อยูในตําแหนง CENTER

และ code ทั้งหมดของโปรแกรม BorderLayoutApp.java ก็มีดังนี้

1: /**
2: Date and Time with Border Layout
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 BorderLayoutApp {
12: public static void main(String[] args) {
13: JFrame.setDefaultLookAndFeelDecorated(true);
14: JFrame frame = new DateAndTimeFrame();
15: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: frame.setVisible(true);
17: }
18: }
19:
20: class DateAndTimeFrame extends JFrame {
21: private static final int WIDTH = 400;
22: private static final int HEIGHT = 110;
23:
24: //create a frame and a panel
25: public DateAndTimeFrame() {
26: setTitle("Date and Time");
27: setSize(WIDTH, HEIGHT);
28: JPanel panel = new DateAndTimePanel();
29: add(panel);
30: }
31: }
32:
33: class DateAndTimePanel extends JPanel implements ActionListener {
34: protected JButton leftButton, rightButton, clearButton;
35: protected JTextField northOutput, southOutput;
36:
37: public DateAndTimePanel() {
38: //get image icon for the button
39: ImageIcon clockIcon = createImageIcon("clock.gif");
40: //create a button
41: rightButton = new JButton("US Time", clockIcon);
42: //set text position before icon
43: rightButton.setVerticalTextPosition(AbstractButton.CENTER);
44: rightButton.setHorizontalTextPosition(AbstractButton.LEADING);
45: //alt/u key activation

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

46: rightButton.setMnemonic(KeyEvent.VK_U);
47: //add a listener to this button
48: rightButton.addActionListener(this);
49:
50: //create a button
51: leftButton = new JButton("TH Time", clockIcon);
52: //set text position before icon
53: leftButton.setVerticalTextPosition(AbstractButton.CENTER);

intro. to Java (FEU.faa)


54: leftButton.setHorizontalTextPosition(AbstractButton.TRAILING);
55: //alt/t key activation
56: leftButton.setMnemonic(KeyEvent.VK_T);
57: //add a listener to this button
58: leftButton.addActionListener(this);
59:
60: //add a button to clear north and south text fields
61: clearButton = new JButton("Clear");
62: clearButton.addActionListener(new ActionListener() {
63: public void actionPerformed(ActionEvent e) {
64: northOutput.setText("");
65: southOutput.setText("");
66: }
67: });
68:
69: //create output label fro us locale
70: northOutput = new JTextField(25);
71: northOutput.setFont(new Font("Serif", Font.BOLD, 14));
72: northOutput.setHorizontalAlignment(JTextField.CENTER);
73: northOutput.setEditable(false);
74:
75: //create output label for thai lacale
76: southOutput = new JTextField(25);
77: southOutput.setFont(new Font("Serif", Font.BOLD, 14));
78: southOutput.setHorizontalAlignment(JTextField.CENTER);
79: southOutput.setEditable(false);
80:
81: //set layout to BorderLayout
82: setLayout(new BorderLayout());
83: add(clearButton, BorderLayout.CENTER);
84: add(northOutput, BorderLayout.NORTH);
85: add(southOutput, BorderLayout.SOUTH);
86: add(leftButton, BorderLayout.WEST);
87: add(rightButton, BorderLayout.EAST);
88: }
89:
90: //get image icon file
91: protected static ImageIcon createImageIcon(String path) {
92: //get a path containing this image
93: java.net.URL imgURL = BorderLayoutApp.class.getResource(path);
94: if (imgURL != null) {
95: return new ImageIcon(imgURL);
96: } else {
97: System.err.println("Couldn't find file: " + path);
98: return null;
99: }
100: }
101:
102: //display date and time when button is clicked or alt/t is activated
103: public void actionPerformed(ActionEvent e) {
104: Date today;
105: String dateOut;
106: DateFormat dateFormatter;
107:
108: if(e.getSource() == leftButton) {
109: //create a Thai locale
110: Locale thLocale = new Locale("th", "TH");
111: //set date and time format with a given locale above
112: dateFormatter = DateFormat.getDateTimeInstance(
113: DateFormat.LONG, DateFormat.LONG, thLocale);
114: //get today's date
115: today = new Date();
116: dateOut = dateFormatter.format(today);
117:
118: //send date and time to output
119: northOutput.setText(dateOut);

350
บทที่ 11: GUI และ Event Handling

120: }
121:
122: if(e.getSource() == rightButton) {
123: //set default locale to US
124: Locale.setDefault(new Locale("us", "US"));
125: //set date and time format with a given locale above
126: dateFormatter = DateFormat.getDateTimeInstance(
127: DateFormat.LONG, DateFormat.SHORT);

intro. to Java (FEU.faa)


128: //get today's date
129: today = new Date();
130: dateOut = dateFormatter.format(today);
131:
132: //send date and time to output
133: southOutput.setText(dateOut);
134: }
135: }
136: }

11.2.2 Grid layout

การจัดวางที่เราทํานั้นยังมีขอดอยอยูพอสมควร เชน ปุมจะขยายตัวจนเต็มทีอ ่ ยู ถาหากเรา


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

โปรแกรม SimpleCalculator.java เปนโปรแกรมตัวอยางที่เราใชทั้ง panel และการจัดวาง


object ดวย grid layout เขามาชวย ภาพที่ 11.4 แสดงหนาตางที่เราสรางขึ้น

JPanel ใช
BorderLayout เพื่อวาง
JTextField และ JPanel

JTextField สําหรับแสดงผล
(BorderLayout.NORTH)

JPanel ใช GridLayout


สําหรับวางปุมตาง ๆ
(BorderLayout.CENTER)

ภาพที่ 11.4 การจัดวาง component ดวย Grid layout และ Border layout

การจัดวาง component ของเราใช JFrame อยูหนึ่งตัวที่เปน container หลักของ component


อื่น เราใช Jpanel สองตัว ตัวแรกใชสําหรับการจัดวาง JTextField และ JPanel ที่ใชจัดวางปุม
ตาง ๆ โดยเราไดกําหนดให JTextField อยูดาน NORTH สวน JPanel ที่เก็บปุมอยูที่ CENTER
สําหรับ JPanel อีกตัวเราใชเก็บ component ทั้งสองเพื่อนําไปเก็บไวใน JFrame ตอไป

การใช GridLayout ก็ทําไดดงั นี้

panel = new JPanel(new GridLayout(5, 4, 2, 2));

จากประโยคที่เห็นเรากําหนดให JPanel เรียกใช GridLayout ที่มีจาํ นวนเทากับ 5 x 4 (5 แถว 4


column) สวนตัวเลข 2 สองตัวที่เห็นเปนการกําหนดชองวางระหวางปุม  ที่อยูใน grid layout วา
ควรจะหางกันสองหนวย (pixel) หลังจากที่เรากําหนด layout แลวเราก็นําปุม  ตาง ๆ เขาสู panel
นี้

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

for(int i = 0; i < labels.length; i++) {


JButton button = new JButton(labels[i]);
button.addActionListener(this);
if(i == 3)
button.setForeground(Color.RED);
button.setFont(f);
panel.add(button);
}

intro. to Java (FEU.faa)


โดยเราไดสราง array สําหรับเก็บขอมูลที่ตองอยูบนปุมไวดังนี้

String[] labels = { "\u221a", "x\262", "\261", "C",


"7", "8", "9", "+",
"6", "5", "4", "-",
"1", "2", "3", "*",
"0", ".", "=", "/" };

ขอมูลที่ปรากฏอยูบนปุมสามตัวแรกเราใชตวั อักษร Unicode เปนตัวกําหนดซึ่งมีคาเปน


เครื่องหมาย √ x2 และ ± ตามลําดับ เรายังไดกําหนดใหตวั อักษร C บนปุมสําหรับการลาง
ขอความใน text field เปนสีแดงดวยการเรียกใช method setForeground(Color.RED) บนปุม
นี้ พรอมกันนี้เราไดกําหนด font ใหกับปุมเหลานี้ดวยเชนกัน

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

การใช panel หลาย ๆ ตัวในการจัดวาง component ดวย layout ที่ตา งกันจะทําใหหนาตาของ


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

สําหรับ code ของการสนองตอบตอการกระทําของผูใชก็ไมซับซอนเทาไรนัก เรายอมเฉพาะการ


กดปุมที่อยูใ นหนาตางเทานั้น การกดปุมตัวเลขบน keyboard ยังไมมีผลใด ๆ ตอเครื่องคิดเลข
ของเรา

public void actionPerformed(ActionEvent e) {


//get action command
String action = e.getActionCommand();

//handle operation clear


if(action.equals("C")) {
screen.setText("0");
op = "";
reg1 = reg2 = 0;
overWrite = true;
return;
}

//handle + or -
if(action.equals("\261")) {
String s = screen.getText();
if(s.startsWith("-")) {
screen.setText(s.substring(1));
}
else
screen.setText("-" + s);
return;
}

//handle fraction
if(action.equals(".")) {
if(overWrite) {
screen.setText(action);
overWrite = false;
return;
}
String s = screen.getText();
if(s.indexOf('.') != -1)
return;
s += '.';

352
บทที่ 11: GUI และ Event Handling

screen.setText(s);
}

//handle digits 0 - 9
if(action.equals("0") || action.equals("1") || action.equals("2") ||
action.equals("3") || action.equals("4") || action.equals("5") ||
action.equals("6") || action.equals("7") || action.equals("8") ||
action.equals("9")) {

intro. to Java (FEU.faa)


if(overWrite)
screen.setText(action);
else
screen.setText(screen.getText() + action);
overWrite = false;
}

//handle operators
if(action.equals("+") || action.equals("-") ||
action.equals("*") || action.equals("/") || action.equals("=")) {
reg2 = Double.parseDouble(screen.getText());
reg1 = calc(op, reg1, reg2);
screen.setText("" + reg1);
op = action;
overWrite = true;
}

//handle square root


if(action.equals("\u221a")) {
double result = Math.sqrt(Double.parseDouble(screen.getText()));
screen.setText(String.valueOf(result));
}

//handle x square
if(action.equals("x\262")) {
double result = Double.parseDouble(screen.getText());
result *= result;
screen.setText(String.valueOf(result));
}
}

และ code สวนอื่น ๆ ทั้งหมดของโปรแกรม SimpleCalculator.java ก็มีดังนี้

1: /**
2: Simple calculator
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 SimpleCalculator {
12: public static void main(String[] args) {
13: JFrame.setDefaultLookAndFeelDecorated(true);
14: JFrame frame = new CalculatorFrame();
15: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: frame.setVisible(true);
17: }
18: }
19:
20: class CalculatorFrame extends JFrame {
21:
22: //create a frame and a panel
23: public CalculatorFrame() {
24: setTitle("Simple Calculator");
25: JPanel panel = new CalculatorPanel();
26: add(panel);
27: pack();
28: }
29: }
30:
31:
32: class CalculatorPanel extends JPanel implements ActionListener {

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

33: private JTextField screen;


34: private JPanel panel;
35: private String[] labels = {"\u221a", "x\262", "\261", "C",
36: "7", "8", "9", "+",
37: "6", "5", "4", "-",
38: "1", "2", "3", "*",
39: "0", ".", "=", "/"};
40: private String op;

intro. to Java (FEU.faa)


41: private double reg1, reg2;
42: private boolean overWrite;
43:
44: public CalculatorPanel() {
45: //use border layout for this panel
46: setLayout(new BorderLayout());
47:
48: //use grid layout for button panel
49: panel = new JPanel(new GridLayout(5, 4, 2, 2));
50: screen = new JTextField(15);
51: screen.setHorizontalAlignment(JTextField.RIGHT);
52: screen.setFont(new Font("SanSerif", 1, 16));
53: screen.setEditable(false);
54: screen.setText("0");
55: screen.setBackground(Color.white);
56:
57: //add component to this panel
58: add(screen, BorderLayout.NORTH);
59: add(panel, BorderLayout.CENTER);
60:
61: //create buttons
62: createButtons();
63:
64: op = "";
65: overWrite = true;
66: }
67:
68:
69: //create buttons
70: private void createButtons() {
71: Font f = new Font("Dialog", 1, 14);
72: for(int i = 0; i < labels.length; i++) {
73: JButton button = new JButton(labels[i]);
74: button.addActionListener(this);
75: if(i == 3)
76: button.setForeground(Color.RED);
77: button.setFont(f);
78: panel.add(button);
79: }
80: }
81:
82: public void actionPerformed(ActionEvent e) {
83: //get action command
84: String action = e.getActionCommand();
85:
86: //handle operation clear
87: if(action.equals("C")) {
88: screen.setText("0");
89: op = "";
90: reg1 = reg2 = 0;
91: overWrite = true;
92: return;
93: }
94:
95: //handle + or -
96: if(action.equals("\261")) {
97: String s = screen.getText();
98: if(s.startsWith("-")) {
99: screen.setText(s.substring(1));
100: }
101: else
102: screen.setText("-" + s);
103: return;
104: }
105:
106: //handle fraction

354
บทที่ 11: GUI และ Event Handling

107: if(action.equals(".")) {
108: if(overWrite) {
109: screen.setText(action);
110: overWrite = false;
111: return;
112: }
113: String s = screen.getText();
114: if(s.indexOf('.') != -1)

intro. to Java (FEU.faa)


115: return;
116: s += '.';
117: screen.setText(s);
118: }
119:
120: //handle digits 0 - 9
121: if(action.equals("0") || action.equals("1") || action.equals("2") ||
122: action.equals("3") || action.equals("4") || action.equals("5") ||
123: action.equals("6") || action.equals("7") || action.equals("8") ||
124: action.equals("9")) {
125: if(overWrite)
126: screen.setText(action);
127: else
128: screen.setText(screen.getText() + action);
129: overWrite = false;
130: }
131:
132: //handle operators
133: if(action.equals("+") || action.equals("-") ||
134: action.equals("*") || action.equals("/") || action.equals("=")) {
135: reg2 = Double.parseDouble(screen.getText());
136: reg1 = calc(op, reg1, reg2);
137: screen.setText("" + reg1);
138: op = action;
139: overWrite = true;
140: }
141:
142: //handle square root
143: if(action.equals("\u221a")) {
144: double result = Math.sqrt(Double.parseDouble(screen.getText()));
145: screen.setText(String.valueOf(result));
146: }
147:
148: //handle x square
149: if(action.equals("x\262")) {
150: double result = Double.parseDouble(screen.getText());
151: result *= result;
152: screen.setText(String.valueOf(result));
153: }
154: }
155:
156: //handle operations +, -, * and /
157: private double calc(String op1, double r1, double r2) {
158: if(op.equals("+"))
159: r1 = r1 + r2;
160: else if(op.equals("-"))
161: r1 = r1 - r2;
162: else if(op.equals("*"))
163: r1 = r1 * r2;
164: else if(op.equals("/"))
165: r1 = r1 / r2;
166: else
167: r1 = r2;
168: return r1;
169: }
170: }

[การกําหนดการทํางานของปุมในเครือ
่ งคิดเลข]

โปรแกรมเครื่องคิดเลขที่เราเขียนขึ้นใช method actionPerformed() ในการจัดการกับ


เหตุการณที่เกิดขึ้นจากการกดปุมที่มีอยูในหนาตางของเครือ
่ ง การที่จะทําใหผใู ชสามารถใชปุมที่
มีอยูบน keyboard ในการใสตวั เลขหรือเครื่องหมายประมวลผลนั้น เราตองใชกระบวนการดักฟง
เหตุการณทม ี่ าจาก class KeyEvent ดวยการเรียกใช method keyPressed(), keyReleased()

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

หรือ keyTyped() ซึ่งในกรณีของเรา method keyTyped() เปน method ที่เหมาะสมทีส


่ ด
ุ ทั้งนี้
ก็เนื่องจากวาเราสนใจเฉพาะการกดปุมจากผูใชเทานั้น

อยางไรก็ตามเราตองเขียน method ทั้งสามตัวไว (ขอบังคับของ Java) แตจะใส code เฉพาะ


method keyTyped() เทานั้น และ code ที่อยูภายใน method นี้ก็คลายกับที่เราใสไวใน
method actionPerformed()

intro. to Java (FEU.faa)


กอนที่จะเขียน code ของ method keyTyped() เราตอง implements interface KeyListener
เชนเดียวกับที่เรา implements interface ActionListener ตอนที่เราเขียน code สําหรับ
method actionPeformed()

class CalculatorPanel extends JPanel implements ActionListener, KeyListener {

เนื่องจากวาเราตองดักฟงเหตุการณที่เกิดจากการกดปุม พรอมกับการสงสิ่งที่ปุมที่ถูกกดจากผูใช
ไปยัง text field เพื่อแสดงผล เราตองกําหนดให text field คอยฟงเหตุการณจาก keyboard
ดวย

screen.addKeyListener(this);

เชนเดียวกันปุมตาง ๆ ที่เราสรางขึ้นก็ตองคอยฟงการกดปุมดวยเหมือนกัน เราจึงใสตัวฟง


เหตุการณหลังจากที่เราสรางปุมเหลานี้ภายใน for/loop เชนเดียวกับที่เราทําเมื่อฟง action ที่
เกิดขึ้นจากการกดปุมที่มีอยูในหนาตางดวย mouse

for(int i = 0; i < labels.length; i++) {


JButton button = new JButton(labels[i]);
button.addActionListener(this);
button.addKeyListener(this);
if(i == 3)
button.setForeground(Color.RED);
button.setFont(f);
panel.add(button);
}

ในการแยกแยะปุมทีผ ่ ูใชกดนั้นเราจะใช method getKeyChar() เปนตัวอานคาปุมกอน หลังจาก


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

ภายใน method keyTyped() เราจะคอยฟง KeyEvent ที่เกิดขึ้นวาเกิดจากปุมใดดวยการเรียก


method getKeyChar() ผานทาง parameter KeyEvent: e นี้ คาที่สงกลับมาจาก method
getKeyChar() จะเปนคา Unicode ของปุมที่ถูกกดนั้น

public void keyTyped(KeyEvent e) {


char c = e.getKeyChar();
switch(c) {
//'0' .. '9'
case 48: case 49: case 50: case 51:
case 52: case 53: case 54: case 55:
case 56: case 57:
if(overWrite)
screen.setText("" + c);
else
screen.setText(screen.getText() + c);
overWrite = false;
break;
//'*', '+', '-', '/', '\n', '='
case 42: case 43: case 45:
case 47: case 61: case 10:
reg2 = Double.parseDouble(screen.getText());
reg1 = calc(op, reg1, reg2);
screen.setText("" + reg1);
op = "" + c;
overWrite = true;
break;
//'.'
case 46:

356
บทที่ 11: GUI และ Event Handling

if(overWrite) {
screen.setText("" + c);
overWrite = false;
return;
}
String s = screen.getText();
if(s.indexOf('.') != -1)
return;

intro. to Java (FEU.faa)


s += '.';
screen.setText(s);
break;
//'C', 'c'
case 67: case 99:
screen.setText("0");
op = "";
reg1 = reg2 = 0;
overWrite = true;
return;
default: return;
}
}

เนื่องจากวาตัวแปร op เก็บคาที่เปน String ดังนั้นการนําคาจากตัวแปร c ไปเก็บใน op จึงตอง


เปลี่ยนตัวแปร c ใหเปน String ดวยการบวกเขากับ null String ("") เชน op = "" + c; เปนตน

การ run โปรแกรมก็เหมือนเดิมแตผูใชสามารถใชไดทั้งการกดปุมผานทาง keyboard และผาน


ทางการกดปุมในหนาตาง

11.3 JRadioButton และ JComboBox

ภาพที่ 11.5 แสดงผลลัพธของโปรแกรมที่ใช JRadioButton และ JCombobox

JRadioButton JComboBox

JPanel:
topPanel ที่ใช
เก็บ
JRadioButton 2
ตัว

JLabel ที่ใช BorderFactory กําหนด line และ title ใช


สําหรับแสดง date/time ตามที่ผูใชเลือก

ภาพที่ 11.5 Swing application ที่ใช JRadioButton, JComboBox และ TitledBorder

โปรแกรมตัวนี้ใช panel 2 ตัวในการจัดวาง component ในหนาตาง ตัวแรกเปน panel


(toppanel) ที่ใชเก็บ radio button (thai และ us) และ combo box (patterns) ตัวทีส ่ องเปน
panel หลักที่ใชเก็บ panel ตัวแรกและ label (result) ที่ใชสําหรับแสดงผลลัพธ เราสราง radio
button ดวยคําสั่ง

thai = new JRadioButton("THAI");


thai.setMnemonic(KeyEvent.VK_T);
thai.setActionCommand("THAI");
thai.setSelected(true);

us = new JRadioButton("US");
us.setMnemonic(KeyEvent.VK_U);
us.setActionCommand("US");

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

เรากําหนดใหตวั แปร thai และ us มาจาก class JRadioButton โดยเรากําหนดขอมูลของตัว


แปรทั้งสองตัวผานทาง constructor ดวยคา "THAI" และ "US" ตามลําดับ ปุมทั้งสองยอมให
ผูใชสามารถใช keyboard: ALT+T สําหรับการแสดงผลที่เปนภาษาไทย และ ALT+U สําหรับ
ผลลัพธที่เปนภาษาอังกฤษ และเราจะกําหนดใหปุมไทยเปนปุมที่ถูกเลือกโดยอัตโนมัตท
ิ ก
ุ ครั้งที่
โปรแกรมถูก execute

การกําหนดตัวดักฟง event ของ radio button ทั้งสองก็ทําผานการกําหนดดวย method

intro. to Java (FEU.faa)


addActionListener() เชนเคย

เนื่องจากวาเราตองการใหปมุ ใดปุมหนึ่ง (ปุมเดียวเทานั้น) ถูกเลือก เราจึงตองใช ButtonGroup


เปนตัวกําหนด (เชนเดียวกันถาโปรแกรมตองการใหมีการเลือกไดมากกวาหนึ่งปุม เราก็ไมตอง
ใช ButtunGroup)

ButtonGroup group = new ButtonGroup();


group.add(thai);
group.add(us);

เมื่อเราได component ทั้งสองแลวเราก็นํา component ทั้งสองเขาสู panel ตอไปโดยเราจะจัด


วางดวย grid layout ที่มีขนาดเทากับ 1 x 2

radioPanel = new JPanel(new GridLayout(1, 2));


radioPanel.add(thai);
radioPanel.add(us);

ขั้นตอนตอไปทีเ่ ราตองทําก็คือการสราง combo box สําหรับกําหนดหนาตา (patterns) ของวัน


และเวลาทีผ่ ูใชสามารถเลือกได

patterns = new JComboBox(patternStrings);


patterns.setEditable(true);
patterns.addActionListener(this);

โดยเราจะกําหนดใหหนาตาของวันและเวลาเปน string ที่ Java ไดกําหนดไว

String[] patternStrings = {
"dd MMMMM yyyy",
"dd.MM.yy",
"MM/dd/yy",
"yyyy.MM.dd G 'at' hh:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"H:mm:ss:SSS",
"K:mm a,z",
"yyyy.MMMMM.dd GGG hh:mm aaa"
};

พรอมกันนี้เรายังไดเปดโอกาสใหผใู ชสามารถใส pattern เขาสู combo box ไดดวยการกําหนด


setEditable(true) บน combo box นี้ หลังจากนั้นเราก็นํา topPanel และ patterns เขาไปเก็บ
ไวใน panel

topPanel.add(radioPanel);
topPanel.add(patterns);

สิ่งที่เราตองทําตอก็คือการสราง label สําหรับการแสดงผล

result = new JLabel(" ");


result.setForeground(Color.blue);
result.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY), "Current Date/Time",
TitledBorder.LEFT, TitledBorder.TOP
));

เรากําหนดใหสข ี องตัวหนังสือที่ตองแสดงผาน label เปนสีนา้ํ เงินพรอมทั้งกําหนดเสนกรอบของ


label เปนเสนสีเทา (light gray) ที่มีการกําหนด title: "Current Date/Time" ใหอยู ณ
ตําแหนงบนซาย หลังจากนั้นเราก็นํา component ทั้งสองเขาสู panel หลักของเรา

358
บทที่ 11: GUI และ Event Handling

add(topPanel, BorderLayout.CENTER);
add(result, BorderLayout.SOUTH);

กระบวนการตอไปก็คือการสนองตอบตอ event ที่เกิดขึ้นจากการเลือกของผูใช นั่นก็คือเราตอง


เขียน code ภายใน method actionPerformed()

intro. to Java (FEU.faa)


public void actionPerformed(ActionEvent e) {
//THAI radio button is checked
if(e.getSource() == thai) {
Locale.setDefault(new Locale("th", "TH"));
String newSelection = (String)patterns.getSelectedItem();
pattern = newSelection;
reformat();
}
//US radio button is checked
if(e.getSource() == us) {
Locale.setDefault(new Locale("us", "US"));
String newSelection = (String)patterns.getSelectedItem();
pattern = newSelection;
reformat();
}
//item in combo box is selected
if(e.getSource() == patterns) {
String newSelection = (String)patterns.getSelectedItem();
pattern = newSelection;
reformat();
}
}

โปรแกรมของเราใช object จาก JRadioButton 2 ตัวและจาก JComboBox 1 ตัวดังนั้นเราจึง


ตองตรวจสอบดูวา object ทั้งสามตัวนั้น ตัวไหนถูกเลือก (fire an event) เรากําหนดใหปม ุ thai
เปนปุมที่ไดรับการกําหนดคาจากโปรแกรม (default) เพราะฉะนั้นคาของ locale ที่เราไดจาก
การ run โปรแกรมจะเปนการแสดงผลลัพธดว ยภาษาไทย ทั้งนี้ทั้งนั้นจะตองมี event ใด event
หนึ่งเกิดขึ้นกอน การตรวจสอบ event ทั้งหมดมีอยูส ามสวนคือ

1. ปุม thai ถูกกด ถาปุมนี้ถูกเลือกเราจะกําหนดให locale ของเครื่องเปนภาษาไทย ดึงคา


pattern ออกจาก combo box ดวย method getSelectedItem() ใชคานีส ้ ําหรับการ
แสดงผลใน method reformat()
2. ปุม us ถูกกด ถาปุมนี้ถูกเลือกเราจะกําหนดให locale ของเครื่องเปนภาษาอังกฤษ ดึง
คา pattern ออกจาก combo box ดวย method getSelectedItem() ใชคา นี้สําหรับ
การแสดงผลใน method reformat()
3. item ตัวใดตัวหนึ่งใน combo box ถูกเลือก เราก็ดึงเอา pattern นั้นสงไปให method
reformat() เพื่อแสดงผลตอไป

method reformat() ใช pattern ทีผ่ ูใชเลือกเปนตัวกําหนดรูปแบบของการแสดงผลของวัน


เวลาทีถ
่ ูกดึงออกจากเครื่อง ณ เวลาทีผ ่ ูใชเลือก

private void reformat() {


//gets today's date and format it with selected pattern and locale
Date today = new Date();
SimpleDateFormat formatter = new SimpleDateFormat(
pattern, Locale.getDefault());
try {
String dateString = formatter.format(today);
result.setForeground(Color.BLACK);
result.setText(dateString);
}
catch (IllegalArgumentException ie) {
result.setForeground(Color.RED);
result.setText("Error: " + ie.getMessage());
}
}

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

intro. to Java (FEU.faa)


ภาพที่ 11.6 JComboBox และ item ที่อยูภายใน

หลังจากที่เลือก pattern ใน JComboBox แลวผลลัพธที่เราไดคือ

ภาพที่ 11.7 ตัวอยางผลลัพธของโปรแกรม

ผูอานควรทดลอง run โปรแกรมตัวนีด ้ วยการใส pattern เขาสู JComboBox แลวดูผลลัพธวา


เปนอยางไร แตจะตองใช pattern ตามที่ Java ยอมใหมีไดดังทีแสดงในตารางที่เห็นนี1้

ตัวอักษร วันและเวลา การแสดงผล ตัวอยาง


G Era designator Text AD
y Year Year 1996; 96
M Month in year Month July; Jul; 07
w Week in year Number 27
W Week in month Number 2
D Day in year Number 189
d Day in month Number 10
F Day of week in month Number 2
E Day in week Text Tuesday; Tue
a Am/pm marker Text PM
H Hour in day (0-23) Number 0
k Hour in day (1-24) Number 24
K Hour in am/pm (0-11) Number 0
h Hour in am/pm (1-12) Number 12
m Minute in hour Number 30
s Second in minute Number 55
S Millisecond Number 978
z Time zone General time zone Pacific Standard Time; PST; GMT-08:00
Z Time zone RFC 822 time zone -0800

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

1
ผูอานควรศึกษาเพิ่มเติมจาก Java API ถึงการใชงานตัวอักษรตาง ๆ ในตาราง

360
บทที่ 11: GUI และ Event Handling

1: /**
2: Using JRadioButton, JComboBox and BorderFatory
3: */
4:
5: import javax.swing.*;
6: import javax.swing.border.*;
7: import java.awt.*;

intro. to Java (FEU.faa)


8: import java.awt.event.*;
9: import java.util.*;
10: import java.text.*;
11:
12: class FormatDate {
13: public static void main(String[] args) {
14: JFrame.setDefaultLookAndFeelDecorated(true);
15: JFrame frame = new FormatDateFrame();
16: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17: frame.setVisible(true);
18: }
19: }
20:
21: class FormatDateFrame extends JFrame {
22:
23: //create a frame and a panel
24: public FormatDateFrame() {
25: setTitle("Date and Time");
26: JPanel panel = new FormatDatePanel();
27: add(panel);
28: pack();
29: }
30: }
31:
32: //date and time panel
33: class FormatDatePanel extends JPanel implements ActionListener {
34: JPanel radioPanel; //radio buttons panel
35: JPanel topPanel; //panel for buttons and combo box
36: JRadioButton thai, us; //locales
37: JComboBox patterns; //patterns in combo box
38: JLabel result; //label displaying result
39: String pattern; //selected pattern
40: Locale locale; //default locale
41:
42: public FormatDatePanel() {
43: //layout for main panel
44: setLayout(new BorderLayout());
45:
46: //THAI radio button - default
47: thai = new JRadioButton("THAI");
48: thai.setMnemonic(KeyEvent.VK_T);
49: thai.setSelected(true);
50:
51: //US radio button
52: us = new JRadioButton("US");
53: us.setMnemonic(KeyEvent.VK_U);
54:
55: //logical grouping - to ensure that only
56: //one radio button is selected
57: ButtonGroup group = new ButtonGroup();
58: group.add(thai);
59: group.add(us);
60:
61: //add listeners to both buttons
62: thai.addActionListener(this);
63: us.addActionListener(this);
64:
65: //add both buttons to panel - one row, two columns
66: radioPanel = new JPanel(new GridLayout(1, 2));
67: radioPanel.add(thai);
68: radioPanel.add(us);
69:
70: //date patterns
71: String[] patternStrings = {
72: "dd MMMMM yyyy",

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

73: "dd.MM.yy",
74: "MM/dd/yy",
75: "yyyy.MM.dd G 'at' hh:mm:ss z",
76: "EEE, MMM d, ''yy",
77: "h:mm a",
78: "H:mm:ss:SSS",
79: "K:mm a,z",
80: "yyyy.MMMMM.dd GGG hh:mm aaa"

intro. to Java (FEU.faa)


81: };
82:
83: //panel contains radio buttons and combo box
84: //user can also edit item in the combo box
85: topPanel = new JPanel();
86: patterns = new JComboBox(patternStrings);
87: patterns.setEditable(true);
88: patterns.addActionListener(this);
89: topPanel.add(radioPanel);
90: topPanel.add(patterns);
91:
92: //displaying label with border and title
93: result = new JLabel(" ");
94: result.setForeground(Color.blue);
95: result.setBorder(BorderFactory.createTitledBorder(
96: BorderFactory.createLineBorder(Color.LIGHT_GRAY),
"Current Date/Time",
97: TitledBorder.LEFT, TitledBorder.TOP
98: ));
99:
100: //add both panels to the main panel
101: add(topPanel, BorderLayout.CENTER);
102: add(result, BorderLayout.SOUTH);
103: }
104:
105: //performs action
106: public void actionPerformed(ActionEvent e) {
107: //THAI radio button is checked
108: if(e.getSource() == thai) {
109: Locale.setDefault(new Locale("th", "TH"));
110: String newSelection = (String)patterns.getSelectedItem();
111: pattern = newSelection;
112: reformat();
113: }
114: //US radio button is checked
115: if(e.getSource() == us) {
116: Locale.setDefault(new Locale("us", "US"));
117: String newSelection = (String)patterns.getSelectedItem();
118: pattern = newSelection;
119: reformat();
120: }
121: //item in combo box is selected
122: if(e.getSource() == patterns) {
123: String newSelection = (String)patterns.getSelectedItem();
124: pattern = newSelection;
125: reformat();
126: }
127: }
128:
129: //formats and displays today's date with either TH or US locale.
130: private void reformat() {
131: //gets today's date and format it with selected pattern and locale
132: Date today = new Date();
133: SimpleDateFormat formatter = new SimpleDateFormat(
134: pattern, Locale.getDefault());
135: try {
136: String dateString = formatter.format(today);
137: result.setForeground(Color.BLACK);
138: result.setText(dateString);
139: }
140: catch (IllegalArgumentException ie) {
141: result.setForeground(Color.RED);
142: result.setText("Error: " + ie.getMessage());
143: }
144: }
145: }

362
บทที่ 11: GUI และ Event Handling

11.4 JCheckBox, JTextField และ JTextArea

โปรแกรมตัวอยางที่จะแสดงใหดูตอไปเปนโปรแกรมการใช JCheckBox และ JTextArea ในการ


นับจํานวนของคํา (words) สระ (vowels) และ ชองวาง (spaces) ที่มีอยูในประโยคทีผ
่ ใู ชใสเขา
สูโปรแกรม

intro. to Java (FEU.faa)


JCheckBox in JPanel JTextField in JPanel

JButton in JPanel JTextArea

ภาพที่ 11.8 JCheckBox, JTextField and JTextArea

จากภาพผลลัพธจะเห็นวาเราไดจัดวาง component ในตําแหนงตาง ๆ ดวยการใช panel เขามา


ชวย โปรแกรมตัวนี้ใช panel 4 ตัวในการวางองคประกอบดังกลาว ตัวแรกใชจัดวาง JCheckBox
3 ตัว ตัวทีส
่ องใชจด
ั วาง JTextField 3 ตัว ตัวที่สามใชจัดวาง JButton3 ตัว สวนตัวสุดทายใช
จัดวาง panel ทั้งสามตัวที่กลาวถึงกับ JTextArea ที่อยูทางดานขวา

เราเริ่มตนดวยการสราง JCheckBox 3 ตัวชื่อ words, vowels และ spaces และ JPanel 1 ตัวที่
ใชเก็บ check box ทั้งสามตัวชื่อ cbPanel ดวยการกําหนดใหเปน grid layout: 3 x 1

words = new JCheckBox("Words");


words.addActionListener(this);
vowels = new JCheckBox("Vowels");
vowels.addActionListener(this);
spaces = new JCheckBox("Spaces");
spaces.addActionListener(this);
cbPanel = new JPanel(new GridLayout(3, 1));
cbPanel.add(words);
cbPanel.add(vowels);
cbPanel.add(spaces);

ตอมาเราก็สราง JTextField 3 ตัวเพื่อเอาไวใชสําหรับแสดงผลที่หาได โดยจะกําหนดใหการ


แสดงผลจํานวนนั้นอยูตรงกึ่งกลางของ text field ทั้งสามตัว พรอมทั้งใช JPanel เก็บ text field
ทั้งสามดวยการใช grid layout: 3 x 1 ที่มช
ี องวางระหวาง component เทากับ 2 pixel

wdCount = new JTextField(8);


wdCount.setEditable(false);
wdCount.setFont(font);
wdCount.setHorizontalAlignment(JTextField.CENTER);
vwCount = new JTextField(8);
vwCount.setEditable(false);
vwCount.setFont(font);
vwCount.setHorizontalAlignment(JTextField.CENTER);
spCount = new JTextField(8);
spCount.setEditable(false);
spCount.setFont(font);
spCount.setHorizontalAlignment(JTextField.CENTER);

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

tfPanel = new JPanel(new GridLayout(3, 1, 2, 2));


tfPanel.add(wdCount);
tfPanel.add(vwCount);
tfPanel.add(spCount);

ขั้นตอนตอไปก็เปนการสรางปุม  สําหรับการแสดงผล และปุมสําหรับการออกจากโปรแกรม โดยจะ


กําหนดใหมีกรอบลอมรอบปุมทั้งสองไว พรอมทั้งใช JPanel 1 ตัวเก็บปุมทัง้ สองไวดวย grid
layout: 1 x 2 ที่มีชองวางเทากับ 2 pixel เชนกัน

intro. to Java (FEU.faa)


ok = new JButton("OK");
ok.addActionListener(this);
exit = new JButton("EXIT");
exit.addActionListener(this);
btPanel = new JPanel(new GridLayout(1, 2, 2, 2));
btPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY),
BorderFactory.createEmptyBorder(4, 4, 4, 4)
));
btPanel.add(ok);
btPanel.add(exit);

หลังจากนั้นเราก็สราง JPanel สําหรับเก็บ panel ทั้งสามตัว

leftPanel = new JPanel(new BorderLayout());


leftPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY),
BorderFactory.createEmptyBorder(2, 2, 2, 2)
));
leftPanel.add(cbPanel, BorderLayout.WEST);
leftPanel.add(tfPanel, BorderLayout.EAST);
leftPanel.add(btPanel, BorderLayout.SOUTH);

ขั้นตอนตอไปก็สราง JTextArea ดวยขนาด 7 x 20 พรอมทัง้ กําหนดใหมีขอ


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

area = new JTextArea(7, 20);


area.setText("Enter your sentences here.");
area.selectAll();
area.setLineWrap(true);
area.setWrapStyleWord(true);

เนื่องจากวาผูใชอาจใสขอความที่มีความยาวเกินขอบเขตของ JTextArea ดังนั้นเราจึงกําหนดให


JTextArea นี้อยูใน scroll pane เพื่อใหสามารถเลื่อนขึ้นลงเพิ่อดูขอความได

JScrollPane scrollPane = new JScrollPane(area);

เสร็จแลวก็นํา component ทั้งสองเขาสู panel หลักตอไป

add(leftPanel);
add(scrollPane);

โปรแกรมตัวอยางเมื่อ run ในครั้งแรกนั้น component ที่มี focus (component ที่กําลัง active)


นั้นคือ JCheckBox ตัวแรกที่ชอื่ words แตเราตองการใหโปรแกรมของเรานั้นมี focus ที่
ขอความที่เราได highlight ไวใน JTextArea เพื่อใหผูใชสามารถเขียนขอความใหมทับไดทน
ั ที
เราจึงตองเขียน code ไวใน constructor ของ class WordsFrame ดังนี้

addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent e) {
panel.area.requestFocusInWindow();
}
});

โดยที่ panel เปน JPanel หลักที่ใชเก็บ component ตาง ๆ และ area เปน JTextArea ที่เราให
ความสนใจ เราจึงเรียกใช method requestFocusInWindow() บน area ที่อยูใน panel นี้

364
บทที่ 11: GUI และ Event Handling

เมื่อจัดวาง UI เสร็จแลวเราก็ตอ  งเขียน code สําหรับการดักฟง event ที่จะเกิดขึ้นจากผูใช เรา


จะแสดงผลก็ตอ  เมื่อผูใชกดปุม
 OK เทานั้น

public void actionPerformed(ActionEvent e) {


String list = area.getText();
int[] counts = new int[3];

intro. to Java (FEU.faa)


counts = getAllCounts(list);

if(e.getSource() == ok) {
if(words.isSelected())
wdCount.setText("" + counts[0]);
else
wdCount.setText("");

if(vowels.isSelected())
vwCount.setText("" + counts[1]);
else
vwCount.setText("");

if(spaces.isSelected())
spCount.setText("" + counts[2]);
else
spCount.setText("");
}

if(e.getSource() == exit) {
System.exit(0);
}
}

เราดึงขอความออกจาก JTextArea ดวยการเรียกใช method getText() ไปเก็บไวในตัวแปร list


เราจะสง list ไปให method getAllCount() ประมวลผล เก็บผลลัพธที่ไดไวใน array counts
โดยกําหนดให count[0] เก็บจํานวนของคํา count[1] เก็บจํานวนของสระ และ count[2] เก็บ
จํานวนของชองวาง หลังจากนั้นเราก็ตรวจสอบวา ระหวางปุม OK กับปุม EXIT ตัวไหนถูกกด ถา
เปนปุม OK เราก็จะตรวจสอบตอไปวา check box ตัวไหนระหวาง words, vowels และ spaces
ถูกเลือก หรือถูกเลือกทั้งหมด การแสดงผลก็ขึ้นอยูกับ check box เหลานี้ เราจะไมแสดง code
ที่อยูภายใน method getAllCount() ผูอานดูไดจาก code ทั้งหมดของโปรแกรมเอง ภาพที่เห็น
เปนตัวอยางการ run โปรแกรมดวยขอความที่ใสเขามาจาก keyboard

ภาพที่ 11.9 ตัวอยางการ run โปรแกรม CountWords.java

ผูอานไมจําเปนตองใสขอความในโปรแกรมตัวนี้ เพื่อเรียกใช JCheckBox เพราะเรามีขอความ


เบื้องตนอยูภายใน JTextArea เพื่อดัก error ที่อาจเกิดขึ้นถาผูใชเลือก check box ใด check
box หนึ่งแลวกดปุม OK

[การแสดงผลลัพธทันทีที่ผูใชเลือก check box]

เราไมจําเปนทีต
่ องใชปุม OK เปนตัวกําหนดการประมวลผลในโปรแกรม CountWords.java ก็ได
เราอาจใหมีการประมวลผลทันทีทผ ี่ ูใชเลือก check box ตัวใดตัวหนึ่ง ซึ่งในการกระทําดังกลาว
เราตองเปลี่ยน code ภายใน method actionPerformed() ใหทาํ การตรวจสอบการเลือกของ
check box ทันทีโดยไมตองตรวจสอบการกดปุม วิธีการก็งายมาก เพียงแคเอา code ที่วา

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

if(e.getSource() == ok) {

และเครื่องหมายปกกา } ที่ปดตัว if ออกดวยก็เทานั้นเอง (แตยังเก็บ code ที่อยูภายในไว)

Code ทั้งหมดของ CountWords.java มีดังนี้

intro. to Java (FEU.faa)


1: /**
2: Using JCheckBox, JTextField, JTextArea and BorderFactory
3: */
4:
5: import javax.swing.*;
6: import javax.swing.border.*;
7: import java.awt.*;
8: import java.awt.event.*;
9: import java.util.*;
10: import java.text.*;
11:
12: class CountWords {
13: public static void main(String[] args) {
14: JFrame.setDefaultLookAndFeelDecorated(true);
15: JFrame frame = new WordsFrame();
16: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17: frame.setVisible(true);
18: }
19: }
20:
21: class WordsFrame extends JFrame {
22: WordsPanel panel;
23:
24: //create a frame and a panel
25: public WordsFrame() {
26: setTitle("Words, vows and spaces");
27: panel = new WordsPanel();
28:
29: /**
30: The first component in the frame that has focus is
31: JCheckBox: words. We need to move the focus to JTextArea,
32: so we make sure 'area' in JPanel gets the focus
33: whenever a frame is activated.
34: This is a work-around solution, a simple call to
35: requestFocusInWindow() on text area should work, but it didn't!
36: */
37: addWindowListener(new WindowAdapter() {
38: public void windowActivated(WindowEvent e) {
39: panel.area.requestFocusInWindow();
40: }
41: });
42:
43: //add this panel to the frame
44: add(panel);
45: pack();
46: }
47: }
48:
49: //words, vows and spaces panel
50: class WordsPanel extends JPanel implements ActionListener {
51: JPanel cbPanel; //panel for check boxes
52: JPanel tfPanel; //panel for text fields
53: JPanel btPanel; //panel for buttons
54: JPanel leftPanel; //panel for panels above
55: JCheckBox words; //word count check box
56: JCheckBox vowels; //vowel count check box
57: JCheckBox spaces; //space count check box
58: JButton ok; //ok button
59: JButton exit; //exit button
60: JTextArea area; //area for input
61: JTextField wdCount; //text field for words
62: JTextField vwCount; //text field for vowels
63: JTextField spCount; //text field for spaces
64: Font font = new Font("Serif", Font.BOLD, 12);
65:

366
บทที่ 11: GUI และ Event Handling

66: public WordsPanel() {


67: setLayout(new FlowLayout());
68:
69: //JCheckBox in a panel
70: words = new JCheckBox("Words");
71: words.addActionListener(this);
72: vowels = new JCheckBox("Vowels");
73: vowels.addActionListener(this);

intro. to Java (FEU.faa)


74: spaces = new JCheckBox("Spaces");
75: spaces.addActionListener(this);
76: cbPanel = new JPanel(new GridLayout(3, 1));
77: cbPanel.add(words);
78: cbPanel.add(vowels);
79: cbPanel.add(spaces);
80:
81: //JTextFields in a panel
82: wdCount = new JTextField(8);
83: wdCount.setEditable(false);
84: wdCount.setFont(font);
85: wdCount.setHorizontalAlignment(JTextField.CENTER);
86: vwCount = new JTextField(8);
87: vwCount.setEditable(false);
88: vwCount.setFont(font);
89: vwCount.setHorizontalAlignment(JTextField.CENTER);
90: spCount = new JTextField(8);
91: spCount.setEditable(false);
92: spCount.setFont(font);
93: spCount.setHorizontalAlignment(JTextField.CENTER);
94: tfPanel = new JPanel(new GridLayout(3, 1, 2, 2));
95: tfPanel.add(wdCount);
96: tfPanel.add(vwCount);
97: tfPanel.add(spCount);
98:
99: //ok and exit buttons in their panel
100: ok = new JButton("OK");
101: ok.addActionListener(this);
102: exit = new JButton("EXIT");
103: exit.addActionListener(this);
104: btPanel = new JPanel(new GridLayout(1, 2, 2, 2));
105: btPanel.setBorder(BorderFactory.createCompoundBorder(
106: BorderFactory.createLineBorder(Color.LIGHT_GRAY),
107: BorderFactory.createEmptyBorder(4, 4, 4, 4)
108: ));
109: btPanel.add(ok);
110: btPanel.add(exit);
111:
112: //panel contains JCheckBox panel,
113: //JTextField panel and JButton panel
114: leftPanel = new JPanel(new BorderLayout());
115: leftPanel.setBorder(BorderFactory.createCompoundBorder(
116: BorderFactory.createLineBorder(Color.LIGHT_GRAY),
117: BorderFactory.createEmptyBorder(2, 2, 2, 2)
118: ));
119: leftPanel.add(cbPanel, BorderLayout.WEST);
120: leftPanel.add(tfPanel, BorderLayout.EAST);
121: leftPanel.add(btPanel, BorderLayout.SOUTH);
122:
123: //area where input is entered
124: area = new JTextArea(7, 20);
125: area.setText("Enter your sentences here.");
126: /**
127: highlights the default text and
128: wraps each word in the area.
129: */
130: area.selectAll();
131: area.setLineWrap(true);
132: area.setWrapStyleWord(true);
133:
134: //add area to scroll pane to get auto-activate scroll bar
135: JScrollPane scrollPane = new JScrollPane(area);
136:
137: //add components to the main panel
138: add(leftPanel);
139: add(scrollPane);

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

140: }
141:
142: //performs action
143: public void actionPerformed(ActionEvent e) {
144: //gets sentences from JTextArea
145: String list = area.getText();
146: //array to store each count
147: int[] counts = new int[3];

intro. to Java (FEU.faa)


148: //calls getAllCounts() to process the list.
149: counts = getAllCounts(list);
150:
151: //OK button is clicked
152: if(e.getSource() == ok) {
153: //words check box
154: if(words.isSelected())
155: wdCount.setText("" + counts[0]);
156: else
157: wdCount.setText("");
158:
159: //vows check box
160: if(vowels.isSelected())
161: vwCount.setText("" + counts[1]);
162: else
163: vwCount.setText("");
164:
165: //spaces check box
166: if(spaces.isSelected())
167: spCount.setText("" + counts[2]);
168: else
169: spCount.setText("");
170: }
171:
172: //EXIT button is clicked
173: if(e.getSource() == exit) {
174: System.exit(0);
175: }
176: }
177:
178: //counts words, vowels and spaces
179: private int[] getAllCounts(String list) {
180: /**
181: counted[0] = words,
182: counted[1] = vowels,
183: counted[2] = spaces.
184: */
185: int[] counted = new int[3];
186:
187: boolean preWhite = true; //white space ahead of a word
188: int index = 0;
189:
190: //loops through each character in the string
191: while(index < list.length()) {
192: char ch = list.charAt(index++);
193: boolean curWhite = Character.isWhitespace(ch);
194: /**
195: only when we found a white space after
196: each word, this statement is true.
197: */
198: if(preWhite && !curWhite)
199: counted[0]++;
200: preWhite = curWhite;
201:
202: if(curWhite)
203: counted[2]++;
204:
205: ch = Character.toLowerCase(ch);
206: if(ch == 'a' || ch == 'e' || ch == 'i' ||
207: ch == 'o' || ch == 'u')
208: counted[1]++;
209: }
210: return counted;
211: }
212: }

368
บทที่ 11: GUI และ Event Handling

ถาเราตองการใหมีการ confirm หลังจากที่ผใู ชกดปุม EXIT เชนเดียวกันกับที่โปรแกรมหลาย ๆ


ตัวนิยมทํากัน เราก็ทําไดดวยการเปลี่ยน code ที่ตรวจสอบปุม  EXIT ใหทําการแสดง dialog
window ดังนี้

if(e.getSource() == exit) {
int status = JOptionPane.showConfirmDialog(null, "Are you sure?");

intro. to Java (FEU.faa)


if(status == JOptionPane.OK_OPTION)
System.exit(0);
}

11.5 JSlider และ JSpinner

JSlider เปนองคประกอบอีกตัวหนึ่งทีย ่ อมใหการเปลี่ยนแปลงตัวเลือกอยูในแบบตอเนื่อง


โปรแกรมตัวอยางที่เราแสดงใหดูนี้เปนการเปลี่ยนองศาจาก Celsius ใหเปน Fahrenheit หรือ
จาก Fahrenheit ใหเปน Celsius โดยการเปลีย ่ นแปลงจะเกิดขึ้นโดยอัตโนมัติถาหาก slider ตัว
ถูกเคลื่อนขึ้นหรือลง slider อีกตัวก็จะเคลื่อนตามคาที่ไดถูกเปลี่ยนไปจากการคํานวณ

ภาพที่ 11.10 การใช JSlider

การสราง JSlider ก็ทําไดดวยการประกาศ JSlider fahSlider = new JSlider() เชน

fahSlider = new JSlider(JSlider.VERTICAL);


fahSlider.setMaximum(MAX_FAH);
fahSlider.setMinimum(MIN_FAH);
fahSlider.setMajorTickSpacing(10);
fahSlider.setMinorTickSpacing(5);
fahSlider.setPaintTicks(true);
fahSlider.setPaintLabels(true);

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

fahSlider.setValue(0);
fahSlider.addChangeListener(this);

การประกาศที่เห็นนี้เรากําหนดให slider ของเราอยูในแนวตั้ง (vertical) ดวยการกําหนดคา


ภายใน constructor ใหเปน JSlider.VERTICAL (ผูอานสามารถเปลีย ่ นใหเปนแนวนอนดวยการ
ใช JSlider.HORIZONTAL แทน) พรอมกับกําหนดใหคาสูงสุด และคาต่ําสุดมีคาเปน MAX_FAH
และ MIN_FAH ตามลําดับ เรายังสามารถกําหนดใหมีขด ี บอกคาใน slider ดวยการเรียกใช

intro. to Java (FEU.faa)


method setPaintTicks(true) และ setPaintLabels(true) พรอมทั้งกําหนดใหมีขด ี บอกคาดวย
method setMeajorTickSpacing(10) และ method setMinorSpacing(5) ซึ่งหมายถึงการ
กําหนดขีดหลักใหหางกัน 10 หนวยและขีดรองหางกัน 5 หนวย (หนวยของ slider) เรายังได
กําหนดคาเริ่มตนของ slider ใหชท
ี้ ี่ 0

การดักฟง event ของ JSlider นั้นเราใช interface ChangeListener เพราะฉะนั้นเราจึงตองเรียก


method addChangeListener() บน JSlider นั้น และ method ที่เราตองสรางขึ้นสําหรับการ
สนองตอบก็คือ method stateChanged() ดังนี้

public void stateChanged(ChangeEvent e) {


int temp;
JSlider s = (JSlider)e.getSource();

if(s == fahSlider) {
temp = fahSlider.getValue();
celSlider.setValue((int)((temp - 32) / 1.8));
}
else {
temp = celSlider.getValue();
fahSlider.setValue((int)((temp * 1.8) + 32));
}
}

สวน code อื่น ๆ ของโปรแกรมการใช JSlider ก็มีดังนี้

1: /**
2: Using JSlider and ChangeListener
3: */
4:
5: import javax.swing.*;
6: import javax.swing.border.*;
7: import javax.swing.event.*;
8: import java.awt.*;
9: import java.awt.event.*;
10:
11: class TempConversion {
12: public static void main(String[] args) {
13: JFrame.setDefaultLookAndFeelDecorated(true);
14: JFrame frame = new SliderFrame();
15: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: frame.setVisible(true);
17: }
18: }
19:
20: class SliderFrame extends JFrame implements ChangeListener {
21: JLabel celTitle; //Celcius tttle
22: JLabel fahTitle; //Fahrenheit title
23: JSlider celSlider;//Celsius's slider
24: JSlider fahSlider;//Fahenheit's slider
25: JPanel titlePanel;//panel for titles
26: JPanel tempPanel; //panel for sliders
27: private final int WIDTH = 300, HEIGHT = 480;
28: private final int MIN_FAH = -40, MAX_FAH = 120;
29:
30: public SliderFrame() {
31: setTitle("Temperature Conversion");
32: setSize(WIDTH, HEIGHT);
33: setLayout(new BorderLayout());
34:
35: //creates titles and panel
36: celTitle = new JLabel("Celsius");
37: celTitle.setHorizontalAlignment(JLabel.CENTER);

370
บทที่ 11: GUI และ Event Handling

38: fahTitle = new JLabel("Fahrenheit");


39: fahTitle.setHorizontalAlignment(JLabel.CENTER);
40:
41: titlePanel = new JPanel(new GridLayout(1, 2));
42: titlePanel.add(celTitle);
43: titlePanel.add(fahTitle);
44:
45: //creates slider for Celsius with values corresponding

intro. to Java (FEU.faa)


46: //to max and min Fahrenheit
47: celSlider = new JSlider(JSlider.VERTICAL);
48: celSlider.setMaximum((int)((MAX_FAH - 32) / 1.8));
49: celSlider.setMinimum((int)((MIN_FAH - 32) / 1.8));
50: celSlider.setMajorTickSpacing(5);
51: celSlider.setMinorTickSpacing(1);
52: celSlider.setPaintTicks(true);
53: celSlider.setPaintLabels(true);
54: celSlider.setValue((int)((0 - 32) / 1.8));
55: celSlider.addChangeListener(this);
56:
57: //creates slider for Fahrenheit with default values
58: fahSlider = new JSlider(JSlider.VERTICAL);
59: fahSlider.setMaximum(MAX_FAH);
60: fahSlider.setMinimum(MIN_FAH);
61: fahSlider.setMajorTickSpacing(10);
62: fahSlider.setMinorTickSpacing(5);
63: fahSlider.setPaintTicks(true);
64: fahSlider.setPaintLabels(true);
65: fahSlider.setValue(0);
66: fahSlider.addChangeListener(this);
67:
68: //adds sliders to panel
69: tempPanel = new JPanel(new GridLayout(1, 2));
70: tempPanel.add(celSlider);
71: tempPanel.add(fahSlider);
72:
73: //adds panels to frame
74: add(titlePanel, BorderLayout.NORTH);
75: add(tempPanel, BorderLayout.CENTER);
76: }
77:
78: //monitor state changes from slider
79: public void stateChanged(ChangeEvent e) {
80: int temp;
81: JSlider s = (JSlider)e.getSource();
82:
83: //converts to Fahrenheit if Celsius slider is activated
84: if(s == fahSlider) {
85: temp = fahSlider.getValue();
86: celSlider.setValue((int)((temp - 32) / 1.8));
87: }
88: //converts to Celsius if Fahrenheit slider is activated
89: else {
90: temp = celSlider.getValue();
91: fahSlider.setValue((int)((temp * 1.8) + 32));
92: }
93: }
94: }

[การใช JSpinner]

โปรแกรมตัวอยางตอไปที่จะแสดงใหดูเปนการใช JSpinner ในการเปลี่ยนอุณหภูมิเชนที่ไดทํา


ในโปรแกรม TempConversion.java

JSpinner เปน text field ชนิดหนึ่งที่มีปุมเล็ก ๆ สองปุมอยูดา นขาง ซึ่งเมื่อปุมถูกกดคาใน text


field ก็จะเปลี่ยนแปลงในลักษณะของการเพิ่มหรือการลดคา คาที่อยูใน spinner สามารถเปนได
ทั้งตัวเลข วัน/เวลา คาที่อยูใ น list หรือคาใด ๆ ที่สามารถหาคากอนหนาหรือคาทีต ่ ามมาได

เราจะดัดแปลงโปรแกรมเล็กนอยเพื่อเปนการใหผูอานไดสมั ผัสถึงการใช JSpinner อยางงาย ๆ


โดยเราจะกําหนดใหมี spinner 1 ตัวสําหรับการกําหนดคาและจะใช label 2 ตัวเปนตัวแสดงคา
ของอุณหภูมิที่เปน Celsius และ Fahrenheit จากคาที่ไดจาก spinner นี้

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

intro. to Java (FEU.faa)


JSpinner

ภาพที่ 11.11 การใช JSpinner

ภาพตัวอยางผลลัพธที่ไดแสดงใหเห็นถึงการใช JSpinner ที่มีคา เทากับ 32 พรอมกับแสดงคา


ของ Celsius และ Fahrenheit (32°F = 0°C และ 32°C = 89°F)

การประกาศใช JSpinner ก็ทําไดสองแบบคือ

JSpinner spinner = new JSpinner();

หรือ

JSpinner spinner;
spinner = new JSpinner(new SpinnerNumberModel(START, MIN, MAX, 1));

โดยที่การประกาศตัวแรกเปนการใช spinner ที่มีคา เปน integer เริ่มตนที่ 0 และมีคาเพิ่มหรือลด


ทีละหนึ่งคา สวนตัวทีส
่ องเปนการเรียกใช spinner model ทีม
่ ีอยูดวยคาเริม
่ ตนที่ START คา
ต่ําสุดคือ MIN คาสูงสุดคือ MAX และการเพิ่มหรือลดคาเปนทีละหนึ่งคา

การดึงคาออกจาก spinner ก็เรียกใช method getValue() เชน

value = (Integer)spinner.getValue();

ทั้งนี้เราตองเปลี่ยนคาทีส
่ งกลับใหเปนไปตามคาที่เราตองการใช เนื่องจากวา getValue() จะสง
Object กลับออกมา

สําหรับ model ตัวอื่น ๆ ที่ Java มีใหกค


็ ือ SpinnerListModel และ SpinnerDateModel แตเราก็
สามารถสราง model ขึ้นมาใชเองได

Code ทั้งหมดของโปรแกรม TempConversion2.java มีดังนี้

1: /**
2: Using JSpinner
3: */
4:
5: import javax.swing.*;
6: import javax.swing.border.*;
7: import javax.swing.event.*;
8: import java.awt.*;
9: import java.awt.event.*;
10:
11: class TempConversion2 {
12: public static void main(String[] args) {
13: JFrame.setDefaultLookAndFeelDecorated(true);
14: JFrame frame = new SpinnerFrame();
15: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16: frame.setVisible(true);
17: }
18: }
19:

372
บทที่ 11: GUI และ Event Handling

20: class SpinnerFrame extends JFrame implements ChangeListener {


21: JSpinner spinner;
22: JPanel panel, panel1, panel2;
23: JLabel celTitle, fahTitle;
24: JLabel window1,window2;
25: private final int WIDTH = 250, HEIGHT = 90;
26: private final int MIN = -200, MAX = 200;
27: private final int START = 0;

intro. to Java (FEU.faa)


28:
29: public SpinnerFrame() {
30: setTitle("Temperature");
31: setSize(WIDTH, HEIGHT);
32:
33: //cteate JSpinner with initial values
34: spinner = new JSpinner(new SpinnerNumberModel(START, MIN, MAX, 1));
35: spinner.setBorder(BorderFactory.createCompoundBorder(
36: BorderFactory.createLineBorder(Color.LIGHT_GRAY),
37: BorderFactory.createEmptyBorder(2, 2, 2, 2)
38: ));
39: spinner.addChangeListener(this);
40:
41: celTitle = new JLabel("Celsius");
42: celTitle.setHorizontalAlignment(JLabel.CENTER);
43: fahTitle = new JLabel("Fahrenheit");
44: fahTitle.setHorizontalAlignment(JLabel.CENTER);
45:
46: panel1 = new JPanel(new GridLayout(1, 3, 2, 2));
47: panel1.setBorder(BorderFactory.createCompoundBorder(
48: BorderFactory.createLineBorder(Color.LIGHT_GRAY),
49: BorderFactory.createEmptyBorder(4, 4, 4, 4)
50: ));
51: panel1.add(celTitle);
52: panel1.add(new JLabel("F / C", JLabel.CENTER));
53: panel1.add(fahTitle);
54:
55: window1 = new JLabel(" ", JLabel.CENTER);
56: window2 = new JLabel(" ", JLabel.CENTER);
57:
58: panel2 = new JPanel(new GridLayout(1, 3, 2, 2));
59: panel2.setBorder(
60: BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
61: panel2.add(window1);
62: panel2.add(spinner);
63: panel2.add(window2);
64:
65: panel = new JPanel(new BorderLayout());
66: panel.add(panel1, BorderLayout.NORTH);
67: panel.add(panel2, BorderLayout.CENTER);
68:
69: add(panel, BorderLayout.CENTER);
70: }
71:
72: //performs event
73: public void stateChanged(ChangeEvent e) {
74: int value;
75: String cel, fah;
76:
77: //gets value fromspinner
78: value = (Integer)spinner.getValue();
79:
80: //converts it to Celsius and sends it to window
81: cel = "" + (int)((value - 32) / 1.8);
82: window1.setText(cel);
83:
84: //converts to Fahrenheit and sends it to window
85: fah = "" + (int)((value * 1.8) + 32);
86: window2.setText(fah);
87: }
88: }

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

11.6 JProgressBar และ ProgressMonitor

ในกรณีที่เราตองการแสดงถึงขัน้ ตอนความกาวหนาของการทํางานในโปรแกรม เชน การ


download ขอมูลจาก WWW หรือการอานขอมูลจากหนวยความจํา หนึ่งใน component ที่เรา
สามารถเรียกใชไดคือ JProgressBar ภาพที่ 11.12 แสดงการใช JProgressBar กับงานสมมติที่
เราสรางขึ้นดวยการใช Thread

intro. to Java (FEU.faa)


JButton JTextField แสดงจํานวนของ
งานที่เกิดขึ้น

JProgressBar ที่มีการ Cursor ขณะที่งาน


แสดง % ของงาน กําลังกาวหนา

ภาพที่ 11.12 การใช JProgressBar

JProgressBar เปน component ที่เปนรูปสี่เหลี่ยมที่มีการ fill ดวยสีเพื่อเปนการบอกถึง


ความกาวหนาของงานหรือกระบวนการ เชนในภาพความคืบหนาของงานถูกบอกดวย n% เรา
สราง progress bar ดวย

progressBar = new JProgressBar();

ถาเราตองการใหมีการแสดงความคืบหนาเราใช

progressBar.setStringPainted(true);

เราสามารกําหนดคาสูงสุดที่ progress bar แสดงดวย

progressBar.setMaximum(1000);

ในขณะที่งานกําลังดําเนินอยูหากเราตองการใหแสดงสถานะของ cursor เพื่อบอกใหผูใชรวู า


งานกําลังถูก execute เราก็ใช

setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

และเมื่อเสร็จงานเราก็เปลี่ยน cursor กลับเปนอยางเดิมดวย

setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));

cursor ยังมีรูปแบบอีกหลายแบบที่เราสามารถใชได ซึ่งผูอานสามารถหาดูไดจาก Java API เอง

โปรแกรมตัวอยางของเราใช Thread simulation เปนตัวสรางงานอยางงาย ๆ นั่นก็คือ เพิ่มคา


ใหกับตัวแปรทีละหนึ่งคาทุก ๆ ครั้งหลังจากถูก interrupt ใหกลับมาทํางานหลังจากการ sleep
ไประยะเวลาหนึ่ง ในเวลาเดียวกัน thread ก็เรียก method setValue() ของ JProgressBar เพื่อ
ทําการเปลีย ่ นคาของ progress bar กระบวนการจะสิ้นสุดลงเมื่อคาที่เปลี่ยนแปลงเทากับคาของ
target ที ไ
่ ด กําหนดไว

เนื่องจากวาการเรียก Swing method จะตองทําใน event dispatching thread ดังนั้นเราจึง


ปรับปรุง code ใหรองรับงานดังกลาว (ผูอานจะเห็นวา code สวนนี้จะแตกตางจากสวนอื่น ซึ่งใน
บางครั้งตัวอยางที่ไมซับซอนมากนักก็จะไมสนใจเรื่องของ thread safe นี้ เพราะเราเพียงแต
ตองการแสดงใหดูเปนตัวอยางเทานั้น) อีกสิ่งหนึ่งที่เราทํากับโปรแกรมตัวอยางนี้คือ การ

374
บทที่ 11: GUI และ Event Handling

กําหนดการสนองตอบตอ event นั้นจะทําบน component นั้น ๆ ทันที กลาวคือเราจะเขียน


code ลงบน component แตจะไมสราง method actionPerformed() หลักของตัวโปรแกรมเอง
เพื่องานดังกลาว เชน การสนองตอบตอการทํางานของปุม Start และงานของ Timer

start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
progressBar.setMaximum(1000);

intro. to Java (FEU.faa)


setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
activity = new Activity(1000);
new Thread(activity).start();
timer.start();
start.setEnabled(false);
}
});

timer = new Timer(500, new ActionListener() {


public void actionPerformed(ActionEvent e) {
int current = activity.getCurrent();
showText.setText("" + current);
progressBar.setStringPainted(true);
progressBar.setValue(current);
if(current == activity.getTarget()) {
timer.stop();
start.setEnabled(true);
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
});

ปุม Start จะเปนตัวกําหนดคาเบื้องตนใหกับ progress bar กําหนดหนาตาของ cursor สราง


activity สมมติ เริ่มการทํางานของ Thread ดวย activity ทีส ่ รางขึ้น เริ่มการทํางานของ Timer
และปลดปุม (disable) Start เพื่อไมใหผใู ชกดอีก

ภายในตัว timer เองก็จะดึงเอาคาของ current ออกมาเพื่อแสดงผลใน text field ปรับปรุง


progress bar และตรวจสอบดูวางานที่ทาํ ไดเปาหมายหรือยัง ถาถึงแลวก็หยุดการทํางานของ
timer (timer.stop()) เปลี่ยนปุม Start ใหอยูในสถานะทีผ
่ ใู ชกดได (enable) พรอมกับเปลี่ยน
cursor ใหเปนรูปเดิม (default cursor)

Code ทั้งหมดของโปรแกรมมีดังนี้

1: /**
2: Using JProgressBar
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import javax.swing.*;
8: import javax.swing.Timer;
9: import javax.swing.event.*;
10: import javax.swing.border.*;
11:
12: public class SimulatedTask extends JPanel {
13: private JProgressBar progressBar;
14: private JButton start;
15: private Timer timer;
16: private JTextField showText;
17: private Activity activity;
18:
19: public SimulatedTask() {
20: super(new BorderLayout());
21:
22: showText = new JTextField(10);
23: showText.setHorizontalAlignment(JTextField.CENTER);
24: showText.setFont(new Font("Dialog", Font.BOLD, 12));
25: showText.setEditable(false);
26:
27: start = new JButton("Start");
28:
29: progressBar = new JProgressBar();

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

30: progressBar.setBorder(
31: BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
32: progressBar.setStringPainted(true);
33:
34: JPanel panel = new JPanel(new GridLayout(1, 2, 2, 2));
35: panel.add(start);
36: panel.add(showText);
37:

intro. to Java (FEU.faa)


38: add(panel, BorderLayout.NORTH);
39: add(progressBar, BorderLayout.SOUTH);
40:
41: //performs activity when button is hit
42: start.addActionListener(new ActionListener() {
43: public void actionPerformed(ActionEvent e) {
44: progressBar.setMaximum(1000);
45: setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
46: activity = new Activity(1000);
47: new Thread(activity).start();
48: timer.start();
49: start.setEnabled(false);
50: }
51: });
52:
53: //starts timer and shows progress
54: timer = new Timer(500, new ActionListener() {
55: public void actionPerformed(ActionEvent e) {
56: int current = activity.getCurrent();
57: showText.setText("" + current);
58: progressBar.setStringPainted(true);
59: progressBar.setValue(current);
60: if(current == activity.getTarget()) {
61: timer.stop();
62: start.setEnabled(true);
63: setCursor(Cursor.getPredefinedCursor(
Cursor.DEFAULT_CURSOR));
64: }
65: }
66: });
67: }
68:
69: //creates and show GUI
70: private static void setGUI() {
71: JFrame.setDefaultLookAndFeelDecorated(true);
72:
73: //creates and set up the window.
74: JFrame frame = new JFrame("JProgessBar");
75: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
76:
77: //creates and set up the content pane.
78: JComponent newContentPane = new SimulatedTask();
79: newContentPane.setOpaque(true); //content panes must be opaque
80: frame.setContentPane(newContentPane);
81:
82: //displays the window.
83: frame.pack();
84: frame.setVisible(true);
85: }
86:
87: public static void main(String[] args) {
88: //schedules a job for the event-dispatching thread:
89: //creating and showing this application's GUI.
90: javax.swing.SwingUtilities.invokeLater(new Runnable() {
91: public void run() {
92: setGUI();
93: }
94: });
95: }
96: }
97:
98: //simulated activity for progress bar
99: class Activity implements Runnable {
100: private int target;
101: private volatile int current;
102:

376
บทที่ 11: GUI และ Event Handling

103: public Activity(int t) {


104: current = 0;
105: target = t;
106: }
107:
108: public int getTarget() {
109: return target;
110: }

intro. to Java (FEU.faa)


111:
112: public int getCurrent() {
113: return current;
114: }
115:
116: public void run() {
117: try {
118: while(current < target) {
119: Thread.sleep(10);
120: current++;
121: }
122: }
123: catch(InterruptedException e) {}
124: }
125: }

[ProgressMonitor]

Progress monitor เปน component อีกตัวหนึ่งที่เราสามารถใชดูความคืบหนาของงานได


เชนเดียวกับ JProgressBar แตตางจาก progress bar ตรงที่วา progress monitor จะใช
dialog box เปนตัวแสดงความคืบหนาแทน ดังทีแ ่ สดงในภาพที่ 11.13

หลังจากที่กดปุม Start ได


ประมาณ 1 วินาที dialog
window ก็จะ pop up ขึ้นมา
พรอมกับแสดงความคืบหนา
ของงาน ในแทงสี่เหลี่ยม สวน
การแสดงคา n% นั้นเราตอง
เรียกใช method setNote()

ภาพที่ 11.13 ProgressMonitor แสดงความคืบหนาของงาน

การเรียกใช ProgressMonitor ก็คลายกับ JProgressBar จะตางกันตรงทีค่ วามคืบหนาของงาน


จะถูกแสดงผานทาง dialog window (pop up) ดังนั้นเราจะไมสราง progress monitor ในที่อื่น
ใด เชนที่เราทํากับ JProgressBar แตเราจะสราง progress monitor ใน method
actionPerformed() ของปุม Start ทั้งนี้ก็เพราะวา เราจะแสดงความคืบหนาหลังจากทีผ
่ ูใชกด
ปุม Start นั่นเอง

[ในการที่จะให dialog window ของเรามีหนาตาที่เปนของ Java เชนเดียวกันกับหนาตางหลัก


ของโปรแกรมนัน ้ เราจะตองกําหนด look and feel ของ JDialog ดวย

JDialog.setDefaultLookAndFeelDecorated(true);

หลังจากที่เรากําหนดหนาตาของตัวโปรแกรมหลัก (ภายใน method setUI())]

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

start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
activity = new Activity(1000);
thread = new Thread(activity);
thread.start();

progressDialog = new ProgressMonitor(SimulatedTask2.this,

intro. to Java (FEU.faa)


"Waiting for activity",
"Progress: ",
0,
activity.getTarget());

progressDialog.setMillisToDecideToPopup(SECOND);

timer.start();
start.setEnabled(false);
}
});

เราประกาศให progressMonitor เปน Object จาก class ProgerssMonitor ดวย parameter 5


ตัวคือ

1. Component Æ component ที่เปน parent (frame หรือ panel) ของ progress


monitor เอง เชน object ที่เกิดจาก class SimulatedTask2 ของเรา
2. message Æ ขอความที่ตองการแสดงใน dialog window ("Waiting for activity")
3. note Æ ขอความที่อาจมีหรือไมมีใน dialog window ("Progress")
4. min Æ คาต่ําสุดของความคืบหนา (0)
5. max Æ คาสูงสุดของความคืบหนา (activity.getTarget())

หลังจากที่ผใู ชกดปุม Start ไดประมาณหนึ่งวินาที dialog window ก็จะ pop up ขึ้นมาแสดง


ความคืบหนาของงาน (เราใชคําสั่ง setMillisToDecideToPopup())

การ update ตาง ๆ ก็ทําใน method actionPerformed() ของ timer

timer = new Timer(500, new ActionListener() {


public void actionPerformed(ActionEvent e) {
int current = activity.getCurrent();
showText.setText("" + current);
progressDialog.setProgress(current);
progressDialog.setNote("Progress: " + (current/10) + "%");

if(current == activity.getTarget() || progressDialog.isCanceled()) {


timer.stop();
progressDialog.close();
thread.interrupt();
start.setEnabled(true);
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
});

เราเรียก method setProgress() สําหรับการแสดงความคืบหนา และเรียก method setNote()


สําหรับคา % ที่แสดงใน dialog window และเมื่องานสิ้นสุดลงเราก็เรียก method close() ของ
class ProgressMonitor และ method interrupt() ของ class Thread สวน code อื่น ๆ ก็
เหมือนกับที่เราทําในโปรแกรมตัวอยางการใช JProgressBar

Code ของ SimulatedTask2.java มีดังนี้

1: /**
2: Using ProgressMonitor
3: */
4:
5: import java.awt.*;
6: import java.awt.event.*;
7: import javax.swing.*;
8: import javax.swing.Timer;

378
บทที่ 11: GUI และ Event Handling

9: import javax.swing.event.*;
10: import javax.swing.border.*;
11:
12: public class SimulatedTask2 extends JPanel {
13: private ProgressMonitor progressDialog;
14: private JButton start;
15: private Timer timer;
16: private JTextField showText;

intro. to Java (FEU.faa)


17: private Thread thread;
18: private JLabel label;
19: private Activity activity;
20: private final int SECOND = 1000;
21:
22: public SimulatedTask2() {
23: super(new BorderLayout());
24:
25:
26: showText = new JTextField(10);
27: showText.setHorizontalAlignment(JTextField.CENTER);
28: showText.setFont(new Font("Dialog", Font.BOLD, 12));
29: showText.setEditable(false);
30:
31: start = new JButton("Start");
32:
33: label = new JLabel("Simulated Task", JLabel.CENTER);
34: label.setBorder(BorderFactory.createEtchedBorder(
35: EtchedBorder.RAISED));
36:
37: JPanel panel = new JPanel(new GridLayout(1, 2, 2, 2));
38: panel.add(start);
39: panel.add(showText);
40:
41: add(panel, BorderLayout.NORTH);
42: add(label, BorderLayout.SOUTH);
43:
44: //performs activity when button is hit
45: start.addActionListener(new ActionListener() {
46: public void actionPerformed(ActionEvent e) {
47: setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
48: activity = new Activity(1000);
49: thread = new Thread(activity);
50: thread.start();
51:
52: progressDialog = new ProgressMonitor(SimulatedTask2.this,
53: "Waiting for activity",
54: "Progress: ",
55: 0,
56: activity.getTarget());
57:
58: progressDialog.setMillisToDecideToPopup(SECOND);
59:
60: timer.start();
61: start.setEnabled(false);
62: }
63: });
64:
65: //starts timer and shows progress
66: timer = new Timer(500, new ActionListener() {
67: public void actionPerformed(ActionEvent e) {
68: int current = activity.getCurrent();
69: showText.setText("" + current);
70: progressDialog.setProgress(current);
71: progressDialog.setNote("Progress: " + (current/10) + "%");
72:
73: if(current == activity.getTarget() ||
74: progressDialog.isCanceled()) {
75: timer.stop();
76: progressDialog.close();
77: thread.interrupt();
78: start.setEnabled(true);
79: setCursor(Cursor.getPredefinedCursor(
80: Cursor.DEFAULT_CURSOR));
81: }
82: }

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

83: });
84: }
85:
86: //creates and show GUI
87: private static void setGUI() {
88: //set frame to java look and feel
89: JFrame.setDefaultLookAndFeelDecorated(true);
90: //set look and feel of ProgressMonitor

intro. to Java (FEU.faa)


91: JDialog.setDefaultLookAndFeelDecorated(true);
92:
93: //creates and set up the window.
94: JFrame frame = new JFrame("ProgressMonitor");
95: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
96:
97: //creates and set up the content pane.
98: JComponent newContentPane = new SimulatedTask2();
99: newContentPane.setOpaque(true);
100: frame.setContentPane(newContentPane);
101:
102: //displays the window.
103: frame.pack();
104: frame.setVisible(true);
105: }
106:
107: public static void main(String[] args) {
108: //schedules a job for the event-dispatching thread:
109: //creating and showing this application's GUI.
110: javax.swing.SwingUtilities.invokeLater(new Runnable() {
111: public void run() {
112: setGUI();
113: }
114: });
115: }
116: }
117:
118: //simulated activity for progress monitor
119: class Activity implements Runnable {
120: private int target;
121: private volatile int current;
122:
123: public Activity(int t) {
124: current = 0;
125: target = t;
126: }
127:
128: public int getTarget() {
129: return target;
130: }
131:
132: public int getCurrent() {
133: return current;
134: }
135:
136: public void run() {
137: try {
138: while(current < target) {
139: Thread.sleep(10);
140: current++;
141: }
142: }
143: catch(InterruptedException e) {}
144: }
145: }

380
บทที่ 11: GUI และ Event Handling

11.7 JFileChooser, JMenu, JMenuBar และ JMenuItem

โปรแกรมตัวอยางที่แสดงใหดต ู อไปนี้ เปนโปรแกรม text editor อยางงายที่มีการเรียกใช menu


และ file chooser ที่ Java มีใหโดยเราจะออกแบบใหมีการเปดไฟลขึ้นมาใชงานไดจริง สามารถ
เปลี่ยนแปลงขอมูลในไฟล และสรางไฟลใหมได

intro. to Java (FEU.faa)


JMenuBar

JMenu

JMenuItem

ภาพที่ 11.14 การสราง menu

เรากําหนดใหมต ี ัวเลือกใน menu อยูสี่ตวั คือ New, Open, Save และ Exit โดยกําหนดให
Open และ Save มีการใช icon แสดงถึงงานที่ตัวเลือกนีท ้ ํา การสราง menu นี้เราใช JMenuBar
เปนตัวสราง สวนหัวของ menu ใช JMenu และตัวเลือกภายในเราใช JMenuItem เปน
ตัวกําหนด

private JMenuBar setMenuBar() {


JMenuBar menuBar = new JMenuBar();
JMenu file = menuBar.add(new JMenu("File"));
file.add(getNewMenuItem());
file.add(getOpenMenuItem());
file.add(getSaveMenuItem());
file.addSeparator();
file.add(getExitMenuItem());
return menuBar;
}

เราสรางหัวเรื่องของ menu ดวยการเรียก constructor ของ JMenu และใสตัวเลือกดวยการ


เรียก method add() ซึ่งเราจะสง JMenuItem ที่เราไดสรางไวจาก method
getNewMenuItem(), getOpenMenuItem(), getSaveMenuITem() และ
getExitMenuItem() เรายังไดกําหนดใหมีเสนแบงระหวาง menu 3 ตัวแรกและ menu ตัว
สุดทายดวยการเรียก method addSeparator() ลองมาดูตัวอยางการสราง JMenuItem: open

private JMenuItem getOpenMenuItem() {


JMenuItem item = new JMenuItem("Open", openIcon);
item.setMnemonic('o');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int value = fc.showOpenDialog(FileChooser.this);
if(value == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String name = file.getPath();
try {
FileReader reader = new FileReader(file);
area.read(reader, "");
frame.setTitle(title + " " + name);
}

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

catch(IOException ioex) {}
}
}
});
return item;
}

เราสรางตัวเลือกที่มี icon จากประโยค

intro. to Java (FEU.faa)


JMenuItem item = new JMenuItem("Open", openIcon);

ในการสนองตอบตอเหตุการณที่ผใู ชกําหนดเราก็เรียกใช method actionPerformed() เชนเดิม


และกําหนดใหมีการแสดง dialog window สําหรับการเลือกไฟลดวย

int value = fc.showOpenDialog(FileChooser.this);

โดยที่ fc เปนตัวแปรที่สรางมาจาก JFileChooser และ method showOpenDialog() จะแสดง


หนาตางที่เราสามารถเลือกไฟลได โดยเรากําหนดใหไฟลที่ถก
ู เปดเปนไดทั้ง directory และ file

fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

ภาพที่ 11.15 Dialog window สําหรับการเลือกไฟล

เมื่อเราเลือกไฟลใดไฟลหนึ่ง (ไฟลที่เก็บขอมูลเปนตัวอักษรเทานั้น – text file) จาก dialog


window เราก็จะดึงเอาชื่อและที่อยูของไฟลนอ ี้ อกมาดวย เพื่อนําไปแสดงในสวนหัวของ
หนาตาง (frame title)

File file = fc.getSelectedFile();


String name = file.getPath();

การอานไฟลก็ใช constructor ของ class FileReader ดวย parameter: file ที่เราไดเลือกไว


แลว ซึ่งเราก็จะสงไฟลตัวนี้ไปเปดใน text area อีกครั้งหนึ่ง

FileReader reader = new FileReader(file);


area.read(reader, "");
frame.setTitle(title + " " + name);

382
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.16 Simple Text Editor

Text editor ทีเ่ ราสรางขึ้นมีขด


ี จํากัดในการทํางานมากพอสมควร เราไมไดเนนถึงการสราง text
editor แตเราตองการใหผูอานสามารถใช file chooser และสราง menu ตาง ๆ ได ถาผูอา น
อยากที่จะเพิ่มสวนอื่น ๆ เขาสูโ ปรแกรมตัวอยางนี้ก็ยอมทําได แตถาจะใหเปน text editor ที่ดี
นั้นคงตองใช component ตัวอื่นแทน text area เชน JTextPane หรือ JEditorPane

ผูอานสามารถที่จะนําเอา image icon ไปแปะไวใน menu bar ไดเชนเดียวกับที่ทาํ กับ JMenu:


File ดวยการเพิ่ม component ดังกลาวเขาสู menu bar ภายใน method SetMenuBar() ดังนี้

JMenuItem open = new JMenuItem(createImageIcon("open.gif"));


open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {

area.setTabSize(2);

}
});
menuBar.add(open);

เราเพียงแตสราง menu item บน menu bar ใหเปน icon แลวก็เพิ่ม code สําหรับการ
สนองตอบของผูใช (เมื่อปุมถูกกด) เชนเดียวกันกับที่เราไดทําการสนองตอบของ Open ที่มีอยู
ใน menu พรอมกันนี้เรายังไดกําหนดใหขนาดของ tab ใน text area ของเรามีคาเทากับ 2
ตัวอักษรดวยคําสั่ง area.setTab(2)

ภาพที่ 11.17 image icon สําหรับการเปดไฟลที่เราไดเพิ่มเขาสูโปรแกรม

ลูกเลนอื่น ๆ ที่ผูใชสามารถทําได (เพื่อให text editor ตัวนีด


้ ูดีขึ้น) ก็ยังมีอก
ี มาก เชนการ
กําหนดให text area มีเสนกรอบ การกําหนด font เปนตน

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

Code ทั้งหมดของโปรแกรม FileChooser.java มีดังนี้

1: /**
2: Using JFileChooser, JMenu, JMenuBar, JMenuItem
3: files used:
4: open.gif
5: save.gif

intro. to Java (FEU.faa)


6: */
7:
8: import java.io.*;
9: import java.awt.*;
10: import java.awt.event.*;
11: import javax.swing.*;
12: import javax.swing.event.*;
13: import javax.swing.border.*;
14: import javax.swing.filechooser.*;
15:
16: public class FileChooser extends JPanel {
17: private static JTextArea area;
18: private static JFileChooser fc;
19: private static JScrollPane scrollPane;
20: private static JFrame frame;
21: private final String newline = "\n";
22: private static final String title = "Simple Text Editor";
23:
24: public FileChooser() {
25: //nothing to do here
26: }
27:
28: //creates content pane
29: public Container createContentPane() {
30: JPanel contentPane = new JPanel(new BorderLayout());
31: contentPane.setOpaque(true);
32:
33: //creates file chooser
34: fc = new JFileChooser();
35: fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
36:
37: //creates text area
38: area = new JTextArea(15, 50);
39: area.setFont(new Font("Tahoma", Font.PLAIN, 12));
40: area.setBorder(BorderFactory.createEtchedBorder(
EtchedBorder.RAISED));
41: scrollPane = new JScrollPane(area);
42:
43: //add area with scroll bar to content pane
44: contentPane.add(scrollPane, BorderLayout.CENTER);
45:
46: return contentPane;
47: }
48:
49: //creates and show GUI
50: private static void setGUI() {
51: //set frame to java look and feel
52: JFrame.setDefaultLookAndFeelDecorated(true);
53: //set look and feel for dialog window
54: JDialog.setDefaultLookAndFeelDecorated(true);
55:
56: //creates and set up the window.
57: frame = new JFrame(title);
58: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
59:
60: //creates and set up the content pane.
61: FileChooser editor = new FileChooser();
62: frame.setJMenuBar(editor.setMenuBar());
63: frame.setContentPane(editor.createContentPane());
64:
65: //displays the window.
66: frame.pack();
67: frame.setVisible(true);
68: }
69:

384
บทที่ 11: GUI และ Event Handling

70: //creates icon


71: protected static ImageIcon createImageIcon(String path) {
72: java.net.URL url = FileChooser.class.getResource(path);
73: if(url != null)
74: return new ImageIcon(url);
75: else
76: return null;
77: }

intro. to Java (FEU.faa)


78:
79: //creates menu
80: private JMenuBar setMenuBar() {
81: JMenuBar menuBar = new JMenuBar();
82: JMenu file = menuBar.add(new JMenu("File"));
83: file.add(getNewMenuItem());
84: file.add(getOpenMenuItem());
85: file.add(getSaveMenuItem());
86: file.addSeparator();
87: file.add(getExitMenuItem());
88:
89: //image icon to open a file in a menu bar
90: JMenuItem open = new JMenuItem(createImageIcon("open.gif"));
91: open.addActionListener(new ActionListener() {
92: public void actionPerformed(ActionEvent e) {
93: int value = fc.showOpenDialog(FileChooser.this);
94: if(value == JFileChooser.APPROVE_OPTION) {
95: //gets file and path
96: File file = fc.getSelectedFile();
97: String name = file.getPath();
98: try {
99: //sets file in text area
100: FileReader reader = new FileReader(file);
101: area.read(reader, "");
102: area.setTabSize(2);
103: //sets frame's title
104: frame.setTitle(title + " " + name);
105: }
106: catch(IOException ioex) {}
107: }
108: }
109: });
110: menuBar.add(open);
111:
112: return menuBar;
113: }
114:
115: //creates menu item: new
116: private JMenuItem getNewMenuItem() {
117: JMenuItem item = new JMenuItem("New");
118: item.setMnemonic('n');
119: item.addActionListener(new ActionListener() {
120: public void actionPerformed(ActionEvent e) {
121: area.setText("");
122: }
123: });
124: return item;
125: }
126:
127: //creats menu item: open
128: ImageIcon openIcon = createImageIcon("open.gif");
129: private JMenuItem getOpenMenuItem() {
130: JMenuItem item = new JMenuItem("Open", openIcon);
131: item.setMnemonic('o');
132: item.addActionListener(new ActionListener() {
133: public void actionPerformed(ActionEvent e) {
134: int value = fc.showOpenDialog(FileChooser.this);
135: if(value == JFileChooser.APPROVE_OPTION) {
136: //gets file and path
137: File file = fc.getSelectedFile();
138: String name = file.getPath();
139: try {
140: //sets file in text area
141: FileReader reader = new FileReader(file);
142: area.read(reader, "");
143: area.setTabSize(2);

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

144: //sets frame's title


145: frame.setTitle(title + " " + name);
146: }
147: catch(IOException ioex) {}
148: }
149: }
150: });
151: return item;

intro. to Java (FEU.faa)


152: }
153:
154: //creates menu item: save
155: ImageIcon saveIcon = createImageIcon("save.gif");
156: private JMenuItem getSaveMenuItem() {
157: JMenuItem item = new JMenuItem("Save", saveIcon);
158: item.setMnemonic('s');
159: item.addActionListener(new ActionListener() {
160: public void actionPerformed(ActionEvent e) {
161: int value = fc.showOpenDialog(FileChooser.this);
162: if(value == JFileChooser.APPROVE_OPTION) {
163: //gets file and path
164: File file = fc.getSelectedFile();
165: String name = file.getPath();
166: try {
167: //opens file in text area
168: FileWriter writer = new FileWriter(file);
169: area.write(writer);
170: //sets frame's title
171: frame.setTitle(title + " " + name);
172: }
173: catch(IOException ioex) {}
174: }
175: }
176: });
177: return item;
178: }
179:
180: //creates menu item: exit
181: private JMenuItem getExitMenuItem() {
182: JMenuItem item = new JMenuItem("Exit");
183: item.setMnemonic('x');
184: item.addActionListener(new ActionListener() {
185: public void actionPerformed(ActionEvent e) {
186: System.exit(0);
187: }
188: });
189: return item;
190: }
191:
192: public static void main(String[] args) {
193: //schedules a job for the event-dispatching thread:
194: //creating and showing this application's GUI.
195: javax.swing.SwingUtilities.invokeLater(new Runnable() {
196: public void run() {
197: setGUI();
198: }
199: });
200: }
201: }

11.7.1 การใช action ใน menu

เนื่องจากวา item ใน menu จะเปนตัวกําหนดการสนองตอบผานทาง interface ตัวอื่น เชน ปุม


ใน tool bar ไดดังนั้นเราอาจเลือกที่จะกําหนดใหมี action สําหรับเหตุการณที่อาจเกิดขึน
้ ผาน
ทาง action object หลังจากนัน ้ ก็เพิ่ม action นี้เขาสู menu ตอไป

Action exitAction = new AbstractAction("Exit") {


public void actionPerformed(ActionEvent e) {
System.exit(0);
}
};

386
บทที่ 11: GUI และ Event Handling

และเราก็เปลีย
่ นการเพิ่ม component เขาสู menu ใหเปน

file.add(exitAction);

การทําแบบนีท ้ าํ ใหเราไมสามารถกําหนด hot key ใหกับ item ใน menu ได วิธีการที่จะทําให


เราสามารถใช hot key ไดก็คือ กําหนดตัวแปรที่ JMenuItem ดวย action แลวจึงเพิ่ม item ตัว
นี้เขาสู menu ตอไป เชน

intro. to Java (FEU.faa)


JMenuItem exitItem = new JMenuItem(exitAction);
exitItem.setMnemonic('x');
file.add(exitItem);

ทั้งสองวิธีเปนกระบวนการงาย ๆ ที่ทําให item ใน menu มีการสนองตอบตอเหตุการณที่เกิดขึ้น


จากผูใช ผูอานอาจเลือกใชวิธใี ดก็ไดที่เห็นวาเหมาะสมกับงานนั้น ๆ

11.7.2 Pop-UP Menu

Pop-up menu เปน menu ทีไ ่ มไดผูกติดกับ menu bar เชนที่เราทํากอนหนานี้ แตจะเปน
menu ที่โผลขน
ึ้ มาเมื่อผูใช activate

ภาพที่ 11.18 pop-up menu ใน text area

การสราง pop-up menu ก็คลายกับการสราง menu โดยทั่วไปจะตางกันตรงที่ pop-up menu


จะไมมี title เทานั้นเอง

JPopupMenu popup = new JPopupMenu();


JMenuItem item = new JMenuItem("Pop-Up One");
popup.add(item);
popup.add(exitAction);

เมื่อสราง pop-up menu เสร็จการทีจ ่ ะให component ใด ๆ มี pop-up menu ดวยนั้น เชน
จากโปรแกรมตัวอยาง เราตองการที่จะให pop-up menu เกิดขึ้นเมื่อผูใชกดปุมขวาบน mouse
ในพื้นที่ของ text area เราก็ใชคําสั่ง

area.setComponentPopupMenu(addPopup());

ในที่นี้เราสราง pop-up menu ใน method addPopup() ดังนั้นเราก็เพียงแตสง method ตัวนี้


ไปให setComponentPopupMenu()

public JPopupMenu addPopup() {


//code ที่เหมือนกับที่แสดงกอนหนานี้
return popup;
}

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

11.7.3 Accelerator ใน menu

ใน menu หลาย ๆ ตัวที่เราเคยเห็นหรือเคยใชมากอน เราจะเห็นวามีคําสั่งทีต


่ ามหลัง item ใน
menu ในรูปแบบของ combination key เชน CTRL+O หรือ CTRL+X เปนตน key เหลานี้เปน
accelerator key ที่ยอมใหเราใชโดยไมตองเปด menu (ไมเหมือนกับ key mnemonic ที่เรา
ตองเปด menu กอนถึงจะใชได)

intro. to Java (FEU.faa)


ภาพที่ 11.19 Accelerator ใน menu

การกําหนดใหมี accelerator key ใน menu ก็ทําไดงาย ๆ เราเพียงแตใชคาํ สั่ง

item.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_O, InputEvent.CTRL_MASK));

กับ menu item ที่เราตองการ แคนี้เราก็สามารถใช CTRL+O เพื่อเปด dialog window สําหรับ
การเลือกไฟลที่จะเปดโดยไมตองเปด menu

[ยกเวนการเลือก menu (disable menu item)]

ในบางครั้งเราไมตองการใหผใู ชเลือก menu item ทีย ่ ังไมสามารถทําไดจนกวากระบวนการใด


กระบวนการหนึง่ ไดยุติ (หรือเริม
่ ) กอน เชน จะทําการ save ไดก็ตอเมื่อไดเปดไฟลเรียบรอยแลว
วิธีการก็มีดังนี้

กําหนดให menu item ของการ save ใหเปน false ดวยคําสั่ง

saveItem.setEnabled(false);

และเพิ่ม code เพื่อกําหนดใหการ save เปนไปไดหลังจากการเปดไฟลแลวดวยคําสั่ง

saveItem.setEnabled(true);

ภาพที่ 11.20 disable menu item

388
บทที่ 11: GUI และ Event Handling

11.7.4 JToolBar

intro. to Java (FEU.faa)


JToolBar

ภาพที่ 11.21 JMenuBar

JToolBar เปน component อีกตัวหนึ่งที่เราสามารถนํามาใชเปนตัวเลือกทีด ่ ีอีกตัวหนึ่งใหกับผูใช


การสรางก็งาย ๆ เราเพียงแตกาํ หนด action ที่เกี่ยวของกับสิ่งที่เราตองการใหมีอยูใน tool bar
เชน open และ save ที่เราแสดงใหดูในภาพที่ 11.20

JToolBar bar = new JToolBar();


bar.add(openAction);
bar.add(saveAction);

เมื่อไดแลวเราก็นํา tool bar ไปใสไวใน container ของเรา

contentPane.add(bar, BorderLayout.PAGE_START);

สวน action ก็ทําคลาย ๆ กับที่เราไดทําในสวนของ exit ดังนี้

Action openAction = new AbstractAction("Open", openIcon) {


public void actionPerformed(ActionEvent e) {
//code for open file
}
};

Action saveAction = new AbstractAction("Save", saveIcon) {


public void actionPerformed(ActionEvent e) {
//code for save file
}
};

Tool bar ที่เราสรางขึ้นนีส


้ ามารถที่จะนําไปอยูในตําแหนงใดก็ได (drag-able)

ภาพที่ 11.22 การวาง tool bar ในที่ตาง ๆ

เพื่อใหผูใชรูวา ปุมหรือ icon นั้น ๆ ใชสําหรับทํางานอะไร เราก็สามารถใส tooltip ใหกับ


component เหลานั้นได เชน ถาเราใช action object ดังตัวอยางทีผ ่ านมาเราก็เรียกใช

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

openAction.putValue(Action.SHORT_DESCRIPTION, "Open");

แตถา เราใช component อื่น ๆ เราก็เรียกใช method setToolTipText() แทน เชน

saveButton.setToolTipText("Save");

intro. to Java (FEU.faa)


menu ที่เราใชเปน menu ที่ Java เรียกวา floatable menu ซึ่งเปน menu ที่ผใู ชสามารถเลือก
ตําแหนงในการวางได แตถาผูใ ชตองการทีจ
่ ะให menu อยูกับที่ก็ตองเรียกใช method
setFloatable() ดวยคา false เชน

toolbar.setFloatble(false);

ภาพที่ 11.23 non-floatable menu

Swing ยังมี component อีกหลายตัวที่เราสามารถนํามาใชใน application ตาง ๆ ที่เราตองการ


พัฒนาไดอีกมากมาย การจัดวาง component ที่มีความซับซอน และความหลากหลายของ
component ก็สามารถทําไดงา ย ๆ ดวย layout manager ตัวอื่น ๆ ที่ Java ไดจัดไวให อยางไร
ก็ตามหนังสือเลมนี้เปนการแนะนําการสราง GUI ในระดับเริ่มตนเทานั้น เราคงไมสามารถนําเอา
ขอมูลทั้งหมดที่เกี่ยวของกับการพัฒนาโปรแกรมดวย Swing มาแสดงใหผูอานไดทั้งหมด ทั้งนี้ก็
คงตองเปนหนาที่ของผูอานทีจ ่ ะหาขอมูลอื่น ๆ สําหรับการสรางโปรแกรมทีต
่ องการ GUI ที่ดู
เหมาะสมและสวยงามจากที่อน ื่ ๆ ตอไปดวยตัวเอง เชนการใช BoxLayout, GridbagLayout,
SpringLayout หรือ laytout อื่น ๆ ที่สรางขึ้นมาใชเอง

สําหรับสวนที่เหลืออยูของหนังสือเลมนี้เราจะแสดงถึงการสราง GUI ดวยการใช Netbeans 5.0


ซึ่งเปนโปรแกรมที่ไดกลาวไวในบททีห ่ นึ่งอยางคราว ๆ

11.8 Swing กับ Netbeans 5.0

การสราง application ที่ตองมีการจัดวาง component มาก ๆ จําเปนที่จะตองมีเครื่องมือทีท


่ ําให
การทํางานดังกลาวเปนไปอยางงาย และรวดเร็ว ในปจจุบันมีเครื่องมือหลายตัวทีช
่ วยได หนึ่งใน
นั้นก็คือ Netbeans 5.0

เราจะลองสรางโปรแกรมตัวอยางทีท
่ ําการเปลีย
่ นหนวยการวัดจากระบบ Metric ไปสูระบบ US
(หรือจาก US ไปเปน Metric) โดยเราจะกําหนดใหมีการใช component ดังนี้คือ

1. JTextField 2 ตัว
2. JRadioButton 3 ตัว (พรอมกับ ButtonGroup อีกหนึ่งตัว)
3. JScrollBar 2 ตัว
4. JComboBox 2 ตัว
5. JPanel 2 ตัว

โดยใหโปรแกรมมีหนาตาดังนี้

390
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.24 โปรแกรมตัวอยางที่สรางดวย Netbeans 5.0

เรามาลองดูขั้นตอนการสรางโปรแกรมตัวอยางดวย Netbeans 5.0 ดู

1. เลือก File Æ New Project ภายใต Categories เลือก General ภายใต Project เลือก
Java Application แลวจึงกดปุม  Next
2. ภายใต Project Name ใหใส conversion สําหรับ Project Location ก็ใหเลือกแฟมที่
เราตองการเก็บ project เชน D:\NetbeansProjects และภายใต Project Folder ก็ให
เปนไปตามคาที่มาจาก Netbeans เอง (D:\NetbeansProjects\conversion)
3. สวนที่เหลืออื่น ๆ ก็ใหเปนไปตามที่ Netbeans กําหนดหรือถาตองการกําหนด
package สําหรับ Main class ใหเปนอยางอืน ่ เชน ที่เราใชก็ใส
edu.fec.it.conversion.Main (เพราะวาเราเปนภาควิชา it ที่ fec.edu)
4. กดปุม Finish

องคประกอบของ Netbeans IDE

Palette

Inspector Design Area Properties Window

ภาพที่ 11.25 Netbeans Window

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

Design Area เปนพื้นที่สําหรับการสราง GUI โดยกําหนดใหปุม Source และ Design เปน


หนาตางที่เปน code และหนาตางที่มีการสรางแบบ visual สวนปุมอื่น ๆ ก็เปนปุมสําหรับการ
เลือก mode และการปรับแตงการจัดวาง

Inspector เปนหนาตางที่รวบรวมเอา component ทั้งหมดใน project มารวมไว โดยที่เรา


สามารถที่จะเลือก component ในการออกแบบผานทาง Inspector window ได

intro. to Java (FEU.faa)


Palette เปนที่รวม component ตาง ๆ ที่เราสามารถนํามาใชในโปรแกรมของเราไดดวยการ
ลากแปะ

Properties Window เปนที่แสดง property ของ component ตาง ๆ ในโปรแกรม และเปนที่


ที่เราสามารถเปลี่ยน หรือกําหนดคาตาง ๆ ของ component

หลังจากกดปุม Finish แลวเราก็จะเห็นไฟล Main อยูภายใน IDE ทางดานขวาของ Project tab


ขั้นตอนตอไปทีเ่ ราตองทําก็คือสราง Java ไฟลอีกตัวหนึ่งชื่อ ConversionForm ซึ่งจะเปนไฟล
หลักของเรา โดยเราจะกําหนดใหการออกแบบไฟลตัวนี้เปนการออกแบบดวย Visual
component

1. ภายใต Project tab และ node: ที่เราสรางขึ้น เชน edu.fec.it.conversion ใหกดปุม


ขวาบน mouse เลือก New Æ JFrame Form …
2. ภายใต Class Name ใหใส ConversionForm อยาใหตัวอักษรตัวแรกเปนตัวเล็ก ทั้งนี้
เพราะวา Java ชอบใชตัวอักษรตัวใหญขึ้นตนชื่อ class
3. สวนอื่น ๆ ใหเปนไปตามที่ Netbeans กําหนด กดปุม Finish เราก็จะเห็นพื้นที่สีเทาที่
พรอมใหเราสราง GUI ที่เราตองการตอไป

ตอไปก็เปนขั้นตอนการสราง Form ที่เราตองการ ซึ่งมีลําดับดังนี้

1. จาก Component Palette ทางดานขวาของ IDE ลาก JPanel เขาสู Form โดยลากไป
ทางดานซายบนจนกวาจะเห็นเสนประสองเสน ทางดานบนและทางซาย ซึง่ เสนประทั้ง
สองเปนตัวชวยในการวาง component (ที่ GroupLayout manager2 ใช) กําหนดให
ขนาดของ JPanel มีขนาดประมาณครึ่งหนึ่งของ Form หลังจากนั้นก็สราง JPanel อีก
หนึ่งตัววางไวทางขวาของตัวแรก
2. เพื่อใหทั้งสอง JPanel มีขนาดที่เทากับเราก็เลือก JPanel ตัวหนึ่ง (เชนตัวทางซาย)
กดปุม Shift แลวจึงเลือก JPanel ทางขวา Æ กดปุมขวาบน mouse เลือก Same Size
แลวจึงเลือก Same Width
3. ถึงแมวาเราไดสราง JPanel 2 ตัวแลวแตเราก็มองไมเห็น นอกเสียจากวาเราจะนํา
mouse ไปวางบน JPanel ทั้งสอง (JPanel เปน container ที่ใชจัดเก็บ component
อีกทีหนึ่ง) และเพื่อใหมองดูแลวสวยงามเราก็จะสราง border ใหกับ JPanel ทั้งสอง
4. เลือก JPanel ตัวใดตัวหนึ่ง (เราไมไดเปลี่ยนแปลงสิ่งที่ Netbeans กําหนดดังนั้นชื่อ
ของ component ก็จะเปน jPanel1 และ jPanel2) เมื่อเราเลือก jPanel1 เราก็จะเห็น
Properties windows ทางดานขวาลางของ Netbeans (ถัดจาก Palette) ภายใต
border properties กดปุม … เลือก TitledBorder ภายใต Title ใส Metric System ใน
สวนของ Border กดปุม … เลือก LineBorder พรอมกับ check ชอง Rounded
Corners กดปุม OK 2 ครั้ง
5. ทําอยางเดียวกันนี้กับ jPanel2 ดวยชื่อ border เปน US System

2
Netbeans 5.0 โดย Matisse ใช GroupLayout manager เปนตัวจัดวาง component

392
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.26 JPanel ที่เราสรางขึ้นจาก Netbeans

จากภาพที่ 11.26 ผูอานจะเห็นวามีสญ


ั ลักษณที่คลาย ๆ กับตัว H บน JPanel ทั้งสอง ซึ่ง
เปนตัวบงบอกถึงความมีขนาดที่เทากันของทั้งสอง component

ลําดับตอไปก็เปนการใส component ตัวอื่น ๆ เชน JScrollBar, JTextField, และ


JComboBox

1. เลือก JTextField จาก Palete วางไวใน jPanel1 ตามดวย JScrollBar และ


JComboBox จัดขนาดใหเหมาะสมกับพื้นที่
2. ทําอยางเดียวกันกับ jPanel2
3. ใน JTextField ทังสองตัวเปลีย ่ นคา text ใน Properties ใหเปน 0 เลือก Font และสี
ของ text ตามใจชอบ
4. ในการจัดวาง JScrollBar ครั้งแรกนั้น Netbeans จะกําหนดใหเปนไปในแนวตั้ง เราก็
เปลี่ยนใหเปนแนวนอนใน Properties ดวยการเลือก HORIZONTAL ใน orientation
พรอมกับกําหนดใหคา maximum เปน 10000 และคา minimum เปน 0 สวน
unitIncrement นั้นเราก็ใหเปนคา default คือ 1 (เทากันทั้งสอง scroll bar)
5. สวนเสนแนวนอนที่เห็นก็เปน JSeparator ที่ไดจัดวางไวเฉย ๆ
6. ตอไปก็ลาก JRadioButton มาวางไวสามตัว พรอมกับเปลี่ยนคา text ใหเปน
Distance, Weight และ Temperature ตามลําดับโดยกําหนดให Distance มีการ
check ไวกอนลวงหนาดวยการกําหนดคาใน Properties: selected
7. และเพื่อใหการกดปุมใหเปนการเลือกปุมใดปุม หนึ่งเทานั้น เราตองลาก ButtonGroup
จาก Palete มาวางไวใน Form ที่ใดที่หนึ่ง ซึ่งถาเราดูใน Inspector window ทาง
ดานซายลางของ IDE เราก็จะเห็นวา ButtonGroup อยูภายใต node: Other
Components หลังจากนั้นเราก็เลือก jRadioButton1 ภายใน Proiperties window
กําหนดคา buttonGroup ใหเปน buttonGroup1 ทําอยางเดียวกันนี้กับ
jRadioButton2 และ jRadioButton3

กระบวนการที่เราทําก็จะทําใหเราไดหนาตางในรูปแบบของ GUI ทีส


่ วยงามพอสมควร สําหรับ
ลูกเลนอื่น ๆ ที่ตองการก็สามารถทําไดใน Properties window

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

TitledBorder JTextField JScrollBar

intro. to Java (FEU.faa)


JRadioButton ใน ButtonGroup JSeparator JComboBox

ภาพที่ 11.27 component ตาง ๆ ในโปรแกรมตัวอยาง

ขั้นตอนตอไปทีเ่ ราตองทําก็คือการกําหนดการสนองตอบตอเหตุการณทผ ี่ ูใชเลือก แตกอนที่เรา


จะไปถึงตรงนั้นเราตองกําหนดคาตาง ๆ ที่มีอยูใน JComboBox ทั้งสามตัวกอน เราอาจที่จะเลือก
ทําใน Properties window ก็ไดซึ่งโดยการกําหนดของ Netbeans แลวคาเหลานี้จะเปน item1
– item4 เราจะลบคาเหลานี้ทงิ้ ไป (ดวยการกดปุม … ภายใต Properties window ของ
jComboBox1 (2) ในสวนของ model เราก็จะได pop-up window ที่แสดงคา item1 – item4
เลือก item ดังกลาวแลวก็กดปุม Remove) แตจะไปกําหนดใน constructor แทนดัง code ที่
เห็นนี้

public ConversionForm() {
initComponents();
allocateSpace();
distanceInit();
}

private void allocateSpace() {


mdUnit = new Unit[3];
mwUnit = new Unit[3];
udUnit = new Unit[4];
uwUnit = new Unit[3];
}

private void distanceInit() {


mdUnit[0] = new Unit("Centimeters", 0.01);
mdUnit[1] = new Unit("Meters", 1.0);
mdUnit[2] = new Unit("Kilometers", 1000.0);
for(int i = 0; i < mdUnit.length; i++)
jComboBox1.addItem(mdUnit[i].getDescription());

udUnit[0] = new Unit("Inches", 0.0254);


udUnit[1] = new Unit("Feet", 0.305);
udUnit[2] = new Unit("Yards", 0.914);
udUnit[3] = new Unit("Miles", 1613.0);
for(int i = 0; i < udUnit.length; i++)
jComboBox2.addItem(udUnit[i].getDescription());
}

Constructor: Conversion() เปนสวนที่ Netbeans ไดสรางขึ้นพรอมทั้ง method


initComponents() ซึ่งเราไมควรทีจ ่ ะไปแตะตอง สวน method allocateSpace() และ
distanceInit() นั้นเราสรางขึ้นมาเอง

394
บทที่ 11: GUI และ Event Handling

เราไดกําหนดใหมี class Unit เปน class ที่เราจะใชใหเปนหนวยของการวัดสองตัวคือ Distance


และ Weight สวน Temperature นั้นเราจะกําหนดตางหาก การสราง class Unit ขึ้นมาใชทําให
การเขียน code รองรับการเปลีย ่ นหนวยทีผ
่ ูใชเลือกทําไดงายขึ้น (เราจะมาดูกันตอไป) ผูอานจะ
เห็นวาเราเรียก method allocateSpace() และ method distanceInit() หลังจากการเรียก
method initComponents() ทั้งนี้ก็เพราะวา component ตาง ๆ ที่เราลากไปแปะไวใน form จะ
อยูใน method นี้ (ดู code ดวยการกด Source tab ใน IDE)

intro. to Java (FEU.faa)


ภายใน method allocateSpace() เราสราง object จาก class Unit 4 ตัวคือ mdUnit, mwUnit,
udUnit และ uwUnit โดยที่ตวั แรกเปนตัวเก็บ ระยะทางในหนวย Metric ตัวทีส
่ องเก็บ น้ําหนักใน
หนวย Metrics สวนสองตัวสุดทายเปนการเก็บ ระยะทางและน้ําหนักในหนวย US ตามลําดับ

ภายใน class Unit เรากําหนดใหมีองคประกอบดังนี้ (สรางใน package เดียวกับ


ConversionForm.java ดูภาพที่ 11.28 ประกอบ)

เราสรางเอง

ภาพที่ 11.28 ไฟลตาง ๆ ที่อยูใน package: edu.fec.it.conversion

public class Unit {


private String description;
private double multiplier;

/** Creates a new instance of Unit */


public Unit() {
}

public Unit(String description, double multiplier) {


this.description = description;
this.multiplier = multiplier;
}

public String getDescription() {


return description;
}

public double getMultiplier() {


return multiplier;
}
}

description เปนตัวแสดงการวัด เชน Kilograms สวน multiplier เปนตัวคูณของการเปลี่ยน


หนวย จากหนวยหนึ่งไปยังอีกหนวยหนึ่ง method getDescription() สงคา description กลับ
สวน method getMultiplier() ก็สงคาตัวคูณกลับ

ภายใน method distanceInit() เราก็กําหนดคาตาง ๆ ที่ตองการใหกับทั้ง mdUnit และ


mwUnit พรอมทั้งนําขอมูลเหลานี้เขาสู jComboBox1 (Metric) และ jComboBox2 (US)

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

สําหรับขอมูลใน jComboBox1 และ jComboBox2 ของ jRadioButton2 นั้นเราก็กําหนดจาก


method weightInit() ซึ่งมีการกําหนดดังนี้

private void weightInit() {


mwUnit[0] = new Unit("Grams", 0.001);
mwUnit[1] = new Unit("Kilograms", 1.0);
mwUnit[2] = new Unit("Tons", 1000.0);
for(int i = 0; i < mwUnit.length; i++)

intro. to Java (FEU.faa)


jComboBox1.addItem(mwUnit[i].getDescription());

uwUnit[0] = new Unit("Ounces", 0.02834949);


uwUnit[1] = new Unit("Pounds", 0.45359291);
uwUnit[2] = new Unit("Short Tons", 907.19404881);
for(int i = 0; i < uwUnit.length; i++)
jComboBox2.addItem(uwUnit[i].getDescription());
}

ในครั้งแรกที่โปรแกรมทํางานนัน้ ขอมูลจะมีอยูใ น jComboBox1 และ jComboBox2 ที่เปนหนวย


ของการวัดระยะทางเทานั้น เพราะเรากําหนดไวใน constructor แตถาผูใชเลือก jRadioButton2
หรือ jRadioButton3 เราก็จะกําหนดคาเหลานี้ใหดวยการใส code สําหรับการสนองตอบตอการ
กระทําดังกลาวเขาสู jComboBox2 และ jComboBox3 ตอไปดังนี้

เลือก jRadioButton2 (Weight) Æ กดปุมขวา เลือก Events Æ Action Æ actionPerformed


เราก็จะได method:

private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) {


jComboBox1.removeAllItems();
jComboBox2.removeAllItems();
weightInit();
}

ซึ่งจะเปน method ที่ไมมีอะไรอยูเลย เราตองใส code เองซึ่งเราก็จะตองทําการกําหนดคา


ใหกับ jComboBox ตัวนี้โดยขัน ้ ตอนแรกเราก็ลบขอมูลเดิมออกกอนดวยการเรียก method
removeAllItems() หลังจากนั้นเราก็ใสขอมูลใหมเขาไปดวยการเรียกใช method weightInit()
และในสวนของการเลือก jRadioButton3 เราก็เขียน code รองรับดังนี้

private void jRadioButton3ActionPerformed(java.awt.event.ActionEvent evt) {


jComboBox1.removeAllItems();
jComboBox2.removeAllItems();
jComboBox1.addItem("Celsius");
jComboBox2.addItem("Fahrenheit");
}

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


โปรแกรมหนึ่งสวน คือผานทาง constructor และผานทางเงื่อนไขจากผูใชอีกสองสวนคือ เมื่อ
เลือก Weight และเมื่อเลือก Temperature

สําหรับการสนองตอบตอการเลือก jRadioButton1 นั้นเราก็ทําคลาย ๆ กันคือ ลบขอมูลเกาออก


นําขอมูลใหมเขาใสแทน

private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {


jComboBox1.removeAllItems();
jComboBox2.removeAllItems();
distanceInit();
}

396
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.29 แสดงขอมูลเมื่อผูใชเลือก jRadioButton2 (Weight)

ขั้นตอนตอไปก็คือการสนองตอบตอเหตุการณใน JTextField และ JScrollBar โดยในขั้นแรกนี้


เราจะเริ่มจาก JTextField กอน

เริ่มตนดวยการเลือก jTextField1 กดปุมขวาบน mouse เลือก Events Æ Action Æ


actionPerformed เราก็จะได method actionPerformed() ที่ไมมี code และเราตองใส code
ดังนี้

private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {


value = Double.parseDouble(jTextField1.getText());
jScrollBar1.setValue((int)value);
actionMtoU(value);
}

เราดึงเอาขอมูลออกจาก jTextField1 แลวนําไปเก็บไวใน value (เราประกาศใหเปน class


variable) หลังจากนั้นเราก็กําหนดใหคาใน jScrollBar1 เปนคาเดียวกันกับคาที่เขามาจาก
keyboard ดวยการเรียกใช method setValue() แตเนื่องจากวาเราสามารถกําหนดคาจาก int
เทานั้นเราจึงตองเปลี่ยนคา value จากเดิมทีเ่ ปน double ใหเปน int

เราตองไมลืมวาผูใชตองกดปุม
 Enter กอนการสนองตอบเหลานี้จึงเกิดขึ้น สวนสําคัญที่เราตอง
ทําตอไปก็คอื เปลี่ยนขอมูลทีผ
่ ูใชใสเขามาใหเปนอีกหนวยหนึ่งใน jTextField2 โดยกําหนดให
เรียก method actionMtoU()

private void actionMtoU(double value) {


char type;
if(jRadioButton1.isSelected())
type = 'D';
else if(jRadioButton2.isSelected())
type = 'W';
else
type = 'T';
int index1 = jComboBox1.getSelectedIndex();
int index2 = jComboBox2.getSelectedIndex();
result = convertMtoU(value, type, index1, index2);
jTextField2.setText(String.valueOf(result));
}

เราตองตรวจสอบใหดีวาคาใน component ตาง ๆ สัมพันธกัน ทั้งนี้ก็เพื่อใหการเปลี่ยนหนวย


ถูกตองกับการวัดทีผ
่ ูใชเลือก เชน จาก Centimeters Æ {Inches, Feet, Yards, Miles} โดยเรา
กําหนดให 'D' เปนระยะทาง 'W' เปนน้ําหนัก และ 'T' เปนอุณหภูมิ การเปลี่ยนหนวยเกิดขึ้นใน
method convertMtoU() ซึ่งเปน method ทีใ่ ชตวั แปร value, type, index1 และ index2 เปน
parameter ซึ่งมีการทํางานดังนี้

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

private double convertMtoU(double value, char type, int i, int j) {


double multiplier = 0;
double result = 0;
switch(type) {
case 'D': multiplier = mdUnit[i].getMultiplier() /
udUnit[j].getMultiplier();
result = value * multiplier;
break;

intro. to Java (FEU.faa)


case 'W': multiplier = mwUnit[i].getMultiplier() /
uwUnit[j].getMultiplier();
result = value * multiplier;
break;
case 'T': result = value * 9 / 5 + 32;
break;
}
return result;
}

คาที่ไดจากคํานวณใน method convertMtoU() ก็จะถูกนําไปแสดงใน jTextField2 ตอไป

การสนองตอบเมื่อผูใชใสขอมูลใน jTextField2 ก็คลาย ๆ กับที่เราทําใน jTextField1 ตางกัน


ตรงที่เราเรียกใช method actionUtoM() และ method convertUtoM() แทนซึ่งมี code ดังนี้

private void actionUtoM(double value) {


char type;
if(jRadioButton1.isSelected())
type = 'D';
else if(jRadioButton2.isSelected())
type = 'W';
else
type = 'T';
int index1 = jComboBox1.getSelectedIndex();
int index2 = jComboBox2.getSelectedIndex();
result = convertUtoM(value, type, index2, index1);
jTextField1.setText(String.valueOf(result));
}

private double convertUtoM(double value, char type, int i, int j) {


double multiplier = 0;
double result = 0;
switch(type) {
case 'D': multiplier = udUnit[i].getMultiplier() /
mdUnit[j].getMultiplier();
result = value * multiplier;
break;
case 'W': multiplier = uwUnit[i].getMultiplier() /
mwUnit[j].getMultiplier();
result = value * multiplier;
break;
case 'T': result = (value - 32) * 5 / 9;
break;
}
return result;
}

สวนสุดทายที่เราตองทําก็คือการเขียน code รองรับถาผูใชเลือกเปลี่ยนขอมูลใน JScrollBar


(ดวยการเลื่อนไปมา)

ขั้นตอนก็คือ เลือก Events Æ Adjustment Æ adjustmentValueChanged บน jScrollBar1


เราก็จะได method (เชนเดียวกันกับ code ทีเ่ กิดขึ้นโดยอัตโนมัติในการดักฟงเหตุการณอื่น ๆ –
นั่นก็คือไมมี code! - ตองเขียนเอง)

private void jScrollBar1AdjustmentValueChanged(


java.awt.event.AdjustmentEvent evt) {
double scbValue = (double)jScrollBar1.getValue();
jTextField1.setText("" + scbValue);
actionMtoU(scbValue);
}

398
บทที่ 11: GUI และ Event Handling

Code ที่อยูภ
 ายในก็ไมมีอะไรซับซอนเราแคดึงคาออกจาก jScrollBar1 สงออกไปยัง
jTextField1 เรียกใช method actionMtoU() ดวยคาใน jScrollBar1 นี้คาในสวนอื่น ๆ ที่มีการ
ดักจับเหตุการณก็จะทําหนาทีใ่ นการเปลี่ยนคาตามที่ตัวเองรับผิดชอบ

intro. to Java (FEU.faa)


ภาพที่ 11.30 คาที่เปลี่ยนไปใน jTextField1 และ jTextField2 ถาผูใชเลื่อนปุมใน jScrollBar1

เชนเดียวกันการเขียน code รองรับการเปลี่ยนของ jScrollBar2 ก็คลายกันเราเพียงแตเปลี่ยน


code ใหเปน

private void jScrollBar2AdjustmentValueChanged(


java.awt.event.AdjustmentEvent evt) {
double scbValue = (double)jScrollBar2.getValue();
jTextField2.setText("" + scbValue);
actionUtoM(scbValue);
}

หนาตาของโปรแกรมทีผ ่ ูอานไดอาจไมคอยเหมือนกับที่เห็นนี้ ทั้งนี้ก็อาจขึน


้ อยูกับ font ที่ใช
ขนาดหรือการเปลี่ยนแปลงอื่น ๆ ใน Properties ของ component นั้น ๆ ผูอ  านสามารถทดสอบ
ดวยการเปลี่ยน properties เหลานี้เอง

ณ จุดนี้ผูอานอาจจะสงสัยวาทําไมไมมีอะไรแสดงเลยหลังจากที่ compile และ run โปรแกรม


ยังไมจบครับเรายังตองไปเขียน code อีกสักสองหรือสามบรรทัดภายใน Main ที่ Netbeans
สรางขึ้น ดังนี้

public static void main(String[] args) {


//sets Java's LAF
JFrame.setDefaultLookAndFeelDecorated(true);

ConversionForm cfr = new ConversionForm();


cfr.setVisible(true);
}

การ compile และ run โปรแกรมทีท


่ ําไดงา ย ๆ ก็คือ กดปุมใน tool bar

Build Main Project Run Main Project

Clean & Build Main Project

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

ในสวนของตัวแปร (global variables) ที่เราใชมีดังนี้ (ประกาศตามหลังตัวแปรที่ Netbeans


สรางขึ้นใน ConversionForm.java)

private Unit[] mdUnit;


private Unit[] udUnit;
private Unit[] mwUnit;
private Unit[] uwUnit;
private double value;

intro. to Java (FEU.faa)


private double result;

โปรแกรมตัวอยางที่เราแสดงใหดูเปนตัวอยางงาย ๆ ที่แสดงใหเห็นถึงความสามารถของ
Netbeans ในการสราง GUI ทีม ่ ีหนาตาดูแลวสวยงาม ใชเวลานอยในการสราง (ไมเหมือนกับ
การสรางดวยการเขียน code เชนทีท่ ํากอนหนานี้) ผูอานสามารถลดเวลาในการพัฒนาโปรแกรม
ที่มีความซับซอนลงไดอยางมาก ทําใหมีเวลาในการพัฒนาสวนที่เปน logic ของโปรแกรมมาก
ขึ้น

[Run โปรแกรมภายนอก Netbeans IDE]

ถึงตอนนี้เรา run โปรแกรมไดเพียงแคผานทาง IDE ของ Netbeans เทานั้น ซึ่งอาจไม


สนองตอบตอความตองการของบางคนไดดีพอ ดังนั้นเพื่อใหโปรแกรมที่เราสรางขึ้นสามารถ run
ในที่อื่น ๆ ไดเราก็เพียงแคหาไฟลสําคัญ ๆ ที่ Netbeans สรางขึ้นโดยมีขั้นตอนดังนี้

1. หา folder: dist ใน path ที่เราสราง project ของเรา


2. run โปรแกรมดวยคําสั่ง java –jar conversion.jar

ถาเราตองการที่จะ run ไฟลของเราในที่อื่น เราก็ตองเอาไฟลทุกตัวใน folder นี้ไป วิธีการที่งา ย


ที่สด
ุ ก็คือการบีบอัด (zip) เพื่อนําไปขยายในที่อื่นที่เราตองการ run โปรแกรม ภาพที่ 11.31
แสดงไฟลทุกตัวใน project conversion ที่สรางขึ้นโดย Netbeans ผูอานจะเห็นไฟล
conversion.jar ไฟล README.TXT และไฟล swing-layout-1.0.jar ซึ่งเปนไฟลที่ Netbeans
ใชในการจัดวาง component ใหเราเพราะฉะนั้นเราตองเอาไฟลตวั นี้ไปดวย

ภาพที่ 11.31 dist folder และไฟลที่อยูภายใน (มองผาน Files tab ของ Netbeans)

ขั้นตอนสั้น ๆ เพียงเทานี้กท
็ ําใหเราสามารถที่จะ run โปรแกรมที่เราสรางขึ้นจาก Netbeans ใน
ที่อื่น ๆ ที่ไมใชภายใน IDE ได

ถาผูอานอยากรูว าไฟลที่มีอยูใ น conversion.jar มีอะไรบางก็ใชคําสั่ง jar xvf conversion.jar


เพื่อทําการแตกไฟลออก

400
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.32 การขยาย (extract) ไฟล conversion.jar

เราคงไมพูดถึงกระบวนการสราง jar ไฟลในที่นี้ ผูอานสามารถหาดูไดจากหนังสือหรือ web site


ตาง ๆ ไดอยางงาย ๆ ภาพดานบนนี้แสดงถึงไฟลตาง ๆ ที่จาํ เปนในการ run project ที่เราสราง
ขึ้น โดย Netbeans จะทําการบีบอัดไฟลเหลานี้ใหเราเองเมือ ่ เราทําการ build project ภายในตัว
IDE เองที่นาสนใจอีกอันหนึ่งก็คือขอมูลที่มีอยูในไฟล MANIFEST.MF

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: 1.5.0_06-b05 (Sun Microsystems Inc.)
Main-Class: edu.fec.it.conversion.Main
Class-Path: lib/swing-layout-1.0.jar
X-COMMENT: Main-Class will be added automatically by build

เราจะเห็นไฟล Main และ swing-layout-1.0.jar ซึ่งเปนไฟลสําคัญทั้งสองตัวโดยตัวแรกจะเปน


จุดเริ่มตน (entry point) ในการ run และตัวที่สองเปน layout ที่ถูกใชโดย Netbeans ในการจัด
วาง component ตาง ๆ ใหเรา

[เปลี่ยน Main.class ใหเปนชื่อที่เราตองการ]

ในตอนแรกที่เราสราง project: conversion นั้นเราใชชื่อที่ Netbeans กําหนดใหเราคือ Main


แตถา เราไมตองการใชชื่อนี้เราก็สามารถเปลีย่ นใหเปนอยางอื่นตามที่เราตองการได วิธีการก็งาย
ๆ คือ เปลี่ยนชือ่ จาก Main ใหเปนชื่อที่เราตองการตอนเริ่มตน project

เปลี่ยนชื่อจาก Main ให


เปนชื่อที่เราตองการ เชน
SomeClass ดังที่เห็นนี้

ภาพที่ 11.33 เปลี่ยนชื่อ class จาก Main ใหเปนชื่อที่เราตองการ

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

[ใสลูกเลนใหกับโปรแกรม conversion]

เราอยากทีจ
่ ะใหโปรแกรม conversion ของเรามีการแสดงเวลาขณะที่โปรแกรมกําลัง run อยู
ทางดานขวาลางของ form เชนที่เห็นจากภาพนี้

intro. to Java (FEU.faa)


ภาพที่ 11.34 โปรแกรม conversion ที่มีการแสดงเวลา

กระบวนการในการทําก็ไมยาก เราเพียงแตเพิม ่ class TimeOfDay เขาสู class


ConversionForm ดังนี้ (ใหเปน inner class)

class TimeOfDay implements ActionListener {


public void actionPerformed(ActionEvent e) {
SimpleDateFormat tf = new SimpleDateFormat("hh:mm:ss", getLocale());
String st = tf.format(new Date());
jLabel1.setText(st);
}
}

ภายใน constructor ของ ConversionForm เราก็เพิ่ม code

time = new TimeOfDay();


timer = new Timer(1000, time);
timer.start();

โดยเราจะใช jLabel1 เปนตัวแสดงเวลาที่เปลีย


่ นไปทุก ๆ หนึง่ วินาที ผานทาง class Timer ที่
ถูกเรียกใน constructor ผานทาง method start()

[เปลี่ยนโปรแกรม conversion ใหพด


ู ภาษาไทย]

การกําหนดใหขอความที่อยูในโปรแกรมใหใชภาษาไทยเปนภาษาในการสือ ่ ถึงผูใชนั้น ทําไดงาย


ๆ ดวยการกําหนด properties ไฟลใหกับตัวโปรแกรมของเรา เพื่อใหเห็นถึงตัวอยางเราจะ
เปลี่ยนขอความที่แสดงในหนาตางหลัก ใหเปน

ภาพที่ 11.35 เปลี่ยนขอความในหนาตางใหใชภาษาไทย

ขั้นตอนแรกที่เราตองทําก็คือ สราง properties ไฟล

1. เลือก File Æ New File


2. เลือก Properties File จาก Other category เสร็จแลวกดปุม Next

402
บทที่ 11: GUI และ Event Handling

3. กําหนดให properties ไฟลมช


ี ื่อเปน Bundle โดยกําหนดใหอยูภายใต path ที่เราใช
ในโปรแกรมของเรา (ใช Browse ในการหา)

intro. to Java (FEU.faa)


4. กดปุม OK

การกําหนดใหมีภาษาไทยใชในโปรแกรมนั้นเราจะเริ่มตนดวยการเลือก component ที่เรา


ตองการแสดงใน form หลักของเรา (ConversionForm)

1. เลือก Distance ในพื้นที่ของการออกแบบ


2. ในหนาตาง Properties กดปุม … ของ Text property เราก็จะไดหนาตาง pop-up
ขึ้นมา
3. เลือก Resource Bundle ใน combo box: Select Mode ซึ่งจะทําให editor
เปลี่ยนเปน resource bundle
4. ภายใน Bundle Name field ใหหาไฟล Bundle.properties

5. ใสคา Distance ใน fields: Key และ Value

6. กดปุม OK
7. ทําแบบเดียวกันนี้กับ Weight และ Temperature

[กําหนด Locale ใหม (ไทย)]

1. กดปุมขวาบน mouse ของ node: Bundle.properties ในหนาตาง Projects


2. เลือก Add Locale
3. ใส th ใน Language code (หรือเลือกจาก list)
4. ใส TH ใน Country Code
5. กดปุม OK เราก็จะได locale ใหมภายใน Bundle.properties

6. ภายในหนาตาง Projects กดปุมขวาบน Bundle.properties เลือก Open


7. เปลี่ยนคาในหนาตางใหเปนไปตามที่เราตองการ

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

intro. to Java (FEU.faa)


[ทดสอบ locale ใหมที่เรากําหนดขึ้น]

1. ในหนาตาง Projects กดปุมขวาบน conversion เลือก Properties


2. ภายใต Categories เลือก Run
3. ใส –Duser.language=th –Duser.country=TH ในชอง VM Options
4. กดปุม OK
5. กดปุมขวาบน project: conversion เลือก Run

เราก็จะไดหนาตางที่มภ
ี าษาไทยใน JRadioButton แทนภาษาอังกฤษดังทีไ
่ ดแสดงใหดูกอ
 น
หนานี้

ผูอานตองอยาลืมกําหนดใหภาษาที่แสดงใน Windows เปนภาษาไทย (ทําผาน control panel)


กอนจึงจะทําใหโปรแกรมของเราทํางานในแบบภาษาไทยได (กําหนดใหภาษาไทยเปน default
locale)

[กําหนดการใชภาษาไทยในขณะที่โปรแกรมกําลัง execute]

ตัวอยางการใชภาษาไทยในตัวอยางกอนหนานี้เปนการทําในชวงเวลาของการออกแบบ (เขียน)
โปรแกรม ซึ่งสวนใหญจะเปนการเปลี่ยนคาของ string ที่แสดงขอความสื่อถึงผูใ ชในรูปแบบตาง
ๆ ในบางครั้งเราตองการกําหนดคาของ string เหลานี้ ณ เวลาที่โปรแกรมกําลัง execute อยู
เชน กําหนดคาของขอมูลใน combo box ดังที่เราทําในโปรแกรม conversion

โปรแกรมตัวอยางที่เราจะแสดงใหดูเปนการกําหนดการใชภาษาดวยการใช resource bundle


จาก locale ที่กาํ หนดจากโปรแกรม ดังที่แสดงใหดูในภาพที่ 11.36 นี้

ภาพที่ 11.36 หนาตางเริ่มตนเมื่อ run โปรแกรม

โปรแกรมตัวอยางนี้จะกําหนดใหมีขอมูลใน combo box หลังจากทีผ ่ ูใชไดกดปุม Thai โดยจะ


กําหนดใหเปนเดือนในภาษาไทยจาก มกราคมไปจนถึงธันวาคม พรอมกับเปลี่ยนขอความบนปุม
ใหเปน English ซึ่งเมื่อผูใชกดปุมอีกครั้งขอมูลใน combo box ก็จะเปลี่ยนเปนเดือน January
ไปจนถึงเดือน December และทกครั้งทีม ่ ีการกดปุม หรือเลือกขอมูลใน combo box เราก็จะ
แสดงขอมูลนั้นใน text area ดานลาง

404
บทที่ 11: GUI และ Event Handling

intro. to Java (FEU.faa)


ภาพที่ 11.37 ผลลัพธบางสวนหลังจากผูใชกดปุม (และเลือกเดือน)

ขั้นตอนการสรางก็คลายกับที่เราไดทํามากอนหนานี้ ตางกันตรงที่วา เราตองสราง properties file


ใน Source Packages และเรียกใช Resource Bundle ภายในโปรแกรม ภาพที่ 11.38 แสดง
ตําแหนงของ properties file (ตําแหนงจะตางกับโปรแกรม conversion)

ภาพที่ 11.38 ตําแหนงของ properties file ที่เราตองสราง

โดยเราจะกําหนดใหมี locale สองตัวคือ en_US และ th_TH ซึ่งมีขอมูลดังนี้

การกําหนดการสนองตอบตอ event ที่เกิดขึ้นทําสองที่ คือ เมื่อปุมถูกกด และเมื่อ combo box


ถูกเลือก ซึ่งมี code ดังนี้

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

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {


String caption = jButton1.getText();
if(caption.equalsIgnoreCase("Thai")) {
jButton1.setText("English");
ResourceBundle f = ResourceBundle.getBundle("Months", thLocale);
populateComboBox(f);
}
else {

intro. to Java (FEU.faa)


jButton1.setText("Thai");
ResourceBundle f = ResourceBundle.getBundle("Months", usLocale);
populateComboBox(f);
}
}

private void populateComboBox(ResourceBundle f) {


jComboBox1.removeAllItems();
jComboBox1.addItem(f.getString("Jan"));
jComboBox1.addItem(f.getString("Feb"));
jComboBox1.addItem(f.getString("Mar"));
jComboBox1.addItem(f.getString("Apr"));
jComboBox1.addItem(f.getString("May"));
jComboBox1.addItem(f.getString("Jun"));
jComboBox1.addItem(f.getString("Jul"));
jComboBox1.addItem(f.getString("Aug"));
jComboBox1.addItem(f.getString("Sep"));
jComboBox1.addItem(f.getString("Oct"));
jComboBox1.addItem(f.getString("Nov"));
jComboBox1.addItem(f.getString("Dec"));
}

private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {


String month = (String) jComboBox1.getSelectedItem();
if(month != null)
jTextArea1.append(month + "\n");
}

ทุกครั้งทีผ
่ ูใชกดปุม (ซึ่งอาจเปน "Thai" หรือ "English") เราก็จะดึงเอาคาที่อยูบนปุมออกเพื่อ
ตรวจสอบวาเราตองกําหนดขอมูลใน combo box วาควรเปนภาษาไทยหรือภาษาอังกฤษ และ
เมื่อไดแลวเราก็เรียกใช properties ที่เราเขียนขึ้นผานทาง resource bundle เชน

ResourceBundle f = ResourceBundle.getBundle("Months", usLocale);

Java ก็จะไปคนหา properties ไฟลที่ขึ้นตนดวย Months ตามดวย locale: th_TH หรือ en_US
ที่เราไดกําหนดไวจากการเรียก หลังจากนั้น (ถาหาไฟลเจอ) เราก็ทําการกําหนดคาใหกับ
combo box ดวย

jComboBox1.removeAllItems();
jComboBox1.addItem(f.getString("Jan"));

โดยเราเริม
่ ตนดวยการลบคาเกาที่อยูออกกอน หลังจากนั้นจึงทําการใสขอมูลที่ดึงออกมาจาก
properties ไฟลตามคาของ key ผานทาง method getString() ของ resource bundle

เนื่องจากวาเราทําการกําหนดคาของขอมูลใน combo box จากการกดปุมซึ่งการกระทําดังกลาว


จะทําใหเกิด event ขึ้นใน combo box ดังนัน ้ ขอมูลก็จะถูกสงไปยัง text area ดวย เพราะฉะนัน

ทุกครั้งของการกดปุมเราก็จะเห็นเดือนที่อยูด
 า นบนสุดของ combo box แสดงออกทาง text
area (January หรือ มกราคม) เสมอ

[การใช Internationalization Wizard]

เราจะใชโปรแกรมการสราง GUI ตัวอยางที่มากับ Netbeans 5.0 เปนตัวอยางการเปลีย


่ น string
ที่มีอยูในโปรแกรมทั้งหมดใหเปนภาษาไทย โดยเราจะใช internationalization wizard เปนตัว
ชวย

เราจะเริ่มตนขั้นตอนดังนี้

406
บทที่ 11: GUI และ Event Handling

1. เลือก File Æ New Project


2. ภายใต Categories เลือก General และเลือก GUI Form Example แลวกดปุม Next
3. ผูอา นสามารถเปลี่ยนชื่อ project ไดถาตองการ ในที่นี้เราจะใชชื่อที่กําหนดมา คือ
GUIFormExamples

เมื่อทําเสร็จแลวเราก็จะเห็นไฟล readme.txt ซึ่งเปนไฟลทบ


ี่ อกถึงสวนประกอบตาง ๆ ที่มอ
ี ยูใน
ตัวอยาง และเมื่อกดดูไฟลใน project folder เราก็จะเห็นไฟลตา ง ๆ ดังนี้

intro. to Java (FEU.faa)


ขั้นตอนตอไปก็คือการสราง properties file ใหกับ form ที่มอ
ี ยู ซึ่งเราจะทําใหกับ Antenna
และ ContactEditor สวน Find เราจะทิ้งไวเฉย ๆ

1. กดปุมขวาบน examples node ใน Projects Tab เลือก New Æ Properties File


2. ใสชื่อ ContactEditor แลวกดปุม Finish

เราจะเห็น Contact.properties ไฟลใน examples folder ซึ่งจะถูกเปดไวใน Source editor


ดวย (เราจะทําอยางเดียวกันนีก
้ ับ Antenna.properties ไฟลดวยก็ได)

ตอไปเราจะเรียก Internationalization wizard ขึ้นมาทํางาน

1. กดปุมขวาบน Source Packages เลือก Tools Æ Internationalization Æ


Internationalization Wizard
2. เพื่อใหเราไมตอ งกังวลกับการทํางาน เราจะลบ examples.Find ออกดวยการเลือก
Find แลวกดปุม  Remove Source(s) (ลบ Antenna ดวยก็ได)

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

3. กดปุม Next เราก็จะไดหนาจอคลาย ๆ กับที่เห็นนี้

intro. to Java (FEU.faa)


4. เมื่อทําการ save ไฟลแลวขั้นตอนตอไปก็คือการเพิ่ม locale ที่ตองการ เพือ
่ ใหงายขึ้น
เราจะทําเฉพาะ ContactEditor เทานั้น

5. ขั้นตอนที่เหลือก็คือการทดสอบ locale เชนทีไ


่ ดทํากอนหนานี้ (ใส –
Duser.language=th –Duser.country=TH ใน VM Options ของ Run ใน
Properties ของ GUIFormExamples project)

408
บทที่ 11: GUI และ Event Handling

เราก็จะไดผลลัพธคลาย ๆ กับที่เห็นนี้

intro. to Java (FEU.faa)


ทุกครั้งที่เราทําการเปลี่ยนแปลง source code เราจะตองทําการเรียก Internationalization
Wizard ขึ้นมาใชเพื่อใหการเปลี่ยนแปลงที่เกิดขึ้นมีผลตอโปรแกรมของเรา (ถึงแมวา จะไมมีการ
เปลี่ยนแปลงตัว properties ไฟลก็ตาม การเปลี่ยนแคนอยนิด เชน ขนาดของ font ก็มีผล ดังนั้น
เราตองไมลืมทีจ ่ ะเรียก Internationalization Wizard หลังการเปลี่ยนแปลงนั้น ๆ)

Netbeans เปนเครื่องมือที่ดที ส
ี่ ุดตัวหนึ่งในการพัฒนาโปรแกรมทีต
่ องใช UI ในการโตตอบกับ
ผูใช โปรแกรมตัวอยางที่เราแสดงใหดูเปนโปรแกรมเล็ก ๆ ทีแ่ สดงถึงการใช UI component
ตาง ๆ (บางสวน) ที่ Netbeans มีให ผูอานควรฝกฝนการใช Netbeans ในการสรางโปรแกรมอื่น
ๆ เพื่อใหเกิดความคลองตัวและสามารถใช Netbeans ไดอยางมีประสิทธิภาพตอไป

สรุป

ในบทนี้เราไดแสดงกระบวนการ และวิธีการเรียกใช component ตาง ๆ สําหรับการสราง


โปรแกรมงาย ๆ หลายโปรแกรม รวมไปถึงการใช event listener ตาง ๆ ที่ Java มีให นอกจากนี้
เรายังไดแสดงวิธีการใช Netbeans ในการสรางโปรแกรมแบบที่เรียกวา visual programming
ซึ่งเปนการลดขัน
้ ตอนในการพัฒนาโปรแกรมในสวนของ UI โดยรวมแลวเราไดพูดถึง

9 การใช JButton, JLabel, JRadioButton, JComboBox, JCheckBox, JTextField,


JTextArea, JSlider, JSpinner, JProgressBar, ProgressMonitor, JFileChooser,
JMenuBar, JMenu, JMenuItem
9 การใช layout manager: FlowLayout, GridLayout, BorderLayout
9 การดักฟง event ตาง ๆ และการสนองตอบตอเหตุการณนน ั้ ๆ
9 การใช Netbeans ในการพัฒนาโปรแกรม
9 การใช internationalization ในโปรแกรม

แบบฝกหัด

1. จงออกแบบ GUI สําหรับโปรแกรมตัวอยาง (Interest.java) ในบททีส ่ าม แทนที่จะรับขอมูล


แบบเดิม ใหใช JTextField รับขอมูลทั้งสามตัวแทนและจะประมวลผลเมื่อผูใ ชกดปุมเทานั้น
การแสดงผลใหใช JTextArea

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

2. จงออกแบบ GUI สําหรับโปรแกรมการหา ตัวหารรวมมาก (GCD) และตัวคูณรวมนอย


(LCM) ของตัวเลขสองตัวที่รับเขามาทาง JTextField ใหแสดงผลลัพธออกทาง JLabel (ดู
คําอธิบายเรื่อง GCD และ LCM จากโจทยทา ยบททีส่ าม)

3. จงออกแบบ GUI สําหรับโปรแกรม Text Editor อยางงายทีย่ อมใหผูใช คนหาคําที่ตองการ


ได ทําการ cut และ paste ได ลองดูโปรแกรม Notepad เปนตัวอยาง

intro. to Java (FEU.faa)


4. จงออกแบบ GUI สําหรับโปรแกรมการเปลีย ่ นสกุลเงินตาง ๆ เชน US Dollar, British
Pound, Japanese Yen โดยมีสกุลเงินไมนอยกวาหาสกุล การเปลี่ยนสกุลจะเกิดขึ้นเมื่อผูใช
ใสสกุลเงินใดสกุลหนึ่งเสร็จ (หลังจากการกดปุม)

5. จงออกแบบ GUI สําหรับโปรแกรมเครื่องคิดเลขฐาน ที่มค ี วามสามารถในการเปลี่ยนเลขฐาน


หลัก ๆ ที่เกี่ยวของกับคอมพิวเตอร พรอมทั้งสามารถที่จะ บวก ลบ เลขตาง ๆ เหลานี้ได

6. จงออกแบบ GUI สําหรับโปรแกรมทีท ่ ําการ simulate การคํานวณดอกเบี้ยของบัญชีเงิน


ฝากทีม ่ ีการนําเงินเขาและออกแบบ random การคํานวณตองคํานึงถึงคาความถูกตองของ
เงิน ณ เวลานั้น ๆ ดวย โปรแกรมควรมีการใช Thread เพื่อแกปญหาเรื่อง Critical Section
โปรแกรมควรคิดดอกเบี้ยทุกครั้งที่มีการฝากเงินที่มากกวา 1000 บาทและหักคาโสหุย  ทุก
ครั้งที่มีการถอนเงินเกิน 500 บาท

410

You might also like