Hướng dẫn viết chương trình vẽ đồng hồ kim 2D bằng Java

Ý tưởng rất đơn giản, dùng hàm fillOval để vẽ mặt đồng hồ, và dùng hàm drawLine để vẽ kim đồng hồ.

Ta vẽ một cái hình oval có 2 đường kính bằng nhau (tức là hình tròn), có tọa độ của đỉnh góc bên trái (chỗ 9 giờ trên mặt đồng hồ) là 50,50 và đường kính là 200 tức là tâm đường tròng có tọa độ là 50+(200/2) = 150.

Còn vẽ kim đồng hồ thì chỉ cần dùng hàm drawLine(x1,y1,x2,y2); trong đó x1 là hoành độ điểm đầu,y1 là tung độ điểm đầu, x2 là hoành độ điểm cuối.Với x1,y1 luôn bằng 150,150 là tâm hình tròn.

còn x2,y2 được tính với công thức sau:

Nếu là kim giây:

-Kim giây phải quay hết 1 đường tròn 360 độ qua 60 cái tíc, vậy 1 cái tích phải nhích 6 độ nhưng do tính bằng radian nên số độ phải

rKimGiay = (số giây * 6) * PI/180; (do 1 độ bằng Pi/180 radian);
x2 = x1 + (bán kính * cos(rKimGiay - Pi/2));

y2 = y1 + (bán kính * sin(rKimGiay - Pi/2));

(mình không hiểu rõ lượng giác lắm, nhưng khi vẽ nó bị lệch 90 nên mình trừ ra 1 cung pi/2 để nó chạy đúng)

Tương tự với kim phút, nhưng mình phải cộng thêm (số giây/60) để kim phút nhích khi kim giây nhích

rKimPhut = ((số phút + (số giây/60)) * 6) * PI/180;

Và với kim giờ thì mình làm giống kim phút nhưng phải * 30 thay thì 6 vì mỗi nấc giờ, kim giờ nhích 1 góc 30 độ ( 30×12=360);

rKimGiờ = ((số giờ + (số phút/60)) * 30) * PI/180;

Còn giờ hiện hành thì mình sử dụng đối tượng GregorianCalendar để lấy giây,phút, giờ hiện hành.

GregorianCalendar cal;
double second = cal.get(Calendar.SECOND);
double minute = cal.get(Calendar.MINUTE);
double hours = cal.get(Calendar.HOUR);

Code hoành chỉnh

package clock;

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;

/**
 *
 * @author hungnq
 */
public class Clock2d extends Applet {

    GregorianCalendar cal;
    Timer clockTimer = new Timer();
    TimeZone clockTimeZone = TimeZone.getDefault();

    public Clock2d() {
        clockTimer.schedule(new TickTimerTask(), 0, 1000);
    }

    @Override
    public void init() {}

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.BLUE);
        g.fillOval(40, 40, 220, 220);
        g.setColor(Color.WHITE);
        g.fillOval(50, 50, 200, 200);
        //g.setColor(Color.ORANGE);
        // g.fillOval(80, 80, 140, 140);
        //g.drawOval(45,45, 210, 210);
        double second = cal.get(Calendar.SECOND);
        double minute = cal.get(Calendar.MINUTE);
        double hours = cal.get(Calendar.HOUR);
        //double milis = cal.get(Calendar.MILLISECOND);
        //Vẽ mặt đồng hồ
        for (int i = 0; i < 60; i++) {
            int length = 90;
            double rad = (i * 6) * (Math.PI) / 180;
            if (i % 5 == 0) {
                length = 82;
                g.setColor(Color.BLUE);
            } else {
                g.setColor(Color.GRAY);
            }
            int x = 150 + (int)(95 * Math.cos(rad - (Math.PI / 2)));
            int y = 150 + (int)(95 * Math.sin(rad - (Math.PI / 2)));
            int x1 = 150 + (int)(length * Math.cos(rad - (Math.PI / 2)));
            int y1 = 150 + (int)(length * Math.sin(rad - (Math.PI / 2)));
            g.drawLine(x, y, x1, y1);
        }
        //vẽ kim đồng hồ
        drawHands(g, second, minute, hours);
        //g.fillOval(50, 50, 200, 200);
        SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
        g.setColor(Color.BLUE);
        g.setFont(new Font("Tahoma", Font.BOLD, 16));
        g.drawString(sdf.format(cal.getTime()), 120, 20);
        g.setFont(new Font("Arial", Font.BOLD, 10));
        g.drawString("CODE BY HUNGNQ", 110, 140);
    }

    /***
     * 1 độ = pi/180 rad
     * -đường tròn 360 độ, kim giây phải nhích 60 lần suy ra 1 lần nhích số radian
     * bằng (số giây * 6) * pi/180 rad
     * -tương tự với số phút, nhưng khi kim giây nhích thì kim phút cũng phải nhích
     * theo nên ta công thêm số giây/60 vào để kim phút nhích, ví dụ 1 phút 30 giây
     *  thì bằng 1,5 phút
     * - đối với kim giờ thì quay 1 vòng tròn là 12 giờ, vậy mỗi giờ tăng 1 góc
     * là  số giờ x 30 (vì 12x30=360), và phải cộng thêm số phút để cho chính xác
     * lưu ý phải trừ ra pi/2 rad để đồng hồ quay đúng, vì đường tròn lượng giác
     * có chiều quay ngược kim đồng hồ tương ứng 9h=pi rad,12=pi/2 rad... nên phải trừ ra pi/2
     * @param g
     * @param second
     * @param minute
     * @param hours
     */
    public void drawHands(Graphics g, double second, double minute, double hours) {
        double rSecond = (second * 6) * (Math.PI) / 180;
        double rMinute = ((minute + (second / 60)) * 6) * (Math.PI) / 180;
        double rHours = ((hours + (minute / 60)) * 30) * (Math.PI) / 180;
        g.setColor(Color.RED);
        g.drawLine(150, 150, 150 + (int)(100 * Math.cos(rSecond - (Math.PI / 2))), 150 + (int)(100 * Math.sin(rSecond - (Math.PI / 2))));
        g.setColor(Color.BLACK);
        g.drawLine(150, 150, 150 + (int)(70 * Math.cos(rMinute - (Math.PI / 2))), 150 + (int)(70 * Math.sin((rMinute - (Math.PI / 2)))));
        g.drawLine(150, 150, 150 + (int)(50 * Math.cos(rHours - (Math.PI / 2))), 150 + (int)(50 * Math.sin(rHours - (Math.PI / 2))));
        //g.drawLine(150, 150, 150+(int)(100*Math.sin(2*Math.PI)),150+(int)(100*Math.cos(2*Math.PI)));
    }

    class TickTimerTask extends TimerTask {

        @Override
        public void run() {
            //throw new UnsupportedOperationException("Not supported yet.");
            cal = (GregorianCalendar) GregorianCalendar.getInstance(clockTimeZone);
            repaint();
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        JFrame frame = new JFrame("Clock 2D");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(330, 330));
        Clock2d clock2d = new Clock2d();
        clock2d.setPreferredSize(new Dimension(320, 320));
        clock2d.init();
        frame.setLayout(new BorderLayout());
        frame.getContentPane().add(clock2d, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }
}