重涂不会更新屏幕 [英] Repaint does not update the screen

查看:127
本文介绍了重涂不会更新屏幕的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想重新粉刷我的屏幕.到目前为止,它所做的只是显示第一个屏幕,该屏幕上带有一个圆点,该圆点应该位于头部.很好,但是我已经在代码中编写了想要将磁头每秒向下移动10个像素的操作.我正在打印打印头应该位于的位置,并且在命令提示符下显示y值确实在增加.但是在我的屏幕上,头部没有动.

I would like to repaint my screen. As of now all it does is show the first screen with a dot where the head is supposed to be. This is fine, however I've written in my code that I want to move the head down 10 pixels every second. I'm printing at what point the head is supposed to be at, and in the command prompt it shows that the y value is indeed increasing. However on my screen the head is not moving.

我尝试使用revalidate方法,尝试扩展canvas类而不是jframe,我尝试仅针对paint方法使用不同的类,我尝试将paint方法替换为paintComponent方法.正如您可能会告诉我的那样,我对与Java绘画有关的任何内容都不太了解.我已经尝试阅读这些超类,但是它们太复杂了,以至于我无法理解.我还尝试了在没有睡眠声明的情况下运行.没关系.

I have tried using the revalidate method, trying to extend the canvas class instead of jframe, I have tried using different classes just for the paint method, i have tried replacing the paint method with the paintComponent method. And as you can probably tell I have a subpar understanding of anything related to painting in java. I have tried reading into these superclasses but they are too complicated for me to understand. I have also tried running without the sleep declaration. This didn't matter.

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

Main class: This class contains the main method to start running the snake game.

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更新头部的位置,并应该重新绘制屏幕.

AllGUIElements class: This class makes a new frame, containing a new panel. This panel is being painted using paintComponent. setGameScreen updates the position of the head and is supposed to repaint the screen.

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类:Snake和Food的父类(由于没有必要,因此不发布),该类返回头部,身体部位和食物的位置.

Screenobject class: Parent class of Snake and Food (which is not posted since it's not necessary) which returns the position of the head, bodypart and food.

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的子级,Head和Body的父级.此类设置对象的方向.

Snake class: Child of Screenobject and parent of Head and Body. This class sets the direction of the objects.

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

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

Head class: Child class of snake and is supposed to be the first object of a linked list for the snake.

import java.awt.*;

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

身体阶层:蛇的孩子阶层,每食用一种食物都会增长一次.

Body class: Child class of Snake and grows for every food eaten.

import java.awt.*;

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

}

推荐答案

第一件事:

我使用了分而治之"的方法来仅发布必要的信息

I have used the "Divide and conquer" method to only post the necessary information

是的,您仅发布了必要的信息,但对于 MCVE/MRE

Yes, you posted only the necessary information, but you're still using too many classes for a MCVE / MRE, ideally all your code would fit in a single class.

在没有省略代码使其运行的情况下,我没有设法使其变得更短.

I didn't manage to make it any shorter without omitting code to make it run.

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

That's the idea about us asking for an MRE, you should create a brand new program to isolate the problem, in your case this is: Move a shape on a certain direction and keep moving on that direction.

现在,我可以看到您已经尝试创建它并进行改进,因此,我将发布一个示例,说明您将来对您的问题的期望,以便您将来提出更好的问题.

Right now, I can see that you've tried creating it, and to improve, so I will post an example of what is expected in future questions of your so that you ask better questions in the future.

话虽这么说,让我们去回答你的问题.

That being said, let's go and answer your question.

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

This is an example with less than 100 lines of code (not including comments) that addresses the following issues in your code:

  1. 删除while(true)语句以支持Swing计时器,以防止阻塞事件调度线程(EDT)

  1. Removes the while(true) statement in favor of a Swing Timer to prevent blocking the Event Dispatch Thread (EDT)

将程序放在EDT上,请参见

Places the program on the EDT, see point #2 of this answer

使用JFrame#pack()方法,而不是同时为JFrameJPanel

Makes use of JFrame#pack() method instead of manually setting it with setSize(...) for both the JFrame and JPanel

摆脱了指示的幻数" 并为此使用了enum,因此代码更易读,因为我们都知道TOP应该将其移到顶部,但我们不知道1应该将其移到顶部.

Gets rid of the "magic numbers" for the directions and uses an enum for this matter, and thus the code is more readable as we all know that TOP should move it to top, but we don't know that 1 should move it to top.

使用Shape API在JPanel上绘制形状,如

Makes use of the Shape API to draw the shapes on the JPanel as shown in this answer

我还建议您使用camelCase命名您的方法,以使rungame()变为runGame(),因为它更易于阅读,与其他方法一样.并给它们起更有意义的名称,例如hoofd,我不知道那是什么,如果我独自阅读而没有上下文,将很难说出它是什么类型的对象.

I would also suggest naming your methods using camelCase so that rungame() becomes runGame() as it's easier to read, same for other methods. And give them more meaningful names such as hoofd, I don't know what that is and if I read it alone without context it would be extremely difficult to say what type of object it is.


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表达式或对您来说太困难,上面的代码会产生以下结果,请检查此答案的第二点,它显示了如何放置程序在没有Lambda表达式和此答案展示了如何在没有它们的情况下也写ActionListener.

The above code produces the following result, for Java 7 and below or if you don't want to use lambda expressions or are too difficult for you, check the point #2 on this answer, it shows how to place the program on the EDT without lambda expressions and this answer shows how to write the ActionListener without them as well.

您去了,这是一个简单的可运行示例,它是完整的,可验证的,最小的,这就是我们在以后的问题中对您的期望.

There you go, a simple runnable example, it's complete, it's verifiable, it's minimal and that's what we expect from you in future questions.

这篇关于重涂不会更新屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