บทนำ
Java เป็นภาษาที่สามารถพัฒนาโปรแกรม Desktop ได้ผ่าน Java Swing ซึ่งเหมาะสำหรับการสร้างโปรแกรมที่ทำงานบนระบบปฏิบัติการ Windows, Linux และ macOS
บทความนี้จะอธิบายตั้งแต่การเขียนโปรแกรม TimeComputer.java สำหรับตั้งเวลาปิดเครื่องคอมพิวเตอร์ ไปจนถึงการสร้างไฟล์ time-computer.jar และแปลงเป็น TimeComputer.exe ด้วย Launch4j เพื่อให้สามารถเปิดใช้งานได้เหมือนโปรแกรม Windows ทั่วไป
คุณสมบัติของโปรแกรม
โปรแกรมสามารถทำงานได้ดังนี้
- แสดงเวลาปัจจุบัน
- ตั้งเวลาปิดเครื่องแบบนับถอยหลัง
- ตั้งเวลาปิดเครื่องตามเวลาจริง
- แสดงเวลานับถอยหลังแบบ Real-time
- ยกเลิกการตั้งเวลาได้
- เปลี่ยนไอคอนของโปรแกรม
- รองรับภาษาไทย
- แปลงเป็นไฟล์ EXE สำหรับ Windows
การออกแบบโปรแกรม
โปรแกรมใช้ Java Swing โดยสร้างคลาส
public class TimeComputer extends JFrame
เนื่องจาก JFrame เป็นหน้าต่างหลักของโปรแกรม
เมื่อเปิดโปรแกรม Constructor จะถูกเรียกใช้งาน
public TimeComputer() {
setUIFont(new Font("Tahoma", Font.PLAIN, 14));
setIconImage(new ImageIcon(getClass().getResource("/icon.png")).getImage());
initUI();
startClock();
}
ภายใน Constructor มีหน้าที่
- กำหนดฟอนต์ภาษาไทย
- กำหนดไอคอนโปรแกรม
- สร้างส่วนติดต่อผู้ใช้ (GUI)
- เริ่มต้นนาฬิกา
การสร้างหน้าต่างโปรแกรม
ภายในเมธอด initUI() ใช้กำหนดคุณสมบัติของหน้าต่าง
setTitle("ตั้งเวลาปิดคอมพิวเตอร์");
setSize(520,380);
setLocationRelativeTo(null);
setResizable(false);
setLayout(null);
โปรแกรมนี้ใช้
Absolute Layout
หรือการกำหนดตำแหน่ง Component ด้วยคำสั่ง
setBounds(x, y, width, height);
เช่น
btnStart.setBounds(120,200,120,40);
ข้อดีคือสามารถจัดตำแหน่งได้อย่างอิสระ เหมาะสำหรับโปรแกรมขนาดเล็ก ส่วนข้อเสียคือเมื่อเปลี่ยนขนาดหน้าต่างหรือเปลี่ยนความละเอียดหน้าจอ ตำแหน่งของ Component จะไม่ปรับอัตโนมัติ
การสร้างส่วนติดต่อผู้ใช้ (GUI)
โปรแกรมประกอบด้วย Component หลายชนิด เช่น
- JLabel
- JButton
- JSpinner
- JRadioButton
- ButtonGroup
ตัวอย่าง
rbCountdown = new JRadioButton("นับถอยหลัง (นาที)");
rbTimeOfDay = new JRadioButton("ปิดตามเวลา (ชั่วโมง:นาที)");
และนำมาอยู่ใน ButtonGroup
ButtonGroup group = new ButtonGroup();
group.add(rbCountdown);
group.add(rbTimeOfDay);
เพื่อให้สามารถเลือกได้เพียงโหมดเดียว
การแสดงเวลาปัจจุบัน
ใช้ Swing Timer ทำงานทุก ๆ 1 วินาที
clockTimer = new Timer(1000, e -> {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
lblClock.setText(sdf.format(new Date()));
});
ทุก ๆ 1 วินาที โปรแกรมจะอ่านเวลาปัจจุบันจากระบบและอัปเดต Label
การตั้งเวลาปิดเครื่องแบบนับถอยหลัง
เมื่อผู้ใช้เลือกโหมดนับถอยหลัง โปรแกรมจะอ่านค่าจาก
JSpinner
แล้วแปลงเป็นมิลลิวินาที
remainingMillis = minute * 60L * 1000L;
จากนั้นสร้าง Timer
shutdownTimer = new Timer((int) remainingMillis, e -> shutdownPC());
เมื่อครบเวลาจะเรียก
shutdownPC();
เพื่อปิดเครื่องคอมพิวเตอร์
การแสดงเวลานับถอยหลัง
โปรแกรมสร้าง Timer อีกตัวหนึ่งเพื่ออัปเดตหน้าจอทุก 1 วินาที
countdownUI = new Timer(1000, e -> {
remainingMillis -= 1000;
});
จากนั้นแปลงเวลาที่เหลือ
long sec = remainingMillis / 1000;
แล้วแสดงผลในรูปแบบ
HH:MM:SS
เช่น
01:15:42
พร้อมกำหนดให้เป็นสีแดง
lblCountdown.setForeground(Color.RED);
เพื่อให้ผู้ใช้สังเกตเห็นได้ง่าย
การตั้งเวลาปิดเครื่องตามเวลาจริง
โปรแกรมอ่านค่าจาก
ชั่วโมง
นาที
แล้วเปรียบเทียบกับเวลาปัจจุบัน
long diff = targetTime.getTime() - nowTime.getTime();
หากเวลาที่เลือกเลยไปแล้ว
diff += 24 * 60 * 60 * 1000;
เพื่อเลื่อนไปยังวันถัดไปโดยอัตโนมัติ
การปิดเครื่องคอมพิวเตอร์
เมื่อครบกำหนด โปรแกรมจะเรียก
Runtime.getRuntime().exec("shutdown -s -t 0");
ซึ่งเป็นคำสั่งของ Windows สำหรับปิดเครื่องทันที
การรองรับภาษาไทย
Swing บางเวอร์ชันอาจใช้ฟอนต์ที่ไม่รองรับภาษาไทย
จึงกำหนดฟอนต์ใหม่ทั้งโปรแกรม
setUIFont(new Font("Tahoma", Font.PLAIN,14));
ทำให้ Label, Button และ Component ต่าง ๆ สามารถแสดงภาษาไทยได้ถูกต้อง
การกำหนดไอคอนโปรแกรม
ไอคอนของหน้าต่างกำหนดด้วย
setIconImage(
new ImageIcon(
getClass().getResource("/icon.png")
).getImage()
);
ไฟล์ icon.png ต้องอยู่ใน Resource ของโปรเจกต์จึงจะสามารถโหลดได้
โค้ดเต็ม TimeComputer.java
import java.awt.Font;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.plaf.FontUIResource;
public class TimeComputer extends JFrame {
private JLabel lblClock;
private JLabel lblStatus;
private JLabel lblCountdown; // ⭐ เพิ่ม
private JRadioButton rbCountdown;
private JRadioButton rbTimeOfDay;
private JSpinner spMinute;
private JSpinner spHour;
private JSpinner spMin;
private JButton btnStart;
private JButton btnCancel;
private Timer clockTimer;
private Timer shutdownTimer;
private Timer countdownUI; // ⭐ timer สำหรับอัปเดตเวลา
private long remainingMillis = 0; // ⭐ เก็บเวลาที่เหลือ
public TimeComputer() {
setUIFont(new Font("Tahoma", Font.PLAIN, 14));
setIconImage(new ImageIcon(getClass().getResource("/icon.png")).getImage());
initUI();
startClock();
}
private void initUI() {
setTitle("ตั้งเวลาปิดคอมพิวเตอร์");
setSize(520, 380);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setLayout(null);
JLabel lblTitle = new JLabel("เวลาปัจจุบัน");
lblTitle.setBounds(30, 20, 150, 20);
add(lblTitle);
lblClock = new JLabel("00:00:00");
lblClock.setFont(new Font("Tahoma", Font.BOLD, 28));
lblClock.setBounds(30, 45, 250, 40);
add(lblClock);
// โหมด
rbCountdown = new JRadioButton("นับถอยหลัง (นาที)");
rbTimeOfDay = new JRadioButton("ปิดตามเวลา (ชั่วโมง:นาที)");
rbCountdown.setBounds(30, 100, 200, 25);
rbTimeOfDay.setBounds(250, 100, 220, 25);
ButtonGroup group = new ButtonGroup();
group.add(rbCountdown);
group.add(rbTimeOfDay);
rbCountdown.setSelected(true);
add(rbCountdown);
add(rbTimeOfDay);
// input countdown
spMinute = new JSpinner(new SpinnerNumberModel(1, 1, 9999, 1));
spMinute.setBounds(30, 130, 80, 30);
add(spMinute);
// input time
spHour = new JSpinner(new SpinnerNumberModel(0, 0, 23, 1));
spHour.setBounds(250, 130, 60, 30);
add(spHour);
spMin = new JSpinner(new SpinnerNumberModel(0, 0, 59, 1));
spMin.setBounds(320, 130, 60, 30);
add(spMin);
// ปุ่ม
btnStart = new JButton("เริ่ม");
btnStart.setBounds(120, 200, 120, 40);
add(btnStart);
btnCancel = new JButton("ยกเลิก");
btnCancel.setBounds(260, 200, 120, 40);
add(btnCancel);
// ⭐ แสดง countdown จริง
lblCountdown = new JLabel("00:00:00");
lblCountdown.setFont(new Font("Tahoma", Font.BOLD, 26));
lblCountdown.setForeground(java.awt.Color.RED);
lblCountdown.setBounds(180, 260, 200, 40);
add(lblCountdown);
lblStatus = new JLabel("Stand-by");
lblStatus.setFont(new Font("Tahoma", Font.BOLD, 18));
lblStatus.setBounds(200, 310, 200, 30);
add(lblStatus);
btnStart.addActionListener(e -> startShutdown());
btnCancel.addActionListener(e -> cancelShutdown());
}
// ================= CLOCK =================
private void startClock() {
clockTimer = new Timer(1000, e -> {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
lblClock.setText(sdf.format(new Date()));
});
clockTimer.start();
}
// ================= START =================
private void startShutdown() {
stopAll();
if (rbCountdown.isSelected()) {
startCountdown();
} else {
startTimeOfDay();
}
}
// ================= COUNTDOWN =================
private void startCountdown() {
int minute = (Integer) spMinute.getValue();
remainingMillis = minute * 60L * 1000L;
// timer ปิดเครื่อง
shutdownTimer = new Timer((int) remainingMillis, e -> shutdownPC());
shutdownTimer.setRepeats(false);
shutdownTimer.start();
// ⭐ timer อัปเดต UI ทุก 1 วิ
countdownUI = new Timer(1000, e -> {
remainingMillis -= 1000;
if (remainingMillis <= 0) {
lblCountdown.setText("00:00:00");
countdownUI.stop();
return;
}
long sec = remainingMillis / 1000;
long h = sec / 3600;
long m = (sec % 3600) / 60;
long s = sec % 60;
lblCountdown.setText(String.format("%02d:%02d:%02d", h, m, s));
});
countdownUI.start();
lblStatus.setText("นับถอยหลัง " + minute + " นาที");
}
// ================= TIME OF DAY =================
private void startTimeOfDay() {
int hour = (Integer) spHour.getValue();
int min = (Integer) spMin.getValue();
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
try {
Date nowTime = sdf.parse(sdf.format(now));
Date targetTime = sdf.parse(String.format("%02d:%02d", hour, min));
long diff = targetTime.getTime() - nowTime.getTime();
if (diff <= 0) {
diff += 24 * 60 * 60 * 1000;
}
remainingMillis = diff;
shutdownTimer = new Timer((int) diff, e -> shutdownPC());
shutdownTimer.setRepeats(false);
shutdownTimer.start();
// ⭐ แสดง countdown เช่นกัน
countdownUI = new Timer(1000, e -> {
remainingMillis -= 1000;
long sec = remainingMillis / 1000;
long h = sec / 3600;
long m = (sec % 3600) / 60;
long s = sec % 60;
lblCountdown.setText(String.format("%02d:%02d:%02d", h, m, s));
});
countdownUI.start();
lblStatus.setText("จะปิดเวลา " + String.format("%02d:%02d", hour, min));
} catch (Exception ex) {
ex.printStackTrace();
}
}
// ================= CANCEL =================
private void cancelShutdown() {
stopAll();
lblStatus.setText("Stand-by");
lblCountdown.setText("00:00:00");
}
// ================= STOP ALL =================
private void stopAll() {
if (shutdownTimer != null) shutdownTimer.stop();
if (countdownUI != null) countdownUI.stop();
}
// ================= SHUTDOWN =================
private void shutdownPC() {
try {
Runtime.getRuntime().exec("shutdown -s -t 0");
} catch (Exception ex) {
ex.printStackTrace();
}
}
// ================= FONT FIX =================
private void setUIFont(Font font) {
Enumeration<Object> keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof FontUIResource) {
UIManager.put(key, new FontUIResource(font));
}
}
}
// ================= MAIN =================
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new TimeComputer().setVisible(true);
});
}
}
การคอมไพล์โปรแกรม
เมื่อเขียนโปรแกรมเสร็จแล้ว ให้เปิด Command Prompt ไปยังโฟลเดอร์ที่เก็บไฟล์
จากนั้นคอมไพล์
javac TimeComputer.java
คำสั่งนี้จะทำหน้าที่
- ตรวจสอบ Syntax
- แปลงไฟล์
.java - สร้างไฟล์
.class
ผลลัพธ์จะได้
TimeComputer.class
การสร้างไฟล์ JAR
เมื่อได้ไฟล์ .class
ให้สร้างไฟล์ JAR
jar cfe time-computer.jar TimeComputer *.class
คำสั่งนี้ประกอบด้วย
- jar คือเครื่องมือสร้างไฟล์ JAR
- c (Create) สร้างไฟล์ใหม่
- f (File) กำหนดชื่อไฟล์ผลลัพธ์
- e (Entry Point) ระบุ Main Class
ผลลัพธ์คือ
time-computer.jar
ซึ่งสามารถเปิดได้ด้วย Java Runtime
แปลง JAR เป็น EXE ด้วย Launch4j
เพื่อให้โปรแกรมสามารถเปิดได้เหมือนโปรแกรม Windows ทั่วไป สามารถใช้ Launch4j
ตัวอย่างไฟล์ launch4j.xml
<launch4jConfig>
<outfile>TimeComputer.exe</outfile>
<jar>time-computer.jar</jar>
<icon>icon.ico</icon>
<headerType>gui</headerType>
<chdir>.</chdir>
<classPath>
<mainClass>TimeComputer</mainClass>
</classPath>
<jre>
<path>jre</path>
<minVersion>1.8.0</minVersion>
<bundledJre64Bit>false</bundledJre64Bit>
</jre>
</launch4jConfig>
รายละเอียดแต่ละส่วน
outfileชื่อไฟล์ EXE ที่สร้างjarระบุไฟล์ JAR ของโปรแกรมiconกำหนดไอคอนของ EXEheaderTypeเป็นโปรแกรมแบบ GUI ไม่แสดงหน้าต่าง Consolechdirกำหนดโฟลเดอร์ทำงานmainClassระบุคลาสที่มีเมธอดmain()pathระบุโฟลเดอร์ JRE ที่แนบมากับโปรแกรมminVersionกำหนดเวอร์ชัน Java ขั้นต่ำที่ต้องใช้
โครงสร้างไฟล์หลัง Build
TimeComputer/
│
├── TimeComputer.exe
├── time-computer.jar
├── icon.ico
└── jre/
โดยโฟลเดอร์ jre คือ Java Runtime ที่โปรแกรมใช้ทำงาน
การนำโปรแกรมไปใช้บนเครื่องอื่น
หากใช้ Launch4j พร้อม Bundle JRE
ให้คัดลอกทั้งโฟลเดอร์
TimeComputer/
├── TimeComputer.exe
├── time-computer.jar
└── jre/
เพียงเท่านี้ก็สามารถเปิดใช้งานได้ โดยไม่ต้องติดตั้ง Java เพิ่มบนเครื่องปลายทาง
สรุป
การพัฒนาโปรแกรมด้วย Java Swing ร่วมกับ Launch4j เป็นวิธีที่เหมาะสำหรับการสร้างโปรแกรม Desktop บน Windows โดยสามารถเริ่มต้นจากการเขียนโค้ดในไฟล์ TimeComputer.java จากนั้นคอมไพล์เป็นไฟล์ .class สร้างเป็นไฟล์ time-computer.jar และแปลงเป็น TimeComputer.exe ด้วย Launch4j พร้อมแนบ Java Runtime เพื่อให้สามารถนำโปรแกรมไปใช้งานบนเครื่องอื่นได้ทันทีโดยไม่ต้องติดตั้ง Java เพิ่ม
แนวทางนี้เหมาะสำหรับโปรแกรมขนาดเล็กถึงขนาดกลาง เช่น โปรแกรมตั้งเวลาปิดเครื่อง โปรแกรมจัดการข้อมูล โปรแกรมช่วยงานภายในองค์กร หรือเครื่องมืออัตโนมัติที่ต้องการความสะดวกในการแจกจ่ายและใช้งานบนระบบปฏิบัติการ Windows
