在线程中使用paint()方法

时间:2014-06-17 04:49:58

标签: java swing paint

我正在尝试创建一个绘制矩形(实际上是两个矩形)的程序,这个矩形根据每50ms产生的随机数上下移动。基本上,程序一遍又一遍地添加一个-5到5之间的随机数,直到它达到-85和85的阈值限制。我试图使paint()方法工作,但我从未看到矩形。我已经附加了我的基础程序,没有任何油漆的东西。我刚刚开始,所以我相信有更简单的方法来做所有事情。 基本上,我希望dialNum指示水平矩形的Y值,以便每次更新dialNum时它都会移动。现在我让它显示int dialNum,但它将被矩形取代。 (对于counterNum来说相同)

    public class runDrones {
    public static int counterNum = 0;
    public static int dialNum = 0;



    public runDrones() {

        final JFrame display = new JFrame();
        display.setSize(800,400);
        display.setLayout(new GridLayout());
        display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        display.setLocationRelativeTo(null);
        display.setVisible(true);



        Thread viewDial = new Thread(){

            public void run() {
                try {
                    runDial();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
            public void runDial() throws InterruptedException {
                JLabel dialStatus = new JLabel("OOPS");
                dialStatus.setFont(new Font("Verdana",1,86));
                display.add(dialStatus);

                while(dialNum >=-85 && dialNum <= 60){
                    dialNum += getRandom();
                    dialStatus.setText(Integer.toString(dialNum));
                    Thread.sleep(50);
                }

                dialStatus.setText("ALARM!!");      

            }   
        };

        Thread viewCounter = new Thread(){

            public void run(){
                try {
                    runCounter();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            public void runCounter() throws InterruptedException {
                JLabel counterStatus = new JLabel("OOPS");
                counterStatus.setFont(new Font("Verdana",1,86));
                display.add(counterStatus);                 

                while(counterNum >=-75 && counterNum <= 75){
                    counterNum += getRandom();
                    counterStatus.setText(Integer.toString(counterNum));
                    Thread.sleep(50);

                }

                counterStatus.setText("ALARM!!");
            }
        };
        viewDial.start();
        viewCounter.start();
    }
    public static int getRandom(){
        int firstNum = new Random().nextInt(5) + 1;
        int secondNum = new Random().nextInt(5) + 1;
        return firstNum - secondNum;
    }



}

3 个答案:

答案 0 :(得分:1)

Swing不是线程安全的,这意味着除了从事件调度线程的上下文之外,不应该以任何方式更新或修改UI。有关详细信息,请参阅Concurrency in Swing

对于Swing中的动画,请考虑使用javax.swing.Timer

以下示例使用javax.swing.Timer代替Thread s。通常情况下,我更喜欢使用单Timer,因为它减少了EDT的开销,但我想创建一个模仿你自己的例子的演示......

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RunDrones {

    public static int counterNum = 0;
    final JFrame display;
    private final JLabel counterStatus;
    private final JLabel dialStatus;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                new RunDrones();
            }
        });
    }

    public RunDrones() {

        display = new JFrame();
        display.setLayout(new GridLayout());

        dialStatus = new JLabel("OOPS");
        dialStatus.setFont(new Font("Verdana", 1, 86));
        display.add(dialStatus);

        counterStatus = new JLabel("OOPS");
        counterStatus.setFont(new Font("Verdana", 1, 86));
        display.add(counterStatus);

        display.setSize(800, 400);
        display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        display.setLocationRelativeTo(null);
        display.setVisible(true);

        Timer dialTimer = new Timer(50, new Dial());
        dialTimer.setInitialDelay(0);

        Timer counterTimer = new Timer(50, new Counter());

        dialTimer.start();
        counterTimer.start();
    }

    public static int getRandom() {
        int firstNum = new Random().nextInt(5) + 1;
        int secondNum = new Random().nextInt(5) + 1;
        return firstNum - secondNum;
    }

    public class Dial implements ActionListener {

        private int dialNum;

        @Override
        public void actionPerformed(ActionEvent e) {

            if (dialNum >= -85 && dialNum <= 60) {
                dialNum += getRandom();
                dialStatus.setText(Integer.toString(dialNum));
            } else {
                ((Timer) e.getSource()).stop();
                dialStatus.setText("ALARM!!");
            }
        }

    }

    public class Counter implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            if (counterNum >= -75 && counterNum <= 75) {
                counterNum += getRandom();
                counterStatus.setText(Integer.toString(counterNum));
            } else {
                ((Timer) e.getSource()).stop();
                counterStatus.setText("ALARM!!");
            }
        }

    }

}

您可能还希望阅读Performing Custom Painting

更新了粗略的示例

这是绘制矩形的一个粗略示例......现在,就个人而言,如果我这样做,我会建立某种范围模型并从中生成一个百分比值,可以用来显示矩形一个给定的可视区域,它只会让生活更轻松,但那就是我......

