6

I'm writing a simple program to test out basic GUI. The program prints a letter in the middle of the screen and allows the user to move it with the arrow keys. Everything works fine, but when I try to center the letter at the start of the program, it seems that the getWidth and getHeight functions aren't returning the proper numbers.

Here's the snippet containing my Panel class

static class LinePanel extends JPanel{
    int xCenter = getWidth() /2;
    int yCenter = getHeight() /2;

    private int x = xCenter;
    private int y = yCenter;
    private char keyChar = 'A';

    public LinePanel(){
        addKeyListener(new KeyAdapter(){
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_DOWN: y += 10; break;
                    case KeyEvent.VK_UP: y -= 10; break;
                    case KeyEvent.VK_LEFT: x -= 10; break;
                    case KeyEvent.VK_RIGHT: x += 10; break;
                    default: keyChar = e.getKeyChar();
                }

                repaint();

            }
        });
    }

    protected void paintComponent(Graphics g){
        super.paintComponent(g);

        g.setFont(new Font("TimesRoman", Font.PLAIN, 24));
        g.drawString(String.valueOf(keyChar), x, y);
    }
}

Why are my getWidth and getHeight functions returning '0'?

Thanks for any help

mKorbel
  • 109,525
  • 20
  • 134
  • 319
KAM1KAZEKOALA
  • 115
  • 1
  • 2
  • 6

3 Answers3

10

Swing components have no width or height until they've been rendered. This occurs if you call pack() or setVisible(true) on a root container. Consider placing your x y int initialization code in the componentResized method of a ComponentListener that is added to your JPanel.

e.g.,

import java.awt.event.*;
import java.awt.*;

import javax.swing.*;

public class TestLinePanel {
   private static void createAndShowGui() {
      LinePanel mainPanel = new LinePanel();

      JFrame frame = new JFrame("TestLinePanel");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

   static class LinePanel extends JPanel {
      private static final int PREF_W = 400;
      private static final int PREF_H = PREF_W;
      private char keyChar = 'A';
      private int x;
      private int y;
      private boolean xySet = false;

      public LinePanel() {
         setFocusable(true);
         addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
               switch (e.getKeyCode()) {
               case KeyEvent.VK_DOWN:
                  y += 10;
                  break;
               case KeyEvent.VK_UP:
                  y -= 10;
                  break;
               case KeyEvent.VK_LEFT:
                  x -= 10;
                  break;
               case KeyEvent.VK_RIGHT:
                  x += 10;
                  break;
               default:
                  keyChar = e.getKeyChar();
               }

               repaint();

            }
         });

         addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
               if (!xySet) {
                  int xCenter = getWidth() / 2;
                  int yCenter = getHeight() / 2;

                  x = xCenter;
                  y = yCenter;

                  requestFocusInWindow();
                  xySet = true;
               }
            }

         });
      }

      @Override
      public Dimension getPreferredSize() {
         return new Dimension(PREF_W, PREF_H);
      }

      protected void paintComponent(Graphics g) {
         super.paintComponent(g);
         g.setFont(new Font("TimesRoman", Font.PLAIN, 24));
         g.drawString(String.valueOf(keyChar), x, y);
      }
   }
}

You'll also want to use key bindings rather than a KeyListener to capture your key strokes.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 1
    some (concededly nitpicking :-) comments: a) no need for a listener, with getPref overridden you can start with the middle of getPref b) _if_ you use a listener, don't forget repaint c) _if_ you use a listener which is meant to run once only make it self-removing instead of adding a flag d) +1 for recommending keyBindings – kleopatra Dec 01 '11 at 10:33
7

I cannot tell the reason but:

A way to avoid this is to override your getPreferredSize() function and return your preferred size.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Anthea
  • 3,741
  • 5
  • 40
  • 64
3

Member variables are initialized when the object is created (after the constructors of any parent classes & before constructor body of the current class. Swing does not doing any sizing work when the object is first created. Therefore, when you call getWidth() and getHeight(), no values have been set yet.

unholysampler
  • 17,141
  • 7
  • 47
  • 64
  • Not true. Sizing only occurs upon rendering, not on putting anthing in a layout manager. You'll want to read "[Filthy Rich Clients](http://filthyrichclients.org/)" by Guy and Haase for more on this. – Hovercraft Full Of Eels Dec 01 '11 at 03:48
  • 1
    @HovercratFullOfEels: I edited out that part. The point that those values are not set by the constructor still holds true. Since the OP only calculates those values once at the beginning, they will never have the intended value. – unholysampler Dec 01 '11 at 04:01