当前位置: 首页 > 知识库问答 >
问题:

重新绘制不会更新屏幕

齐夕
2023-03-14

我想重新绘制我的屏幕。到目前为止,它所做的只是在第一个屏幕上的头部应该在的地方显示一个点。这很好,但是我在代码中写了我想每秒将头部向下移动10个像素。我正在打印头部应该在的位置,在命令提示符中它显示y值确实在增加。但是在我的屏幕上,头部没有移动。

我尝试过使用revalidate方法,尝试扩展canvas类而不是jframe,我尝试过只为paint方法使用不同的类,我尝试过用paintComponent方法替换paint方法。正如你可能知道的那样,我对任何与java绘画相关的东西都不太了解。我试着读懂这些超类,但它们太复杂了,我无法理解。我也试过在没有睡眠声明的情况下跑步。这无关紧要。

主类:此类包含开始运行蛇游戏的主方法。

import java.util.concurrent.TimeUnit;

public class Main{

    public static void main(String[] args) throws InterruptedException {
        Main programma = new Main();
        programma.rungame();
    }

void rungame() throws InterruptedException {
        AllGUIElements gui = new AllGUIElements();
        gui.gui();
        while (true) {
            TimeUnit.SECONDS.sleep(1);
            gui.setGameScreen();
        }
    }
}

AllGUIElements类:该类生成一个新框架,包含一个新面板。正在使用paintComponent对该面板进行喷漆。setGameScreen更新头部的位置,并应重新绘制屏幕。

import javax.swing.*;
import java.awt.*;

public class AllGUIElements extends JPanel {

    private JFrame frame;
    private JPanel panel;

    private int screen;

    Snake hoofd = new Head(new Point(30,30),3,null);

    void gui() throws InterruptedException {

        frame = new JFrame("Snake Game");
        panel = new AllGUIElements();
        panel.setBackground(Color.GRAY);
        panel.setSize(1000,500);
        frame.setSize(1000,500);
        frame.add(panel);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

void setGameScreen() {
    repaint();
        if (hoofd.getDirection() == 1) {
            hoofd.setPosition(new Point(hoofd.getPosition().x, hoofd.getPosition().y-10));
        }
        if (hoofd.getDirection() == 2) {
            hoofd.setPosition(new Point(hoofd.getPosition().x+10, hoofd.getPosition().y));

        }
        if (hoofd.getDirection() == 3) {
            hoofd.setPosition(new Point(hoofd.getPosition().x, hoofd.getPosition().y+10));

        }
        if (hoofd.getDirection() == 4) {
            hoofd.setPosition(new Point(hoofd.getPosition().x-10, hoofd.getPosition().y));
        }
}

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
            g.setColor(Color.DARK_GRAY);
            g.fillRect(0, 0, 1000, 10);
            g.fillRect(0, 0, 10, 500);
            g.fillRect(990, 0, 10, 500);
            g.fillRect(0, 490, 1000, 10);

            g.setColor(Color.GREEN);
            g.fillRect(hoofd.getPosition().x, hoofd.getPosition().y, 10, 10);
    }

}

Screenobject类:蛇和食物的父类(由于没有必要,所以没有发布),返回头部、身体部位和食物的位置。

import java.awt.*;

public class Screenobject{

    Point pos;

    Screenobject(Point pos){
        this.pos = pos;
    }

    void setPosition(Point pos){
        this.pos = pos;
    }

    Point getPosition() {
        return pos;
    }
}

蛇类:Screenobject的子类,头部和身体的父类。此类设置对象的方向。

import java.awt.*;

public class Snake extends Screenobject{

    int direction;
    //directions:
    //1 = up
    //2 = right
    //3 = down
    //4 = left

    Snake nextpart;

    Snake(Point pos, int direction, Snake nextpart){
        super(pos);
        this.direction = direction;
        this.nextpart = nextpart;
    }

    int getDirection() {
        return direction;
    }
}

头类:蛇的子类,应该是蛇链接列表的第一个对象。

import java.awt.*;

