简体   繁体   English

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

[英]why Java repaint() method not working?

The code below is a very simple test which involves an image.下面的代码是一个非常简单的测试,它涉及一个图像。 It should repaint an image whenever I send "a" to System.in and it should exit the program whenever I send "q".每当我向 System.in 发送“a”时它应该重新绘制图像,并且每当我发送“q”时它应该退出程序。

The problem is that only the exit works: the method paint() is never called, and I don't why.问题是只有退出有效:从未调用方法paint(),我不知道为什么。

I checked for the call to "super.paint()", tried replacing paint(Graphics g) with paintCompoenent(Graphics g) but nothing seems to work: simply there's no call.我检查了对“super.paint()”的调用,尝试用paintCompoenent(Graphics g) 替换paint(Graphics g) 但似乎没有任何效果:根本就没有调用。

Is the problem involving the Scanner in main()?问题是否涉及 main() 中的 Scanner?

The path in the program is not the same I used, and the first paint is right, so the problem shouldn't be there.程序中的路径和我用的不一样,而且第一次绘制是对的,所以问题应该不存在。

NB if it's useful, I'm using Eclipse Oxygen and Java9 SE注意,如果有用,我正在使用 Eclipse Oxygen 和 Java9 SE

Thanks to all!谢谢大家!

code paste:代码粘贴:

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

So, there's a few mistakes...所以,有几个错误......

Let's start here...让我们从这里开始......

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

You're creating two instances of TestImagePanel and you're only updating the instance which is not on the screen您正在创建TestImagePanel两个实例,并且您只更新不在屏幕上的实例

Something like...就像是...

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

//...

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

will help.会有所帮助。

Next...下一个...

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

Okay, you should avoid overriding paint , as a general preference it's recommend to override paintComponent instead.好的,您应该避免覆盖paint ,作为一般偏好,建议改为覆盖paintComponent

Because painting can occur at any time for any number of reasons, you should never update or modify the state of the UI from within paint methods, painting is for painting the current state因为绘画可以因多种原因在任何时候发生,你永远不应该从绘画方法中更新或修改 UI 的状态,绘画是为了绘制当前状态

So, it should be something more like...所以,它应该更像是……

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

Okay, so, then how do we update the xpos and ypos values?好的,那么我们如何更新xposypos值呢? In your case, the update method is probably the obvious choice....在您的情况下, 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);
    }
}

Now, this raises an issue.现在,这提出了一个问题。 xpos and ypos are needed by the paintComponent method, this means that these values should not be updated outside the context of the Event Dispatching Thread xposypos由所需paintComponent方法,这意味着这些值不应事件指派线程的上下文之外被更新

A simple fix might be to do something like...一个简单的解决方法可能是做一些像......

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

This ensures that the contents of the update method is executed within the context of the EDT.这确保了update方法的内容在 EDT 的上下文中执行。

This, IMHO, is kind of a mess.恕我直言,这有点混乱。 A better solution would be to use a SwingWorker更好的解决方案是使用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);
        }
    }

};

This takes care of putting the updates onto the EDT for us.这负责为我们将更新放到 EDT 上。

This generates a solution which looks something like this...这会生成一个看起来像这样的解决方案......

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

        };

    }
}

Now, the question is, why are you taking input from the console in a GUI program?现在,问题是,为什么要在 GUI 程序中从控制台获取输入? You should be inputing data via the GUI?您应该通过 GUI 输入数据吗? The above might be a good solution for reading content from a file or other automated source, but should be avoid for user input ... this isn't how GUIs are suppose to work.以上可能是从文件或其他自动化源读取内容的一个很好的解决方案,但应该避免用户输入......这不是 GUI 应该如何工作。

First, you should not override the paint() method;首先,您不应该覆盖paint() 方法; you shoul;d override paintComponent() instead.你应该改写paintComponent()。

Second, you are not doing this on the EventDispatchThread.其次,您不是在 EventDispatchThread 上执行此操作。 And even if you were, putting the updates in a loop will block the event dispatch thread until the loop ends, resulting in only one final repaint.即使是这样,将更新放入循环中也会阻塞事件调度线程,直到循环结束,导致最后只进行一次重绘。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM