问题
When I create a Board instance from my Square instance, I try to assign the size of the window to the integers x and y. I fail to do this because it seems like on start the size is 0. In the constructor in Board.java, x and y shouldn't be -50 like they end up now.
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);
}
}
Full Code for clarification: 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);
}
}
回答1:
Put the calculation into your paintComponent method. In general you want to avoid having code in paintComponent that will slow it down, but these calls shouldn't give too much penalty. Also, if your program is re-sizable, you'll need to be able to re-calculate the sizes of things on the go like this:
public void paintComponent(Graphics g){
super.paintComponent(g);
int x = getWidth() - 50;
int y = getHeight() - 50;
g.fillRect(x, y, 100, 100);
}
but of course in your real program, you will want to avoid "magic" numbers
Another issue: don't call setSize()
on your JFrame. Instead, if you want to specify a hard size, do so in the JPanel by overriding its getPreferredSize()
method. This will also then give you the suggested parameters that can be used for your calculations.
For example:
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();
}
});
}
}
Also, check out the key bindings animation code from this answer of mine.
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();
}
}
回答2:
Hey your codestyle is horrible, but i try to help :). You can't get a size of an undrawn window. First Things first, your Constructor is wrong, you add the Board that actually create the Board Obj. Calling the Constructor of Board, which has no drawn parent yet and no x,y set. Try to initialize your variables in the Constructor. So just use width and height and fill the values in the constructor. Next, just tell your board its creation size by passing its parent size trough constructor variables. I think you try to learn java and this is much more elegant. Furthermore, try to do all parent modification before adding some to it. So first setSize, add some Layout (Border/Flow/whatuwish) then get the frames ContentPane and add your Board component. To make things clear, you can't get e.g. the parent and parent size in Contructor because your board Obj isn't created and added yet. If you wish to getParent() and its size, create the Object add it to JFrame and than you can call getParent().getSize(). You get 0 because your JPanel isn't drawn at this time (before creation). If you wish to get the Parent Size just pass the JFrame Ref to Constructor or its size. Another Advise, don't create things in things in things, keep in mind with your code you create your JPanel as first Obj... Here is some example code: Square:
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);
}
}
Board:
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);
}
}
回答3:
You can take x and y values after the panel has become visible, in the next EDT cycle, by using SwingUtilities.invokeLater, for example.
来源:https://stackoverflow.com/questions/30226301/jpanel-size-is-not-known-on-start