【问题标题】:Can't repaint JPanel from another class无法从另一个类重新绘制 JPanel
【发布时间】:2020-11-20 09:56:44
【问题描述】:

在我之前关于Drawing rectangle within the loop?的问题之后

现在我想从另一个类中绘制矩形,在 for 循环内。这是循环的类:

public class FaceDetect extends SwingWorker {
    
    IntegralCalc3 ic3 = new IntegralCalc3();
    MainFrame mf = new MainFrame();
  
    Rectangle R;

       protected FaceDetect doInBackground() throws Exception {
       //Initial width and height is 60, 60
       outerloop:
       for(int w = 50; w <= ic3.integral.length && w <= ic3.integral[0].length; w = (int) Math.round(w*1.2)  ) {  
            int h = w;

            for(int x = 0; x <= ic3.integral.length-w; x+=5 ) { 
            for(int y = 0; y <= ic3.integral[0].length-w; y+=5 ) {
            
             R = new Rectangle (x, y, w, h);
             mf.lm.add(R);
             mf.lm.revalidate();
             mf.lm.repaint();
            } 
            }             
        } 
        return null;
    }

}

这是我的矩形类:

public class Rect extends JComponent {
    public int x;
    public int y;
    public int w;
    public int h;
    
    public Rect (int x, int y, int w, int h) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        repaint();
    }

    @Override
    public void paintComponent(Graphics g)  {
        Graphics2D g2 = (Graphics2D) g;
        super.paintComponent(g);
        g2.setColor(Color.green);
        g2.drawRect(x+15, y+15, w, h);
    }
}

最后是我在 JFrame 类中的按钮:

public class MainFrame extends JFrame {
    Rect R = new Rect(15, 15, 50, 50);
    JPanel lm = new JPanel();
    LayoutManager lay = new OverlayLayout(lm);
    JButton animate = new JButton("animate");

    public MainFrame () {
        setSize(1200, 700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        lm.setLayout(lay);
        lm.add(R);
}
        animate.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { 
               try {   
               new FaceDetect().execute();
                } catch (Exception ex) {
                    Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
               }   
      });

    public static void main(String[] args) {
            EventQueue.invokeLater(() -> new MainFrame());
        }
    }

但是当我点击animate 按钮时什么也没发生。我的代码缺陷在哪里?

【问题讨论】:

  • 必须事件调度线程 (EDT) 上调用repaint() 方法。方法 doInBackground() 确实在 EDT 上运行。参考Concurrency in Swing

标签: java swing awt draw


【解决方案1】:

我的代码的缺陷在哪里?

您在 main() 方法中创建 MainFrame 的一个实例:

public static void main(String[] args) {
        EventQueue.invokeLater(() -> new MainFrame());
    }
}

然后在 FaceDetect 类中创建另一个实例:

public class FaceDetect extends SwingWorker {
    IntegralCalc3 ic3 = new IntegralCalc3();
    MainFrame mf = new MainFrame();

你不能只是不断地创建一个对象的新实例。您需要将 MainFrame 类的引用传递给 FaceDetect 类。

但是我建议这仍然不是正确的设计,因为您的 SwingWorker 逻辑不正确。您错误地使用了 SwingWorker! doInBackground 逻辑不应该更新 Swing 组件的状态!

按照我之前的问题...

建议使用Swing Timer 制作动画。这就是你的代码应该如何设计的。 Swing Timer 所做的只是定期生成一个事件。然后您响应该事件。

因此,在您的情况下,您可能希望在 Rect 类中创建一个 move() 方法,该方法将更新 x/y 位置并 repaint() 组件。 Timer 的逻辑是简单地调用move() 方法。

有关使用摆动计时器的基本示例,请参阅:How to make JScrollPane (In BorderLayout, containing JPanel) smoothly autoscroll

【讨论】:

  • 所以我需要将我的FaceDetect类从实现Swing Worker改为Swing Timer,然后在for循环中调用move()方法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-22
  • 2014-05-20
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多