为什么Java repaint()方法不起作用?

为什么Java repaint()方法不起作用?,java,swing,paint,repaint,Java,Swing,Paint,Repaint,下面的代码是一个非常简单的测试,它涉及一个图像。 每当我向System.in发送时,它应该重新绘制图像,每当我发送q时,它应该退出程序 问题是只有出口起作用: 这个方法从未被调用,我也不知道为什么 我检查了对super.paint的调用,尝试用PaintComponentGraphics g替换paintGraphics g,但似乎没有任何效果:根本没有调用 问题是否主要涉及扫描仪 程序中的路径与我使用的不同,并且第一个绘制是正确的,因此问题不应该存在 注意:如果有用的话,我使用的是Eclips

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

问题是只有出口起作用: 这个方法从未被调用,我也不知道为什么

我检查了对super.paint的调用,尝试用PaintComponentGraphics g替换paintGraphics g,但似乎没有任何效果:根本没有调用

问题是否主要涉及扫描仪

程序中的路径与我使用的不同,并且第一个绘制是正确的,因此问题不应该存在

注意:如果有用的话,我使用的是EclipseOxygen和Java9SE

谢谢大家

代码粘贴:

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());
    }
}

首先,不应覆盖绘制方法;你应该;d替换为覆盖油漆组件


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

首先,不应覆盖绘制方法;你应该;d替换为覆盖油漆组件


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

因此,有一些错误

让我们从这里开始

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 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);
    }
}
会有帮助的

接下来

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);
}
好的,那么我们如何更新xpos和ypos值呢?在您的情况下,更新方法可能是显而易见的选择

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);
    }
}
现在,这引发了一个问题。paintComponent方法需要XPO和YPO,这意味着不应在事件调度线程的上下文之外更新这些值

一个简单的解决方法可能是做一些像

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 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

这将为我们将更新放在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的工作原理。

因此,有一些错误

让我们从这里开始

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 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);
    }
}
会有帮助的

接下来

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);
}
好的,那么我们如何更新xpos和ypos值呢?在您的情况下,更新方法可能是显而易见的选择

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);
    }
}
现在,这引发了一个问题。paintComponent方法需要XPO和YPO,这意味着不应在事件调度线程的上下文之外更新这些值

一个简单的解决方法可能是做一些像

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 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

这将为我们将更新放在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的工作原理。

我刚刚测试了您的代码,没有任何问题。你期望发生什么事而没有发生?你的问题不清楚。重新绘制方法被调用,它只是不做任何事情,因为没有什么事情要做。您还调用了两次new TestImagePanel-一次,您将其分配给testimg,然后将另一个添加到框架中我希望图像被重新绘制
在一个新的xpos和YPO上,他们也应该得到一个++,请看-您正在使用GUI的事件线程进行控制台输入-不要使用扫描仪进行GUI应用程序的输入。您应该关注的关键信息是swing的事件调度线程的概念-一个好的解读是我希望在新的xpos和YPO上重新绘制图像,他们应该得到一个++-您正在更新一个不在屏幕上的testimg实例我刚刚测试了您的代码,没有问题。你期望发生什么事而没有发生?你的问题不清楚。调用了repaint方法,它只是不做任何事情,因为没有什么要做。您还调用了两次new TestImagePanel-一次将其分配给testimg,然后将另一个添加到框架中我希望在新的xpos和YPO上重新绘制图像,他们还应该得到一个+,请参阅-您正在为控制台输入绑定GUI的事件线程-不要使用扫描仪为GUI应用程序输入。您应该关注的关键信息是swing事件调度线程的概念-一个好的解读是,我希望在新的xpos和YPO上重新绘制图像,他们应该得到一个++-你正在更新一个不在屏幕上的testimg实例Repait是线程安全的-但是你可能是对的,他们可能在滥发EdtRepait是线程安全的-但是你可能是对的,他们可能在滥发EdtPaintComponentGraphics{super.paintg;应该在两个位置保护void paintComponentGraphics g{super.paintComponentg;{super.paintComponentg;;否则,回答很好。非常感谢您的完整性,这真的很有帮助保护void paintComponentGraphics g{super.paintg;应该保护void paintComponentGraphics g{super.paintcomponent;在两个地方。除此之外,回答得很好。非常感谢您的完整性,这真的很有帮助