Frequently Asked Questions About Java

  1. Question: I used the pack() and show() commands to display a JFrame but the window showed up as a tiny window.

  2. Answer: You need to declare a getPreferredSize() method in your JPanels. pack() causes the layout manager to consult this method. pack() may ignore dimensions that were set using the setSize() command.
     

  3. Question: I need to find the width of a text string but I need to get access to a graphics context in order to get a FontMetrics object. How can I get access to a graphics context object if I am not in paintComponent?

  4. Answer: Use the getGraphics() method.
     

  5. Question: I am calling repaint but nothing is happening.


    Answer: You are probably not calling repaint on the correct component. You must call repaint on the component that you want redrawn. For example, if you want a JPanel redrawn, you should call repaint on that JPanel. If you instead try to call repaint on the JFrame containing that JPanel, or on the content pane containing that JPanel, nothing will happen because neither of them will ask their own components to redraw themselves.

  6. Question: When my window gets drawn, images from one part of the window get drawn in other places in the window. For example, radio buttons that should appear at the bottom of the window also get partially drawn at the top of the window.

  7. Answer: You may have omitted a call to super.paintComponent(g) from your paintComponent method. Failing to call the superclass method causes all sorts of strange drawing behavior.

  8. Question: When I write my paintComponent method, how do I know whether the application called repaint() or whether the system called repaint()? If the system called repaint(), I want to redisplay my entire application, but if the application called repaint(), I only want to display the damaged portions of the application.


    Answer: The way I determine the difference is to have the application define a boolean flag which it sets to true when it calls repaint. paintComponent tests this flag and then re-sets it to false. If someone has a better way of doing this, I'd be happy to hear about it.

  9. Question: If I call the super.paintComponent for my JPanel before I call my display manager, the window gets erased and only those objects in the changed list are displayed (the rest of the panel is background). But if I don't call super.paintComponent then the display gets messed up in some weird way.


    Answer: Java does strange things with the clip region. Before Java calls paintComponent, it tiles the clip region with a previous image of the window. As far as I can tell, you cannot prevent Java from doing this. Therefore you might as well call super.paintComponent to blank the clipping region, then redraw into that clipping region. In order to control the clipping region that you are given, give a damaged region to the repaint method when you call it. If you don't, Java assumes the clipping region should be the entire component. Java uses the RepaintManager class to collect damaged regions. Each time that your application calls repaint, an instance of the RepaintManager gets called and it folds the dirty region specified by repaint into the existing dirty region for the component. It appears that the RepaintManager can only maintain one region per component. Unfortunately, this means that it is difficult to implement a display manager that only redraws changed objects, because the union of the changed objects' bounding boxes will typically include some undamaged objects. One way to avoid this problem is call paintImmediately rather than repaint but the Java documentation does not suggest doing this. In your sokoban game, the changed objects will always be contiguous so you can get away with drawing only the changed objects. One other trick that you might try but which won't work is to give repaint a tiny damaged region and then try to enlarge the clipping region in paintComponent. Java won't let you enlarge the clipping region, you can only narrow it.

  10. Question: How can I get a JScrollPane that contains a JList to display at a desired width when the JList is initially empty?


    Answer: The documentation for a JScrollPane says that it gets its sizing information from the getPreferredScrollableViewportSize method. This method returns a Dimension so we can return a Dimension object with the desired width. Unfortunately, we may still want the default height. According to the documentation, the default height accommodates 8 rows. In order to calculate the number of pixels needed to display 8 rows, the default implementation of getPreferredScrollableViewportSizemethod uses 16 pixels per row. Hence the default height is 128 pixels and you can get your desired scroll pane using the following code:

    JList myList = new JList() {
      public Dimension getPreferredScrollableViewportSize() {
        return new Dimension(desiredWidth, 128);
      }
    }
    

  11. Question: Why can I not create an instance of an inner class outside of the class that contains the inner class?


    Answer: An instance of an inner class can access instance variables of its enclosing class which means that it needs to have an instance of its enclosing class as a "scope". However, if you try to create an instance of an inner class from another class, Java does not have such an outer object to provide as a scope. Hence only the class containing an inner class can create instances of its inner classes.

  12. Question: When I call the Graphic's clearRect method, it creates a cleared white rectangle rather than a cleared rectangle in the background color.


    Answer: clearRect does not seem to be a reliable way to clear a display area. If you are writing to an off-screen image, which is what you are doing by default since JFrames are double-buffered by default, then clearRect is not guaranteed to be correct, but it appears to fail in other situations as well. The best way to clear an area is to retrieve the background color from the JComponent, set the color to this background color, and then call fillRect. For example:

    void paintComponent(Graphics g) {
      ...
      g.setColor(getBackground());
      // this command clears the entire component. To clear a smaller area you
      // would provide your own coordinates and size.
      g.fillRect(0, 0, getWidth(), getHeight());
      ...
    }
    
  13. Question: When I want to change the size of a JComponent or JPanel, how do I let Java know that it needs to re-layout the component and/or adjust the window size?

    Answer: Call revalidate on the JComponent or JPanel's whose size has changed if you want to have it re-laid out. If you want to also adjust the window size, then call pack on the component's JFrame. revalidate will recursively traverse up the component hierarchy, invalidating the intervening container objects, until it reaches a container object that knows that its size will not be affected by the change. Most frequently this is the JRootPane itself, although sometimes it can stop earlier, such as at a JTextField. The layout manager will then be invoked on the topmost invalidated container object, and all intervening container objects will also have their layout managers invoked.

    Note that revalidation will not cause the window size to change. The layout manager may re-allocate space among a container's components, but it will not change the size of a window. If you want the window to adjust its size, then you need to call the window's pack method.