繁体   English   中英

无法重绘我的JFrame / JPanel

[英]Can't repaint my JFrame/JPanel

我创建了一个程序,可以在屏幕上移动一个球。 我以前把所有内容都放在一个类中,但认为它看起来太乱了,所以我将其分为三个不同的类: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();

}

}

让我们从显而易见的...开始。

这是有问题的...

@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组件中实现双缓冲了。 另外,通过不将paint方法称为super方法,您正在破坏绘画合同

整个事情应该是...

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

有关更多详细信息,请参见AWT中的绘画和摇摆执行自定义绘画

众所周知, KeyListener存在问题。 仅当注册到的组件可操作并且具有键盘焦点时,它才会引发键事件。 默认情况下, JPanel不能聚焦。 在尝试并使其聚焦之前(令人非常失望),您应该改用Key Bindings API ,该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);
        }

    }
}

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

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

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

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

了解Swing中的并发性以及如何使用Swing计时器以了解更多详细信息

暂无
暂无

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

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