【问题标题】:Multiple clocks using java GUI使用 java GUI 的多个时钟
【发布时间】:2013-02-03 07:26:13
【问题描述】:

我正在做一个项目,其中我必须使用 java GUI 创建一组 6 个时钟,显示不同位置的不同时间。我已经成功创建了使用线程显示一个时钟的代码。但是如何创建 6 个不同的线程来处理 6 个时钟呢? 这是创建单个时钟的以下类:

public class analogClockGUI extends JPanel implements Runnable{

    int secx,secy,lsecx,lsecy;               
    int minx,miny,lminx,lminy;          
    int hrx,hry,lhrx,lhry;
    int secRadius,minRadius,hrRadius,center;
    int pointx1,pointy1,pointx2,pointy2;

    double dhs,dhm,dhh;        
    Random rndm;               
    Thread t;
    Font clockFont;
    Date incre;
    int s,h,m;

    boolean off=false,drawFirst=true;
    Dimension dimension;
    Color wallColor,backColor=Color.black;

    int previousMin,previousSec;
    int change;    

    public analogClockGUI()
    {

        addMouseListener(new MouseAdapter()
        {

            public void mouseEntered(MouseEvent me)
            {
                dimension=getSize();


                if(change!=dimension.height)
                {
                    center=dimension.height/2;
                    setSize(new Dimension(dimension.height,dimension.height));
                }
                /*else
                {
                    center=dimension.width/2;
                    setSize(new Dimension(dimension.width,dimension.width));
                }*/
                drawFirst=true;
                change=dimension.height;     
                secRadius=(int)(0.7*center);
                minRadius=(int)(0.75*center);
                hrRadius=(int)(0.5*center);

            }
        });
        init();
    }



