无法重新绘制我的JFrame / JPanel

时间:2016-01-14 21:41:14

标签: java swing jframe jpanel repaint

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

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

}

}

1 个答案:

答案 0 :(得分:4)

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

这是有问题的......

@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组件中实现双缓冲,它们已经存在。另外,你打破绘画合约,不要调用绘画方法超级方法

整件事应该是......

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

有关详细信息,请参阅Painting in AWT and SwingPerforming Custom Painting

众所周知,

KeyListener存在问题。如果它注册的组件是可聚焦的并且具有键盘焦点,它将仅引发键事件。默认情况下,JPanel不可调焦。在你跑步并试着让它变得可聚焦(并且感到非常失望)之前,你应该使用Key Bindings API来代替KeyListener

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

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人;)),一个更好的例子可能是......

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 Timer中,我们检查哪些键是“活动的”并更新球的位置

这样做,消除了首次按下和按住按键时由操作系统引起的关键“断续”。在第一个键和重复的键事件之间插入延迟。相反,我们只是按照我们的意愿打开和关闭标志。

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

有关详细信息,请查看Concurrency in SwingHow to use Swing Timers