Bar

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RunDrones {

    public static int counterNum = 0;
    final JFrame display;
    private final JLabel counterStatus;
    private RectanglePane dialPane;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                new RunDrones();
            }
        });
    }

    public RunDrones() {

        display = new JFrame();
        display.setLayout(new GridLayout());

        dialPane = new RectanglePane();
        display.add(dialPane);

        counterStatus = new JLabel("OOPS");
        counterStatus.setFont(new Font("Verdana", 1, 86));
        display.add(counterStatus);

        display.setSize(800, 400);
        display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        display.setLocationRelativeTo(null);
        display.setVisible(true);

        Timer dialTimer = new Timer(50, new Dial(dialPane));
        dialTimer.setInitialDelay(0);

        Timer counterTimer = new Timer(50, new Counter());

        dialTimer.start();
        counterTimer.start();
    }

    public static int getRandom() {
        int firstNum = new Random().nextInt(5) + 1;
        int secondNum = new Random().nextInt(5) + 1;
        return firstNum - secondNum;
    }

    public class RectanglePane extends JPanel {

        private int yPos;

        public void setYPos(int yPos) {
            this.yPos = yPos;
            repaint();
        }

        public int getYPos() {
            return yPos;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); 

            g.setColor(Color.BLACK);
            int y = (getHeight() / 2) - 85;
            int x = (getWidth() - 20) / 2;
            g.drawRect(x, y, 40, 165);

//            y = (getHeight() + getYPos()) / 2;
            y = (getHeight() / 2) + getYPos();
            g.setColor(getForeground());
            g.fillRect(x, y, 40, 40);

        }

    }

    public class Dial implements ActionListener {

        private int dialNum;
        private RectanglePane pane;

        public Dial(RectanglePane pane) {
            this.pane = pane;
            pane.setForeground(Color.GREEN);
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            if (dialNum >= -85 && dialNum <= 60) {
                dialNum += getRandom();
                pane.setYPos(dialNum);
            } else {
                pane.setForeground(Color.RED);
                ((Timer) e.getSource()).stop();
            }
        }

    }

    public class Counter implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            if (counterNum >= -75 && counterNum <= 75) {
                counterNum += getRandom();
                counterStatus.setText(Integer.toString(counterNum));
            } else {
                ((Timer) e.getSource()).stop();
                counterStatus.setText("ALARM!!");
            }
        }

    }

}

简单RangeModel

的示例
public class RangeModel {

    private final int min;
    private final int max;
    private int value;

    public RangeModel(int min, int max) {
        this.min = min;
        this.max = max;
    }

    public int getMin() {
        return min;
    }

    public int getMax() {
        return max;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int newValue) {
        newValue = Math.max(getMin(), newValue);
        newValue = Math.min(getMax(), newValue);
        this.value = newValue;
    }

    public double getProgress() {

        int range = getMax() - getMin();
        int normalValue = getValue() - getMin();

        return (double)normalValue / ((double)range - 1);

    }

}

使用示例......

RangelModel model = new RangelModel(-85, 85);
for (int i = model.getMin(); i < model.getMax(); i++) {
    model.setValue(i);
    System.out.println(NumberFormat.getPercentInstance().format(model.getProgress()));
}

答案 1 :(得分:0)

好的,关于绘画:

  1. 你不能用负x或y坐标绘画 - 它们必须是正面的(不能转到-85)

  2. 问题的解决方案:要在屏幕上再次绘画,您希望线程调用:

    重绘();

  3. 在扩展JFrame或任何其他JComponent的情况下,这是一个继承的方法。如果没有,请在您已有的JFrame对象上调用它(不确定是否可能 - 重绘可能是受保护的方法)

答案 2 :(得分:0)

作为MadProgrammer mentioned,对于Swing中的动画,请考虑使用javax.swing.Timer而不是Thread。

对于问题中发布的代码,您可以按如下方式显示矩形:

  1. 创建一个扩展JPanel的类,并覆盖paintComponent

    class RectanglePanel extends JPanel{
    
      int y; //<<---The Y coordinate, which will be modified externally
    
      public void setY(int y) {  //<<---The setter method to change the Y-coord
        this.y = y;
        repaint();  //<<---To repaint the rectangle after the Y coordinate is changed
      }
    
      protected void paintComponent(Graphics g) {  //<<---Override the paintComponent 
       super.paintComponent(g);
       g.setColor(Color.black);  //<<---set the color of rectangle
       g.drawRect(20, y, 100, 60);  //<<---use the y-value to paint the rect
      }
    }
    

    2. 创建类的对象并将其添加到JFrame

    public static int counterNum = 0;
    public static int dialNum = 0;
    RectanglePanel panel = new RectanglePanel(); //<<---Create the object here
    
    //<<---rest of the code -->> 
    
    display.setLayout(new GridLayout());
    display.add(panel);    //<<---Add the object to the JPanel before setVisible
    display.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    

    3. 定期更改Y值

    counterNum += getRandom();
    counterStatus.setText(Integer.toString(counterNum));
    panel.setY(counterNum); //<<---Change the Y value of rectangle