JPanel 大小在开始时未知

JPanel size is not known on start

当我从 Square 实例创建 Board 实例时,我尝试将 window 的大小分配给整数 x 和 y。我没有这样做,因为开始时大小似乎是 0。在 Board.java 的构造函数中,x 和 y 不应该像现在这样结束 -50。

Square.java:

package Square;

import javax.swing.*;

public class Square extends JFrame {

    public Square(){
        add(new Board());
        setSize(800, 800);
        setVisible(true);
    }

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

Board.java

package Square;

import javax.swing.*;
import java.awt.*;

public class Board extends JPanel{
    int x,y;

    public Board(){
        x = width-50;
        y = height-50;
    }

    public int width = (int) getSize().getWidth();
    public int height = (int) getSize().getHeight();

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.fillRect(x,y, 100, 100);
    }
}

完整代码说明: Square.java

package Square;

import javax.swing.*;

public class Square extends JFrame {

    public Square(){
        Board board = new Board();
        board.start();
        add(board);
        setTitle("Square");
        setSize(800, 800);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args){
        Square square = new Square();
        square.setVisible(true);
        square.setLocation(2000, 150);
    }
}

Board.java

package Square;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Board extends JPanel implements ActionListener{

    Timer timer;
    int x, y;
    int velX = 0;
    int velY = 0;

    public Board(){
        setFocusable(true);
        timer = new Timer(1, this);
        addKeyListener(new TAdapter());
    }

    class TAdapter extends KeyAdapter{

        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();

            switch(keyCode){
                case KeyEvent.VK_ESCAPE: x = width()/2-50; y = height()/2-50; break;
                case KeyEvent.VK_RIGHT: velX = 1; break;
                case KeyEvent.VK_DOWN: velY = 1; break;
                case KeyEvent.VK_LEFT: velX = -1; break;
                case KeyEvent.VK_UP: velY = -1; break;
            }
        }

        public void keyReleased(KeyEvent e){
            velX = 0;
            velY = 0;
        }
    }
    public int width(){ return (int) getSize().getWidth();}
    public int height(){ return (int) getSize().getHeight();}

    public void start(){
        timer.setInitialDelay(100);
        timer.start();
        x = width()/2-50;
        y = height()/2-50;
    }



    @Override
    public void actionPerformed(ActionEvent e) {
        x += velX;
        y += velY;
        repaint();
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(Color.RED);
        g.fillRect(x,y, 100, 100);
    }
}

将计算放入您的 paintComponent 方法中。通常,您希望避免在 paintComponent 中使用会减慢速度的代码,但这些调用不应造成太大的损失。此外,如果您的程序是可调整大小的,您将需要能够像这样在旅途中重新计算事物的大小:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    int x = getWidth() - 50;
    int y = getHeight() - 50;
    g.fillRect(x, y, 100, 100);
}

但当然在你的真实程序中,你会想要避免 "magic" 数字

另一个问题:不要在 JFrame 上调用 setSize()。相反,如果您想指定硬尺寸,请在 JPanel 中通过覆盖其 getPreferredSize() 方法来执行此操作。这还将为您提供可用于计算的建议参数。


例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

@SuppressWarnings("serial")
public class DrawRect extends JPanel {
   private static final int PREF_W = 800;
   private static final int PREF_H = PREF_W;
   private static final int DELTA = 50;
   private static final Color RECT_COLOR = Color.red;
   private static final int RECT_WIDTH = 100;
   private static final int TIMER_DELAY = 15;
   private int rectX = PREF_W - DELTA;
   private int rectY = PREF_H - DELTA;

   public DrawRect() {
      new Timer(TIMER_DELAY, new TimerListener()).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(RECT_COLOR);
      g.fillRect(rectX, rectY, RECT_WIDTH, RECT_WIDTH);
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         rectX--;
         rectY--;
         repaint();
      }
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("DrawRect");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new DrawRect());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

另外,查看我的 this answer 中的键绑定动画代码。

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);
      // !! addKeyListener(new DirectionListener());
      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();
   }

}

您可以在面板变得可见后获取 x 和 y 值,例如,在下一个 EDT 周期中,使用 SwingUtilities.invokeLater。

嘿,你的代码风格很糟糕,但我会尽力提供帮助:)。您无法获得未绘制的尺寸 window。首先,您的构造函数是错误的,您添加了实际创建 Board Obj 的 Board。调用 Board 的构造函数,它还没有绘制父级,也没有设置 x,y。尝试在构造函数中初始化变量。所以只需使用宽度和高度并在构造函数中填充值。接下来,只需通过构造函数变量传递其父级大小来告诉您的板其创建大小。 我认为您尝试学习 java 并且这更加优雅。此外,尝试在添加一些之前进行所有父修改。因此,首先设置大小,添加一些布局 (Border/Flow/whatuwish),然后获取框架 ContentPane 并添加您的 Board 组件。为了说清楚,你不能得到例如Contructor 中的父级和父级大小,因为您的板 Obj 尚未创建和添加。如果您希望获取 getParent() 及其大小,请创建对象并将其添加到 JFrame,然后您可以调用 getParent().getSize()。您得到 0 是因为此时(创建之前)未绘制您的 JPanel。如果您希望获得父级大小,只需将 JFrame Ref 传递给构造函数或其大小即可。另一个建议,不要在事物中创建事物,请记住您将 JPanel 创建为第一个 Obj 的代码......这是一些示例代码: 正方形:

public class Square extends JFrame {



    public static void main(String[] args){
       Square square = new  Square();
       square.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       Dimension d = new Dimension(800,800);
       square.setPreferredSize(d);
       square.setSize(d);


       //too much, every Jframe has BorderLayout enabled
       square.getContentPane().setLayout(new BorderLayout());
       square.getContentPane().add(new Board(square), BorderLayout.CENTER);
       square.pack();
       square.setVisible(true);

    }
}

董事会:

public class Board extends JPanel{
    int x,y;
    JFrame parent;

    public Board(JFrame parent){

         int width =  parent.getPreferredSize().width;
         int height = parent.getPreferredSize().height;
        x = width-50;
        y = height-50;
    }



    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.fillRect(x,y, 100, 100);
    }
}