    public void init()
    {

        rndm=new Random();
        t=new Thread(this);
        dimension=getSize();
        center=dimension.height/2;

        secRadius=(int)(0.7*center);   
        minRadius=(int)(0.75*center);
        hrRadius=(int)(0.5*center);
        wallColor=new Color(rndm.nextInt(255),rndm.nextInt(255),rndm.nextInt(255));
        t.start();
    }
    public void run() throws NullPointerException
    {
        while(true)
        {
            s=new Date().getSeconds();//+incre.getSeconds(); 

            if(s!=previousSec)
            {

                dhs=Math.toRadians(s*6-90);
                secx=(int)(secRadius*Math.cos(dhs))+center;
                secy=(int)(secRadius*Math.sin(dhs))+center;

                pointx1=(int)((secRadius+center*0.15)*Math.cos(dhs))+center;
                pointy1=(int)((secRadius+center*0.15)*Math.sin(dhs))+center;


                m=new Date().getMinutes();
                dhm=Math.toRadians(m*6-90);
                minx=(int)(minRadius*Math.cos(dhm))+center;
                miny=(int)(minRadius*Math.sin(dhm))+center;

                pointx2=(int)((secRadius+center*0.15)*Math.cos(dhm))+center;
                pointy2=(int)((secRadius+center*0.15)*Math.sin(dhm))+center;


                h =new Date().getHours();
                dhh=Math.toRadians(h*30-90+((m-1)/2));
                hrx=(int)(hrRadius*Math.cos(dhh))+center;
                hry=(int)(hrRadius*Math.sin(dhh))+center;

                repaint();
                previousSec=s;
            }
            try
            {
                Thread.sleep(100);
                if(off)
                    break;
            }catch(InterruptedException e){}          
        }
    }
    public void upgate(Graphics g) throws NullPointerException
    {

        if(drawFirst==true)
        {

            clockFont=new Font("Serif",Font.PLAIN,(int)(0.2*center));
            drawFirst=false;
        }
        if(m!=previousMin){
            if(s==0)
            {

                wallColor=new Color(rndm.nextInt(255),rndm.nextInt(255),rndm.nextInt(255));
                g.setColor(wallColor);
                g.fillOval(0,0,2*center,2*center);
                g.setColor(backColor);
                g.fillOval((int)(0.1*center),(int)(0.1*center),(int)(center*1.8),(int)(center*1.8));

                clear(g);
                previousMin=m;
            }}
        drawPoints(g);
        g.setColor(Color.white);
        g.setFont(clockFont);

        g.drawString("12",(int)(0.9*center),(int)(0.25*center));
        g.drawString("6",(int)(0.95*center),(int)(1.885*center));
        g.drawString("9",(int)(0.11*center),(int)(1.06*center));
        g.drawString("3",(int)(1.805*center),(int)(1.07*center));
        g.drawString("1",(int)(1.369*center),(int)(0.36*center));
        g.drawString("2",(int)(1.674*center),(int)(0.645*center));
        g.drawString("4",(int)(1.665*center),(int)(1.485*center));
        g.drawString("5",(int)(1.372*center),(int)(1.778*center));
        g.drawString("7",(int)(0.545*center),(int)(1.777*center));
        g.drawString("8",(int)(0.227*center),(int)(1.485*center));
        g.drawString("10",(int)(0.212*center),(int)(0.675*center));
        g.drawString("11",(int)(0.522*center),(int)(0.365*center));


        if(lsecx!=0)
        {
            g.setColor(backColor);
            g.drawLine(center,center,lsecx,lsecy);

        }


        g.setColor(Color.green);
        g.drawLine(center,center,hrx,hry);
        g.drawLine(center+1,center,hrx,hry);
        g.drawLine(center-1,center,hrx,hry);
        g.drawLine(center,center+1,hrx,hry);
        g.drawLine(center,center-1,hrx,hry);
        g.drawLine(center+2,center,hrx,hry);
        g.drawLine(center-2,center,hrx,hry);
        g.drawLine(center,center+2,hrx,hry);
        g.drawLine(center,center-2,hrx,hry);

        //show the minute hand with bigger size
        g.setColor(Color.blue);
        g.drawLine(center,center,minx,miny);
        g.drawLine(center+1,center,minx,miny);
        g.drawLine(center-1,center,minx,miny);
        g.drawLine(center,center+1,minx,miny);
        g.drawLine(center,center-1,minx,miny);

        g.setColor(Color.red);
        g.drawLine(center,center,secx,secy);
        g.fillOval(center-3,center-3,6,6);       //center point

        if(m%5!=0)
        {
            g.setColor(Color.cyan);
            g.fillOval(pointx2-2,pointy2-2,4,4);

        }

        if(s%5!=0)
        {
            g.setColor(Color.red);
            g.fillOval(pointx1-2,pointy1-2,4,4);

        }

        lminx=minx;
        lminy=miny;
        lhrx=hrx;
        lhry=hry;
        lsecx=secx;
        lsecy=secy;



    }
    public void paint(Graphics g)  throws NullPointerException
    {
        g.setColor(wallColor);
        g.fillOval(0,0,2*center,2*center);
        g.setColor(backColor);
        g.fillOval((int)(0.1*center),(int)(0.1*center),(int)(center*1.8),(int)(center*1.8));
        upgate(g);
    }
    public void clear(Graphics g)  throws NullPointerException
    {
        if(lsecx!=0)
        {
            g.setColor(backColor);
            g.drawLine(center,center,lhrx,lhry);
            g.drawLine(center+1,center,lhrx,lhry);
            g.drawLine(center-1,center,lhrx,lhry);
            g.drawLine(center,center+1,lhrx,lhry);
            g.drawLine(center,center-1,lhrx,lhry);
            g.drawLine(center+2,center,lhrx,lhry);
            g.drawLine(center-2,center,lhrx,lhry);
            g.drawLine(center,center+2,lhrx,lhry);
            g.drawLine(center,center-2,lhrx,lhry);

            g.drawLine(center,center,lminx,lminy);
            g.drawLine(center+1,center,lminx,lminy);
            g.drawLine(center-1,center,lminx,lminy);
            g.drawLine(center,center+1,lminx,lminy);
            g.drawLine(center,center-1,lminx,lminy);


        }


    }
    public void drawPoints(Graphics g) throws NullPointerException
    {
        int x1,y1;
        double p;
        for(int i=1;i<=60;i++)
        {
            if(i%5!=0)
            {
                p=i*0.10472;
                x1=(int)((secRadius+center*0.15)*Math.cos(p))+center;
                y1=(int)((secRadius+center*0.15)*Math.sin(p))+center;
                g.setColor(Color.gray);
                g.fillOval(x1-2,y1-2,4,4);

            }
        }


    }
    public void stop() throws NullPointerException /*close the thread when applet stopped*/
    {
        off=true;
        t=null;
    }

}

