สร้างโปรแกรมตั้งเวลาปิดเครื่องคอมพิวเตอร์ด้วย Java Swing และแปลงเป็นไฟล์ EXE ด้วย Launch4j


บทนำ

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 กำหนดไอคอนของ EXE
  • headerType เป็นโปรแกรมแบบ GUI ไม่แสดงหน้าต่าง Console
  • chdir กำหนดโฟลเดอร์ทำงาน
  • 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

,