【问题标题】:Repaint JPanel from inside loop从内部循环重绘 JPanel
【发布时间】:2014-01-08 10:26:29
【问题描述】:

是否可以从另一个对象的循环中重新绘制 JPanel?我有一个由 JPanel (DrawPanel) 和 SA 对象组成的 JFrame。我想在这个 SA 对象的 while 循环期间更新/重绘 JPanel。我启动了一个新线程,但 panel.repaint() 仍然没有执行。

public class Mainform extends JFrame {
    private DrawPanel DrawPanel;
    public static void main(String[] args) {
        DrawPanel panel = new DrawPanel();
        SA sa = new SA(panel);
        Thread t = new Thread(sa);
        t.start();
        //...
    }
}
public class DrawPanel extends JPanel implements MouseMotionListener, MouseListener {
    public DrawPanel() {
        super();
        setBackground(Color.WHITE);
        addMouseWheelListener(this);
        addMouseListener(this);
        addMouseMotionListener(this);
    }
    //...
}
public class SA implements Runnable {
    private DrawPanel panel;
    public SA(DrawPanel p) {
        this.panel = p;
        init();
    }
    public void run() {
        while (true) {
            //...
            panel.repaint();
        }
    }
}

编辑:运行是公开的

【问题讨论】:

  • run 不能是私有的 - 但基本上,是的......这假设您尝试重绘的面板已添加到容器中,该容器附加到本机对等点并且是可见的。 ..
  • 尝试使面板无效,而不是调用 repaint()。
  • @jtmon 使组件无效将标记布局,一旦布局,将重新绘制...

标签: java swing repaint


【解决方案1】:

基本答案是“是”。

这假设您尝试重绘的组件是

  1. 添加到容器中
  2. 该容器附加到某种本地对等点(即窗口)
  3. 那个窗口是可见的。

RepaintManager 通常足够聪明,知道不会浪费时间绘制无法显示的东西。

以下示例相当基本,但每次调用时都会在JPanelpaintComponent 内增加一个计数器。附加到ThreadRunnable 将每秒更新一次...

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RepaintTest {

    public static void main(String[] args) {
        new RepaintTest();
    }

    public RepaintTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                TestPane tp = new TestPane();
                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(tp);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Thread thread = new Thread(new Repainter(tp));
                thread.setDaemon(true);
                thread.start();
            }
        });
    }

    public class Repainter implements Runnable {

        private JPanel panel;

        public Repainter(JPanel panel) {
            this.panel = panel;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                panel.repaint();
            }
        }

    }

    public class TestPane extends JPanel {

        private int repaints = 0;

        public TestPane() {
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics g2d = (Graphics2D) g.create();
            repaints++;
            FontMetrics fm = g2d.getFontMetrics();
            String text = Integer.toString(repaints);
            int x = (getWidth() - fm.stringWidth(text)) / 2;
            int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, x, y);
            g2d.dispose();
        }

    }

}

【讨论】:

  • 我认为在摆动线程之外调用重绘是一种糟糕的做法。我会使用 swing-timer 或至少使用 SwingUtilities.invokeLater 来做同样的事情
  • @MadProgrammer please did you read,我仍然认为 repaint() 在(因为我看到)JDK7(在您的代码示例中使用)不是线程安全的,肯定它可能是无穷无尽的讨论
  • @mKorbel 我不确定 Java 7,但假设 RepaintManager 正在将重绘调度到事件队列上,怎么可能不......?
  • @SergiyMedvynskyy repaint 向 RepaintManager 发出请求,RepaintManager 将 repaint 事件发布到由 EDT 处理的事件队列中,以便在 EDT 的上下文中完成实际的绘制
  • @SergiyMedvynskyy 10 次中有 9 次我更喜欢使用 Swing Timer,但问题与 Thread 有关,所以我遵循了这条逻辑。此外,即使有 1 个案例,我也会尝试看看如何让它使用 Swing Timer :P
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多