【问题讨论】:

  • 如何显示第一个时钟?对于仅显示多个时钟的任务,线程听起来过于复杂。例如,如果您的 GUI 中有六个文本框,则不会为每个文本框启动单独的线程。
  • 现在,当我尝试更改一个时钟对象的大小时,其他时钟对象也会显示更改后的时钟对象。
  • 抱歉,我现在编辑了代码。手现在应该可以动了。
  • 你为什么不用Timer,它实现起来容易得多,事实上,我看不出为什么一个时钟会影响另一个时钟?
  • 在我的主要方法中,我正在制作类 AnalogClockGUI 的六个对象,并将这六个对象添加到六个不同的框架中。现在,当我尝试使用鼠标更改帧大小时运行代码后,其他帧显示每隔一帧的时钟。

标签: java multithreading swing user-interface


【解决方案1】:

您最大的问题在于您的Thread#run 代码...您时钟的每个实例都使用相同的Date 值。

public void run() throws NullPointerException {
    while (true) {
        // !! This is wrong !!
        // Each clock should have its own concept of it's start time, then each "second" you
        // should advance the time by a single second...
        s = new Date().getSeconds();//+incre.getSeconds(); 

您遇到的另一个问题是您不应该在线程内容中修改 UI 所依赖的值...

        // This is a bad idea...
        if (s != previousSec) {

            dhs = Math.toRadians(s * 6 - 90);
            secx = (int) (secRadius * Math.cos(dhs)) + center;
            secy = (int) (secRadius * Math.sin(dhs)) + center;

            pointx1 = (int) ((secRadius + center * 0.15) * Math.cos(dhs)) + center;
            pointy1 = (int) ((secRadius + center * 0.15) * Math.sin(dhs)) + center;


            m = new Date().getMinutes();
            dhm = Math.toRadians(m * 6 - 90);
            minx = (int) (minRadius * Math.cos(dhm)) + center;
            miny = (int) (minRadius * Math.sin(dhm)) + center;

            pointx2 = (int) ((secRadius + center * 0.15) * Math.cos(dhm)) + center;
            pointy2 = (int) ((secRadius + center * 0.15) * Math.sin(dhm)) + center;


            h = new Date().getHours();
            dhh = Math.toRadians(h * 30 - 90 + ((m - 1) / 2));
            hrx = (int) (hrRadius * Math.cos(dhh)) + center;
            hry = (int) (hrRadius * Math.sin(dhh)) + center;

            repaint();
            previousSec = s;
        }

问题是,UI 可能会在所有值更新之前更新,从而导致奇怪且不可预测的绘画技巧。

你真的需要 100 毫秒的更新吗?我最好将睡眠时间延长到 500 毫秒,将第二次更改稍早或稍晚。

你的画也把我吓坏了。非常小心,JPanel 已经有一个名为 update(Graphics) 的方法继承自 Container,您最终可能会出现命名冲突,导致程序崩溃(因为重绘引擎使用了 update

很少需要直接覆盖paint,最好使用paintComponent。这可确保在每个绘制周期中正确更新和准备组件。作为该过程的一部分,paintComponent 还将清除图形上下文,因此不需要 clear 方法(只要您尊重绘制链并调用 super.paintComponent

想法和解决方案

我要做的是设置一个“ticker”类,它的唯一职责是通知已注册的类“tick”已经发生。在这堂课中,我会使用javax.swing.Timer。这将强制从事件调度线程的上下文中调用“tick”事件,确保对绘制方法所依赖的变量的更改在同一线程的上下文中更新。我可能会考虑使用大约 250 毫秒的延迟,以便在通知周期中进行一些额外的调整。

无论是使用 Thread 还是 Timer,在更新“滴答声”时都会遇到瓶颈。

我还会考虑将时钟的表面绘制为BufferedImage,因为除非组件的大小发生变化,否则它不太可能发生变化。这将使油漆更快。

【讨论】:

  • 非常感谢您的建议。
  • 将paint改为paintComponent后,一切正常。再次感谢您的帮助。
【解决方案2】:

由于这些时钟的差异仅在于固定的时间偏移量,因此它们可能不需要单独的线程甚至单独的计时器任务。编写一个更大、更复杂的paintComponent 方法来绘制具有不同时间偏移且使用相同基准时间值的多个时钟。

【讨论】:

  • +1,是的,我就是这样做的。一个时钟组件,它采用时间偏移(可能还有位置和父级)作为 ctor 参数。根据需要创建任意数量,存储在向量中并在墙上时间定期触发每个“timeUpdate”方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 2018-06-18
  • 2017-10-23
  • 2021-12-30
  • 2020-01-17
相关资源
最近更新 更多