【问题标题】:why Java repaint() method not working?为什么 Java repaint() 方法不起作用?
【发布时间】:2018-03-28 19:43:08
【问题描述】:

下面的代码是一个涉及图像的非常简单的测试。 每当我向 System.in 发送“a”时它应该重新绘制图像,并且每当我发送“q”时它应该退出程序。

问题是只有出口有效: 永远不会调用paint() 方法,我不知道为什么。

我检查了对“super.paint()”的调用,尝试用paintCompoenent(Graphics g) 替换paint(Graphics g),但似乎没有任何效果:根本没有调用。

问题是否涉及 main() 中的 Scanner?

程序中的路径和我用的不一样,而且第一次绘制是对的,所以应该没有问题。

注意,如果有用的话,我正在使用 Eclipse Oxygen 和 Java9 SE

谢谢大家!

代码粘贴:

public class TestImagePanel extends JPanel {

    private BufferedImage image;
    private int xpos = 0;
    private int ypos = 0;
    private String _imagePath = "//myFolder//image.png";

    public TestImagePanel() {
        try {
            image = ImageIO.read(new File(_imagePath));
        } catch (IOException ex) {}
    }

    public void paint(Graphics g) {
        super.paint(g);
        System.out.println("painting LOG");
        g.drawImage(image, this.xpos++, this.ypos++, this);
    }

    public void update(String a) {
        System.out.print("Receiving:" + a + "---" + xpos + ":" + ypos);
        if (a.equals("a"))
            repaint();
        else if (a.equals("q")) {
            System.out.println("LOGOUT");
            System.exit(0);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("test");
        TestImagePanel testimg = new TestImagePanel();
        frame.add(new TestImagePanel());
        frame.setSize(new Dimension(600, 600));
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Scanner in = new Scanner(System.in);
        while (true)
            testimg.update( in .next());
    }
}

【问题讨论】:

  • 我刚刚测试了您的代码,没有任何问题。你期望发生的事情没有发生?从你的问题看不清楚。 repaint() 方法被调用,它什么也不做,因为没有什么可做的。
  • 您还调用了两次new TestImagePanel() - 一次将其分配给testimg,然后将另一个添加到frame
  • 我希望在新的 xpos 和 ypos 上重新绘制图像,他们应该得到一个 ++
  • 另外,请参阅this question - 你是 tying up your GUI's event thread for console input - 不要使用扫描仪为你的 GUI 应用程序输入。您应该关注的关键信息是 swing 的“事件调度线程”的概念 - 值得一读的是 this question
  • “我希望在新的 xpos 和 ypos 上重新绘制图像,它们应该得到一个 ++” - 您正在更新 testimg 的一个实例,它是不在屏幕上

标签: java swing paint repaint


【解决方案1】:

所以,有一些错误......

让我们从这里开始......

JFrame frame = new JFrame("test");
TestImagePanel testimg = new TestImagePanel();
frame.add(new TestImagePanel());

//...

Scanner in = new Scanner(System.in);
while (true)
    testimg.update( in .next());

您正在创建 TestImagePanel 的两个实例,并且您只是在更新不在屏幕上的实例

类似...

JFrame frame = new JFrame("test");
TestImagePanel testimg = new TestImagePanel();
frame.add(testimg);

//...

Scanner in = new Scanner(System.in);
while (true)
    testimg.update( in .next());

会有帮助。

下一步...

public void paint(Graphics g) {
    super.paint(g);
    System.out.println("painting LOG");
    g.drawImage(image, this.xpos++, this.ypos++, this);
}

好的,您应该避免覆盖paint,作为一般偏好,建议改为覆盖paintComponent

由于任何原因都可以随时进行绘制,因此您永远不应该在绘制方法中更新或修改 UI 的状态,绘制是为了绘制当前状态

所以,它应该更像...

protected void paintComponent(Graphics g) {
    super.paint(g);
    g.drawImage(image, this.xpos, this.ypos, this);
}

好的,那么我们如何更新 xposypos 值?在您的情况下,update 方法可能是显而易见的选择......

public void update(String a) {
    xpos++;
    ypos++;
    System.out.print("Receiving:" + a + "---" + xpos + ":" + ypos);
    if (a.equals("a"))
        repaint();
    else if (a.equals("q")) {
        System.out.println("LOGOUT");
        System.exit(0);
    }
}

现在,这引发了一个问题。 xposypospaintComponent 方法所需要的,这意味着不应在事件调度线程的上下文之外更新这些值

一个简单的解决办法可能是做类似...

public void update(String a) {
    if (!EventQueue.isDispatchThread()) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                update(a);
            }
        });
    }
    xpos++;
    ypos++;
    System.out.print("Receiving:" + a + "---" + xpos + ":" + ypos);
    if (a.equals("a")) {
        repaint();
    } else if (a.equals("q")) {
        System.out.println("LOGOUT");
        System.exit(0);
    }
}

这可确保 update 方法的内容在 EDT 的上下文中执行。

恕我直言,这有点乱。更好的解决方案是使用SwingWorker

SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
    @Override
    protected Void doInBackground() throws Exception {
        Scanner in = new Scanner(System.in);
        while (true) {
            publish(in.next());
        }
    }

    @Override
    protected void process(List<String> chunks) {
        for (String text : chunks) {
            testimg.update(text);
        }
    }

};

这会为我们将更新放到 EDT 上。

这会生成一个看起来像这样的解决方案...

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

public class TestImagePanel extends JPanel {

    private BufferedImage image;
    private int xpos = 0;
    private int ypos = 0;
    private String _imagePath = "//myFolder//image.png";

    public TestImagePanel() {
        try {
            image = ImageIO.read(new File(_imagePath));
        } catch (IOException ex) {
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        System.out.println("painting LOG");
        g.drawImage(image, this.xpos, this.ypos, this);
    }

    public void update(String a) {
        System.out.print("Receiving:" + a + "---" + xpos + ":" + ypos);
        if (a.equals("a")) {
            xpos++;
            ypos++;
            repaint();
        } else if (a.equals("q")) {
            System.out.println("LOGOUT");
            System.exit(0);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("test");
        TestImagePanel testimg = new TestImagePanel();
        frame.add(new TestImagePanel());
        frame.setSize(new Dimension(600, 600));
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
            @Override
            protected Void doInBackground() throws Exception {
                Scanner in = new Scanner(System.in);
                while (true) {
                    publish(in.next());
                }
            }

            @Override
            protected void process(List<String> chunks) {
                for (String text : chunks) {
                    testimg.update(text);
                }
            }

        };

    }
}

现在,问题是,您为什么要在 GUI 程序中从控制台获取输入?您应该通过 GUI 输入数据吗?以上可能是从文件或其他自动源读取内容的好解决方案,但应该避免用户输入......这不是 GUI 的工作方式。

【讨论】:

  • protected void paintComponent(Graphics g) { super.paint(g); 在两个地方应该是 protected void paintComponent(Graphics g) { super.paintComponent(g);。除此之外,很好的答案。
  • ?‍♂️复制粘贴
  • 非常感谢您的完整,这真的很有帮助
【解决方案2】:

首先,你不应该重写paint()方法;你应该重写paintComponent()。

其次,您没有在 EventDispatchThread 上执行此操作。即使你是这样,将更新放在一个循环中也会阻塞事件调度线程,直到循环结束,导致最后一次重绘。

【讨论】:

  • repaint 是线程安全的 - 但您可能是正确的,他们可能正在向 EDT 发送垃圾邮件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
  • 1970-01-01
  • 2013-12-13
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多