paintComponent() 可以用在 AbstractAction class 中吗?
Can paintComponent() be used in an AbstractAction class?
我正在尝试制作一个创建 JPanel 的程序,当用户按下 W、A、S 和 D 时,绘制的立方体将在 window 中导航(通过某个每次按下一个键时的数量),我已经创建了 MoveCubeUp class,我覆盖了其中的 paintComponent 方法以在调用时重新绘制立方体,但它不起作用。有人可以解释为什么吗?
public MyPanel(){
…
MoveSquareUp m=new MoveSquareUp(squareX, squareY);
getInputMap().put(KeyStroke.getKeyStroke(("W"), "pressed"));
getActionMap().put("pressed", m)
}
class MoveSquareUp extends AbstractAction{
public int squareXX, squareYY;
public moveSquare(){
squareXX=squareX+5;
}
//I define the paintComponent method to draw the rectangle with its set height
//at squareXX, squareYY
//action method is null (I am still trying to figure out binding keys to
//actions but the paintComponent not working is preventing that
}
如果格式不正确,我深表歉意。第一个post:/
是否需要在扩展 JFrame 的 class 中定义 paint 方法,如果是这样,我如何将它与 abstractAction class 一起使用(或者我如何完全避免 AbstractAction class )?
问题的症结在于您需要学会将模型与视图与控件分开。这里的模型是你的精灵的位置,视图是绘制这个位置的 GUI,控件将保存包括你的 AbstractAction 在内的动作,如果可能的话,它们应该彼此分开。
所以回答你的直接问题 -- 任何 paintComponent 绝对 不应该 在 AbstractAction 内部,因为前者是视图的关键部分,而后者是关键控制的一部分。相反,让您的视图反映模型的状态,并且模型的状态将由控件(操作)更改。
关于你的另一个问题,所有绘画方法都应该是 JFrame 的一部分吗:none 绘画方法应该在 class 扩展 JFrame 中,因为这个 class 是一个复杂的class 创建一个顶层 window 和几个子组件来显示您的 GUI,如果您覆盖它的绘画,您可能会以糟糕的方式影响子组件的绘画。而是在扩展 JPanel 的 class 的 paintComponent 方法中绘制,然后在您的 JFrame 中显示此对象。
例如:
package pkg3;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class GamePanel extends JPanel {
private static final int ANIMATION_DELAY = 15;
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private EnumMap<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private Map<Integer, Direction> keyToDir = new HashMap<>();
// !! private Circle circle;
private Timer animationTimer;
public GamePanel() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
keyToDir.put(KeyEvent.VK_UP, Direction.UP);
keyToDir.put(KeyEvent.VK_DOWN, Direction.DOWN);
keyToDir.put(KeyEvent.VK_LEFT, Direction.LEFT);
keyToDir.put(KeyEvent.VK_RIGHT, Direction.RIGHT);
setKeyBindings();
setBackground(Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
animationTimer = new Timer(ANIMATION_DELAY, new AnimationListener());
animationTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
final InputMap inputMap = getInputMap(condition);
final ActionMap actionMap = getActionMap();
boolean[] keyPressed = { true, false };
for (Integer keyCode : keyToDir.keySet()) {
Direction dir = keyToDir.get(keyCode);
for (boolean onKeyPress : keyPressed) {
boolean onKeyRelease = !onKeyPress; // to make it clear how
// bindings work
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0, onKeyRelease);
Object key = keyStroke.toString();
inputMap.put(keyStroke, key);
actionMap.put(key, new KeyBindingsAction(dir, onKeyPress));
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
square.display(g);
}
private class AnimationListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
boolean repaint = false;
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
square.move(dir);
repaint = true;
}
}
if (repaint) {
repaint();
}
}
}
private class KeyBindingsAction extends AbstractAction {
private Direction dir;
boolean pressed;
public KeyBindingsAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
@Override
public void actionPerformed(ActionEvent evt) {
dirMap.put(dir, pressed);
}
}
private static void createAndShowGUI() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
private int incrX;
private int incrY;
private Direction(int incrX, int incrY) {
this.incrX = incrX;
this.incrY = incrY;
}
public int getIncrX() {
return incrX;
}
public int getIncrY() {
return incrY;
}
}
class Square {
private int x = 0;
private int y = 0;
private int w = 20;
private int h = w;
private int step = 1;
private Color color = Color.red;
private Color fillColor = new Color(255, 150, 150);
private Stroke stroke = new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
public void display(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(x, y, w, h);
g2d.setStroke(stroke);
g2d.setColor(color);
g2d.drawRect(x, y, w, h);
g2d.dispose();
}
public void setStep(int step) {
this.step = step;
}
public void move(Direction dir) {
x += step * dir.getIncrX();
y += step * dir.getIncrY();
}
}
我正在尝试制作一个创建 JPanel 的程序,当用户按下 W、A、S 和 D 时,绘制的立方体将在 window 中导航(通过某个每次按下一个键时的数量),我已经创建了 MoveCubeUp class,我覆盖了其中的 paintComponent 方法以在调用时重新绘制立方体,但它不起作用。有人可以解释为什么吗?
public MyPanel(){
…
MoveSquareUp m=new MoveSquareUp(squareX, squareY);
getInputMap().put(KeyStroke.getKeyStroke(("W"), "pressed"));
getActionMap().put("pressed", m)
}
class MoveSquareUp extends AbstractAction{
public int squareXX, squareYY;
public moveSquare(){
squareXX=squareX+5;
}
//I define the paintComponent method to draw the rectangle with its set height
//at squareXX, squareYY
//action method is null (I am still trying to figure out binding keys to
//actions but the paintComponent not working is preventing that
}
如果格式不正确,我深表歉意。第一个post:/ 是否需要在扩展 JFrame 的 class 中定义 paint 方法,如果是这样,我如何将它与 abstractAction class 一起使用(或者我如何完全避免 AbstractAction class )?
问题的症结在于您需要学会将模型与视图与控件分开。这里的模型是你的精灵的位置,视图是绘制这个位置的 GUI,控件将保存包括你的 AbstractAction 在内的动作,如果可能的话,它们应该彼此分开。
所以回答你的直接问题 -- 任何 paintComponent 绝对 不应该 在 AbstractAction 内部,因为前者是视图的关键部分,而后者是关键控制的一部分。相反,让您的视图反映模型的状态,并且模型的状态将由控件(操作)更改。
关于你的另一个问题,所有绘画方法都应该是 JFrame 的一部分吗:none 绘画方法应该在 class 扩展 JFrame 中,因为这个 class 是一个复杂的class 创建一个顶层 window 和几个子组件来显示您的 GUI,如果您覆盖它的绘画,您可能会以糟糕的方式影响子组件的绘画。而是在扩展 JPanel 的 class 的 paintComponent 方法中绘制,然后在您的 JFrame 中显示此对象。
例如:
package pkg3;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class GamePanel extends JPanel {
private static final int ANIMATION_DELAY = 15;
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private EnumMap<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private Map<Integer, Direction> keyToDir = new HashMap<>();
// !! private Circle circle;
private Timer animationTimer;
public GamePanel() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
keyToDir.put(KeyEvent.VK_UP, Direction.UP);
keyToDir.put(KeyEvent.VK_DOWN, Direction.DOWN);
keyToDir.put(KeyEvent.VK_LEFT, Direction.LEFT);
keyToDir.put(KeyEvent.VK_RIGHT, Direction.RIGHT);
setKeyBindings();
setBackground(Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
animationTimer = new Timer(ANIMATION_DELAY, new AnimationListener());
animationTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
final InputMap inputMap = getInputMap(condition);
final ActionMap actionMap = getActionMap();
boolean[] keyPressed = { true, false };
for (Integer keyCode : keyToDir.keySet()) {
Direction dir = keyToDir.get(keyCode);
for (boolean onKeyPress : keyPressed) {
boolean onKeyRelease = !onKeyPress; // to make it clear how
// bindings work
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0, onKeyRelease);
Object key = keyStroke.toString();
inputMap.put(keyStroke, key);
actionMap.put(key, new KeyBindingsAction(dir, onKeyPress));
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
square.display(g);
}
private class AnimationListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
boolean repaint = false;
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
square.move(dir);
repaint = true;
}
}
if (repaint) {
repaint();
}
}
}
private class KeyBindingsAction extends AbstractAction {
private Direction dir;
boolean pressed;
public KeyBindingsAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
@Override
public void actionPerformed(ActionEvent evt) {
dirMap.put(dir, pressed);
}
}
private static void createAndShowGUI() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
private int incrX;
private int incrY;
private Direction(int incrX, int incrY) {
this.incrX = incrX;
this.incrY = incrY;
}
public int getIncrX() {
return incrX;
}
public int getIncrY() {
return incrY;
}
}
class Square {
private int x = 0;
private int y = 0;
private int w = 20;
private int h = w;
private int step = 1;
private Color color = Color.red;
private Color fillColor = new Color(255, 150, 150);
private Stroke stroke = new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
public void display(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(x, y, w, h);
g2d.setStroke(stroke);
g2d.setColor(color);
g2d.drawRect(x, y, w, h);
g2d.dispose();
}
public void setStep(int step) {
this.step = step;
}
public void move(Direction dir) {
x += step * dir.getIncrX();
y += step * dir.getIncrY();
}
}