public class Head extends Snake{
    Head(Point pos, int direction, Snake nextpart){
        super(pos, direction, nextpart);
    }
}

身体类:蛇的子类,每吃一种食物就会生长。

import java.awt.*;

public class Body extends Snake{
    Body(Point pos, int direction, Snake nextpart){
        super(pos, direction, nextpart);
    }

}

共有1个答案

丌官积厚
2023-03-14

第一件事:

我使用“分而治之”的方法只发布必要的信息

是的,您只发布了必要的信息,但您仍然为MCVE/MRE使用了太多的类,理想情况下,您的所有代码都适合一个类。

我没能在不省略代码的情况下将其缩短。

这就是我们要求MRE的想法,你应该创建一个全新的程序来隔离问题,在你的情况下,这是:在某个方向上移动一个形状,并继续朝那个方向移动。

现在,我可以看到你已经尝试创建它,并加以改进,所以我将发布一个例子,说明你未来的问题中需要什么,以便你在未来提出更好的问题。

话虽如此,让我们去回答你的问题。

这是一个不到100行代码(不包括注释)的示例,它解决了代码中的以下问题:

>

  • 删除while(true)语句以支持Swing Timer以防止阻塞事件调度线程(EDT)

    将程序放在EDT上,参见本答案的第2点

    使用JFrame#pack()方法,而不是使用setSize(...)JFrameJBoard手动设置它

    去掉方向的“幻数”,并为此使用枚举,因此代码更可读,因为我们都知道顶部应该移动到顶部,但我们不知道1应该移动到顶部。

    使用图形API在JPanel上绘制图形,如本答案所示

    我还建议您使用camelCase来命名方法,这样,与其他方法一样,rungame()变得更易于阅读。给他们起个更有意义的名字,比如hoofd,我不知道那是什么,如果我没有语境单独阅读,就很难说出它是什么类型的物体。

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionListener;
    import java.awt.geom.Rectangle2D;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    public class SnakeGame {
        private JFrame frame;
        private Snake snake;
        private JPanel buttonsPane;
        private JButton[] buttons; // Our array of buttons
        private Timer timer;
        private Direction currentDirection;
    
        // This enum will be used to determine the direction the snake will take.
        private enum Direction {
            TOP, LEFT, BOTTOM, RIGHT
        }
    
        public static void main(String[] args) {
            // We place our program on the EDT using Java 8 lambda expressions.
            SwingUtilities.invokeLater(() -> new SnakeGame().createAndShowGUI());
        }
    
        private void createAndShowGUI() {
            frame = new JFrame(getClass().getSimpleName());
            snake = new Snake();
            buttonsPane = new JPanel();
            buttons = new JButton[Direction.values().length];
    
            for (int i = 0; i < buttons.length; i++) {
                buttons[i] = new JButton(Direction.values()[i].toString()); // We create a JButton with the current value of the direction
                buttons[i].addActionListener(listener); // We set their ActionListeners
                buttonsPane.add(buttons[i]); // And add them to the buttonsPane
            }
    
            currentDirection = Direction.RIGHT; // We set a default direction
            timer = new Timer(1000, listener); // And start our Swing Timer
    
            // We add our components and then pack the frame, after that we start the timer.
            frame.add(snake);
            frame.add(buttonsPane, BorderLayout.SOUTH);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
            timer.start();
        }
    
        // Our ActionListener for moving the snake
        private ActionListener listener = e -> { // Using Java 8 lambda expressions again
            // We set the current direction using a ternary, if the source of the event is
            // the timer we leave the current direction as is
            // otherwise we set it to the direction from the button clicked
            currentDirection = e.getSource().equals(timer) ? currentDirection : Direction.valueOf(e.getActionCommand());
            snake.move(currentDirection); // And we call the move method
        };
    
        @SuppressWarnings("serial")
        class Snake extends JPanel {
            private int xPos;
            private int yPos;
            private static final int SPEED = 10; // We set the speed as a constant (10 pixels at a time) in any direction
    
            // We determine the movement direction
            public void move(Direction direction) {
                switch (direction) {
                case TOP:
                    yPos -= SPEED;
                    break;
                case LEFT:
                    xPos -= SPEED;
                    break;
                case BOTTOM:
                    yPos += SPEED;
                    break;
                case RIGHT:
                    xPos += SPEED;
                    break;
                }
                this.repaint(); // And repaint the snake
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                g2d.setColor(Color.DARK_GRAY);
                g2d.fill(new Rectangle2D.Double(xPos, yPos, 10, 10)); // We draw a rectangle using the Shape API
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(300, 300); // We set the preferredSize of the JPanel to 300 x 300
            }
        }
    }
    

    以上代码生成以下结果,对于Java 7及以下版本,或者如果您不想使用lambda表达式或对您来说太难,请检查此答案上的第2点,它显示了如何在没有lambda表达式的情况下将程序放置在EDT上,此答案还显示了如何在没有lambda表达式的情况下编写ActionListener。

    好了,一个简单的可运行示例,它是完整的、可验证的、最小的,这就是我们在未来的问题中对您的期望。

    https://imgs.xnip.cn/cj/n/18/85a0abe9-7bf6-4f3c-8cda-6bc819bb9a69.gif" width="100%" height="100%" />

  •  类似资料:
    • 编辑:当我拖动边框时,某种刷新被发送,我需要弄清楚并手动发送相同的刷新。 请注意,我已经尝试使用revalidate()和repaint()。 当使用JFrame和JPanel来显示一个框架时,我试图使框架的大小易于改变。 null 此外,一个重要的注意事项是,当您拖动边框时,白条会消失,并正确地重新加载/刷新内容(即使您只是少量拖动)

    • 在用JFrame编写一个简单的Tron游戏时,我遇到了一点问题。我不确定我如何能保持显示为“光循环”作为一条线停留在屏幕上显示,但同时重新油漆屏幕,使移动仍然有效。以下是我的代码:(仅供参考,它全部编译,所有工作都像它在这里写的一样完美) 另外,我想知道,当我按下一个箭头键时,它将如何使它,而不是只从x或y键中添加或减去一次,而是不断地添加到x或y键,直到按下另一个箭头。 顺便说一句,如果有人想知

    • 问题内容: 我是Java的初学者,我正在尝试创建一个在光标所在的位置绘制一个矩形的应用程序。我已经完成了所有操作,但无法获得重绘的信息。如果没有重绘,则矩形仅绘制一次,仅此而已。使用重新绘制,它可以很好地编译,但是当我运行它时,每次移动鼠标时,都会出现这个大错误。 所以,有人可以帮我吗? 问题答案: 希望代码示例中的注释能够告诉您您在代码中做错了什么:-),否则总有理由提出您的疑问…

    • 问题内容: 我在程序开始时,根据数据库中的某些内容,以编程方式在JScrollPane中添加了许多组件(JPanels,JLabels等)。 似乎对于GUI(?)而言,此过程太快了,因此JScrollPane并不总是正确更新,即,即使内部JPanel大于可见区域,滚动条也不可见。 调整窗口大小(JFrame)可以解决此问题,因为我认为Java在调整组件大小时会重新打印它们。 作为测试,我添加了一个

    • 我有一个正在添加JLabel的JPanel。然后我想删除所有的JLabel并添加一些新的。 所以我做了以下几点: 这很好。当我在这之后开始一个新线程时,我的问题就出现了,比如: 然后原始JLabels的输出仍然可见。我读到重新验证过程是一个长时间运行的任务,因此firstProducer线程正在启动,而重新验证正在进行并产生冲突。处理这个问题的最佳方法是什么?

    • 我试图在if语句中设置状态,但它不会这样做。结果,我需要从if语句中更新状态,在那个里我可以接收经度和纬度坐标,但它不会保存状态。若我在If语句之外的控制台中回显,我将只读取第一个setState值表单componentWillMount。有什么问题吗?我做错了什么?结构如下: