无法重新绘制我的JFrame / JPanel [英] Can't repaint my JFrame/JPanel

查看:95
本文介绍了无法重新绘制我的JFrame / JPanel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个只在屏幕上移动球的程序。我曾经把它全部放在一个班级,但我认为它看起来太乱了,所以我把它分成三个不同的类:Main ...初始化一切,Game ...画出一切,是JPanel,AL是一个KeyListener(也是问题所在)。问题是,无论我尝试传递给它,我都无法从我的AL类重新编写程序。有人能帮忙吗?这是我的三个类:

I have created a program that just moves a ball across a screen. I used to have it all in one class, but decided that it looked too messy so I split it up into three different classes: Main... initializes everything, Game... which paints everything and is a JPanel, and AL which is a KeyListener (which is also where the problem is). The problem is that I can't get the program to repaint from my AL class no matter what I try to pass into it. Can anyone help with this? Here are my three classes:

import java.awt.Color;

import javax.swing.JFrame;

public class Main {
    static Game game;
    static JFrame frame;

public static void main(String[] args) {
    game = new Game();
    frame = new JFrame();

    frame.getContentPane().add(game);
    frame.addKeyListener(new AL(game, frame));
    frame.setTitle("Game");
    frame.setSize(500, 500);
    frame.setResizable(true);
    frame.setVisible(true);
    frame.setBackground(Color.BLACK);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

-

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game extends JPanel implements Runnable {
    int x, y, xCoord, yCoord;
    private Image dbImage;
    private Graphics dbg;
    JFrame frame;

public void changeCoord() {
    x += xCoord;
    y += yCoord;
    if (x <= 20) {
        x = 20;
    }
    if (x >= 480) {
        x = 480;
    }
    if (y <= 40) {
        y = 40;
    }
    if (y >= 480) {
        y = 480;
    }
}

public void setXCoord(int xcoord) {
    xCoord = xcoord;
}

public void setYCoord(int ycoord) {
    yCoord = ycoord;
}

public static void main(String[] args) {
    Game game = new Game();
    Thread t = new Thread(game);
    t.start();
}

public Game() {
    x = 250;
    y = 250;

}

@Override
public void paintComponent(Graphics g) {
    g.setColor(Color.GREEN);
    g.fillOval(x, y, 15, 15);
}

@Override
public void paint(Graphics g) {
    dbImage = createImage(getWidth(), getHeight());
    dbg = dbImage.getGraphics();
    paintComponent(dbg);
    g.drawImage(dbImage, 0, 0, this);
}

@Override
public void run() {
    try {
        while (true) {
            changeCoord();
            Thread.sleep(30);
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

}

-

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class AL extends KeyAdapter {
    Game game;
    JFrame frame;

public AL(Game game, JFrame frame) {
    this.game = game;
    this.frame = frame;
}

@Override
public void keyPressed(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode == e.VK_LEFT) {
        game.setXCoord(-1);
    }
    if (keyCode == e.VK_RIGHT) {
        game.setXCoord(+1);
    }
    if (keyCode == e.VK_UP) {
        game.setYCoord(-1);
    }
    if (keyCode == e.VK_DOWN) {
        game.setYCoord(+1);
    }
    game.repaint();
}

@Override
public void keyReleased(KeyEvent e) {
    int keyCode = e.getKeyCode();
    if (keyCode == e.VK_LEFT) {
        game.setXCoord(0);
    }
    if (keyCode == e.VK_RIGHT) {
        game.setXCoord(0);
    }
    if (keyCode == e.VK_UP) {
        game.setYCoord(0);
    }
    if (keyCode == e.VK_DOWN) {
        game.setYCoord(0);
    }
    game.repaint();

}

}


推荐答案

让我们从明显的开始......

Let's start with the obvious....

这是有问题的...

@Override
public void paintComponent(Graphics g) {
    g.setColor(Color.GREEN);
    g.fillOval(x, y, 15, 15);
}

@Override
public void paint(Graphics g) {
    dbImage = createImage(getWidth(), getHeight());
    dbg = dbImage.getGraphics();
    paintComponent(dbg);
    g.drawImage(dbImage, 0, 0, this);
}

没有必要在Swing组件中实现双缓冲,它们已经存在。另外,你打破了绘画合约,不要调用绘画方法超级方法

There's no need to implement double buffering in Swing components, they already are. Also, you're breaking the painting contract, by not call the paint methods super methods

整个事情应该是......

The whole thing should be...

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(Color.GREEN);
    g.fillOval(x, y, 15, 15);
}

参见在AWT和Swing中绘画执行自定义绘画以获取更多详细信息

See Painting in AWT and Swing and Performing Custom Painting for more details

KeyListener 众所周知有问题的。如果它注册的组件是可聚焦的并且具有键盘焦点,它将仅引发键事件。默认情况下, JPanel 不可调焦。在你运行之前尝试让它可以集中注意力(并且感到非常失望),你应该使用键绑定API ,其设计超出了 KeyListener的限制

KeyListener is well known for been problematic. It will only raise key events if the component it registered to is focuable AND has keyboard focus. A JPanel by default, is not focusable. Before you run of and try and make it focusable (and get bitterly disappointed), you should be using the Key Bindings API instead, which was designed to over come the limitations of KeyListener

作为一个基本的例子......

As a basic example...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Game());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Game extends JPanel {

        int x, y, xCoord, yCoord;

        public Game() {
            x = 250;
            y = 250;
            addKeyBinding(KeyEvent.VK_LEFT, "move.left", new MoveAction(this, -1, 0));
            addKeyBinding(KeyEvent.VK_RIGHT, "move.right", new MoveAction(this, 1, 0));
            addKeyBinding(KeyEvent.VK_UP, "move.up", new MoveAction(this, 0, -1));
            addKeyBinding(KeyEvent.VK_DOWN, "move.down", new MoveAction(this, 0, 1));
        }

        protected void addKeyBinding(int keyCode, String name, Action action) {
            addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
        }

        protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();
            inputMap.put(keyStroke, name);
            actionMap.put(name, action);
        }

        public void changeCoord() {
            x += xCoord;
            y += yCoord;
            if (x <= 20) {
                x = 20;
            }
            if (x >= 480) {
                x = 480;
            }
            if (y <= 40) {
                y = 40;
            }
            if (y >= 480) {
                y = 480;
            }

            repaint();
        }

        public void setXCoord(int xcoord) {
            xCoord = xcoord;
            changeCoord();
        }

        public void setYCoord(int ycoord) {
            yCoord = ycoord;
            changeCoord();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(480, 480);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillOval(x, y, 15, 15);
        }

    }

    public class MoveAction extends AbstractAction {

        private int xDelta;
        private int yDelta;

        // I'd prefer an interface with just the "move" methods, but
        // that's more time I don't have
        private Game game;

        public MoveAction(Game game, int xDelta, int yDelta) {
            this.xDelta = xDelta;
            this.yDelta = yDelta;
            this.game = game;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            game.setXCoord(xDelta);
            game.setYCoord(yDelta);
        }

    }
}

但是,等等,这不是你想要的(相信我,我是互联网上的一个anoymouse人;)),一个更好的例子可能是......

But, wait, that isn't exactly what you want (trust me, I'm an anoymouse person on the Internet ;)), a better example might be...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new Game());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public enum Direction {
        UP,
        LEFT,
        DOWN,
        RIGHT;
    }

    public class Game extends JPanel {

        int x, y, xCoord, yCoord;

        private Set<Direction> movement;

        public Game() {
            x = 250;
            y = 250;

            movement = new HashSet<>(4);

            addKeyPressedBinding(KeyEvent.VK_LEFT, "left.pressed", new MoveAction(movement, Direction.LEFT, true));
            addKeyReleasedBinding(KeyEvent.VK_LEFT, "left.released", new MoveAction(movement, Direction.LEFT, false));

            addKeyPressedBinding(KeyEvent.VK_RIGHT, "right.pressed", new MoveAction(movement, Direction.RIGHT, true));
            addKeyReleasedBinding(KeyEvent.VK_RIGHT, "right.released", new MoveAction(movement, Direction.RIGHT, false));

            addKeyPressedBinding(KeyEvent.VK_UP, "up.pressed", new MoveAction(movement, Direction.UP, true));
            addKeyReleasedBinding(KeyEvent.VK_UP, "up.released", new MoveAction(movement, Direction.UP, false));

            addKeyPressedBinding(KeyEvent.VK_DOWN, "down.pressed", new MoveAction(movement, Direction.DOWN, true));
            addKeyReleasedBinding(KeyEvent.VK_DOWN, "down.released", new MoveAction(movement, Direction.DOWN, false));

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    changeCoord();
                }
            });
            timer.start();
        }

        protected void addKeyBinding(int keyCode, String name, Action action) {
            addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0), name, action);
        }

        protected void addKeyPressedBinding(int keyCode, String name, Action action) {
            addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, false), name, action);
        }

        protected void addKeyReleasedBinding(int keyCode, String name, Action action) {
            addKeyBinding(KeyStroke.getKeyStroke(keyCode, 0, true), name, action);
        }

        protected void addKeyBinding(KeyStroke keyStroke, String name, Action action) {
            InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = getActionMap();
            inputMap.put(keyStroke, name);
            actionMap.put(name, action);
        }

        public void changeCoord() {

            if (movement.contains(Direction.UP)) {
                y--;
            } else if (movement.contains(Direction.DOWN)) {
                y++;
            }
            if (movement.contains(Direction.LEFT)) {
                x--;
            } else if (movement.contains(Direction.RIGHT)) {
                x++;
            }

            x += xCoord;
            y += yCoord;
            if (x <= 20) {
                x = 20;
            }
            if (x >= 480) {
                x = 480;
            }
            if (y <= 40) {
                y = 40;
            }
            if (y >= 480) {
                y = 480;
            }

            repaint();
        }

        public void setXCoord(int xcoord) {
            xCoord = xcoord;
            changeCoord();
        }

        public void setYCoord(int ycoord) {
            yCoord = ycoord;
            changeCoord();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(480, 480);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillOval(x, y, 15, 15);
        }

    }

    public class MoveAction extends AbstractAction {

        private Set<Direction> movement;
        private Direction direction;
        private boolean pressed;

        public MoveAction(Set<Direction> movement, Direction direction, boolean pressed) {
            this.movement = movement;
            this.direction = direction;
            this.pressed = pressed;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (pressed) {
                movement.add(direction);
            } else {
                movement.remove(direction);
            }
        }

    }
}

这样做只是在按下一个键时激活一个标志(并在释放时将其取消激活),然后在Swing 计时器中,我们检查哪些键是激活并更新球的位置。

What this does is simply activates a flag when a key is pressed (and deactivates it when it's released), then in a Swing Timer, we check which keys are "active" and update the location of the ball.

这样做,消除了首次按下并按住键时操作系统引起的关键口吃 。在第一个键和重复的键事件之间插入延迟。相反,我们只是根据需要打开和关闭标志。

What this does is, eliminates the key "stutter" which is caused by the OS when a key is first pressed and held. A delay is inserted between the first key and the repeated key events. Instead, we just turn the flag on and off as we like.

它还允许您同时向两个方向移动(水平和垂直)

It also allows you to move in two directions at the same time (horizontally and vertically)

查看 Swing中的并发性如何使用Swing Timers 获取更多详细信息

Have a look at Concurrency in Swing and How to use Swing Timers for more details

这篇关于无法重新绘制我的JFrame / JPanel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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