Java Tips Weblog

  • Blog Stats

    • 2,569,786 hits
  • Categories

  • Archives

Overlap Layout

Posted by Rob Camick on July 26, 2009

All the layout managers, that I’m aware of, position components in a separate area of a container. This makes sense as you generally don’t want components to overlap one another. However, there may situations, in a card game for example, where it is reasonable to have components overlapping one another. I haven’t played with the ZOrder support that was added in JDK5, but I figured this might be a way to support overlapping components.

Sure enough using the ZOrder makes the code of an overlapping layout relatively easy,
although it took me a little while to understand how ZOrdering works. The API simply says that components with a higher ZOrder are painted before components with a lower ZOrder. However, the ZOrder is not really a property of a component. The ZOrder is actually defined by the components placement in the container. As you add components A, B and C to a container the components are assigned a ZOrder of 0, 1, 2 respectively. When you iterate through the container you get the components back in the order you added them, which is A, B, C. However, if the ZOrder of component C is changed to 0, then the components position in the container is altered. That is, if you now iterate through the container you get components C, A, B. So changing the ZOrder of a single component can affect other components as well. Until I realized this the layout code wasn’t working as expected.

Anyway, enough about how ZOrder works. The end result of this effort is the OverlapLayout, which hopefully is straight forward to use. First you need to specify whether a component should be painted “above” or “below” the previous component. The default is to paint components above one another. Next you need to specify the overlapping position of each component. A Point object is used to control the overlap position. By specifying different x, y values you can achieve various layout effects:

  • x, 0 – left-to-right layout
  • -x, 0 – right-to-left layout
  • 0, y – top-to-bottom layout
  • 0, -y – bottom-to-top layout
  • x, y – diagonal top/left-to-bottom/right layout
  • -x, y – diagonal top/right-to-bottom/left layout
  • x, -y – diagonal bottom/left-to-top/right layout
  • -x, -y – diagonal bottom/right-to-top/left layout
  • 0, 0 – a “stack” layout. Components are stacked on top of one another.

The following code was used to create the left-to-right OverlapLayout used in the image below:

OverlapLayout layout = new OverlapLayout(new Point(20, 0));
layout.setPopupInsets(new Insets(20, 0, 0, 0));
JPanel panel = new JPanel( layout );
panel.add(card1);
panel.add(card2);
...

OverlapLayout

Now I guess I need to explain what the popup insets are used for. This is a feature that was specifically added to support card games. In some games you may want a selected card to stand out from the rest. This can be achieved by having the card “popup”. Therefore we need to reserve extra space in the container to allow room for the component to popup. The component will popup in the direction that has been allocated extra space. The popup state of a component is controlled by a contraint:

  • POP_UP – the component is painted in the popup position
  • POP_DOWN (or null) – the component is painted in the normal position

A MouseListener was added to each card in the Web Start demo. The code to toggle the popup state of the component is as follows:

public void mousePressed(MouseEvent e)
{
    Component c = e.getComponent();
    Boolean constraint = layout.getConstraints(c);

    if (constraint == null || constraint == OverlapLayout.POP_DOWN)
        layout.addLayoutComponent(c, OverlapLayout.POP_UP);
    else
        layout.addLayoutComponent(c, OverlapLayout.POP_DOWN);

    c.getParent().invalidate();
    c.getParent().validate();
}

The OverlapLayout works similiar to the GridLayout in that the components are all set to be the same size as determined by the maximum width or height of any given component added to the container. An exception to this is when components are “stacked”. In this case all components are sized to the area of the container.

In general, the OverlapLayout has only been tested using JLabels. I did notice that when using JButtons, mouse entered events caused the overlapped button to be painted on the top other buttons, resulting in confusing rendering of each component. If you have this problem then the solution is to override the isOptimizedDrawingEnabled() method of the JPanel to return false. This tells the repainting subsystem that components overlap so painting issues can be resolved properly.

Anyway, hopefully you can find some interesting uses for the OverlapLayout, whether it be for a card game or something else.

Get The Code

OverlapLayout.java
OverlapLayoutDemo.java
RelativeLayout.java (needed for demo)

24 Responses to “Overlap Layout”

  1. Alejandro Fernandez said

    Can you include the source code for the demo?

    • Rob Camick said

      Sorry for taking so long to reply. The source code doesn’t show how to use the class. The example code in the blog is all you need to add this class to your program. However, I have included the test code as part of the OverlayLayout.jar file which you can download and then extract the code.

  2. Mr. NO said

    Nevermind. Here it is: http://www.camick.com/java/webstart/overlaplayout.jar

  3. Mr. NO said

    I cannot get the panel to show unless I add it to a JScrollPane before I add it to the frame. How come? What am I missing?


    OverlapLayout layout = new OverlapLayout(new Point(20, 0));
    layout.setPopupInsets(new Insets(20, 0, 0, 0));
    JPanel panel = new JPanel( layout );
    panel.add(card1);
    panel.add(card2);
    ...
    JFrame frame = new JFrame("Card game");
    frame.add(new JScrollPane(panel)); //Works
    frame.add(panel); //Does not work for me, nothing shows.

    • Rob Camick said

      You should not need to add the panel to a scrollpane. Do your “cards” actually have a preferred size that the layout manager can use? If you need more help then use the “Contact Us” page and post a SSCCE so we can take the discussion offline.

  4. Benny said

    “Hi Rob Camick, I was wondering if my team could use your code for a project we are designing? We are creating the Hearts card game for our class and this helps a lot for overlapping the cards. Thank you for your time.”

  5. Jeff said

    Rob, I was wondering if you could provide some assistance. I’m throwing java.lang.ArrayIndexOutOfBoundsException: No such child: 85

    Here’s my code:

    private void createPanel() {
    JPanel setting = new JPanel(layout);
    setting.setBackground(new Color(48,200,126));

    if (this.hand != null) {
    hand.sort();
    for (Card card: hand.getCards()) {
    CardPanel cp = new CardPanel(setting, card);
    cp.addMouseListener(this);
    }
    }
    try {
    Component c = setting.getComponent(layout.convertIndex(1)); // XXX: out of bounds!!
    layout.addLayoutComponent(c, OverlapLayout.POP_UP);
    } catch (java.lang.ArrayIndexOutOfBoundsException ex){ /* error! bad input to the function*/
    ex.printStackTrace();
    }
    this.add(setting, BorderLayout.NORTH);
    }

    Any thoughts?

    • Jeff said

      The problem surfaced because I had the layout at the class level and I called createPanel multiple times. I corrected it by addint this method to the OverlapLayout:

      public void removeAll() {
      components.clear();
      constraints.clear();
      }

      I call removeAll in createPanel before I add new components.

  6. Soupaman said

    Somehow I’m getting the same issue as Mr. NO. my cards arent showing at all. this is the code I used

    public class overlapper extends JFrame {
    
    	overlapper() {
    
    		JLabel k = new JLabel(new ImageIcon("king//king_of_hearts.png"));
    		JLabel q = new JLabel(new ImageIcon("cards//queen_of_hearts.png"));
    
    		OverlapLayout layout = new OverlapLayout(new Point(0, 10));
    		layout.setPopupInsets(new Insets(0, 0, 0, 0));
    		JPanel panel = new JPanel(layout);
    
    		panel.add(k);
    		panel.add(q);
    		add(panel);
    	}
    
    	public static void main(String[] args) {
    		overlapper gui = new overlapper();
    		gui.setSize(500, 400);
    		gui.setVisible(true);
    
    	}
    
    }
    

    does anyone know whats wrong with the code?

    • Rob Camick said

      Just noticed the question. There is a problem with the OverlapLayout. It doesn’t work when you add a panel to the “CENTER” of a BorderLayout and use the setSize() method. It will work if you use pack() or you add the panel to any of the other areas of the BorderLayout.

  7. zpython.kaa said

    1. I implement Container to my class
    2. MyContainer.add( new MyCanvas);
    3. Create MyJFrame -> MyJFrame.add(new MyContainer), but it does not show, what the problem?

  8. Milos said

    Many thanks Rob, works perfectly. After so much frustration trying to use JLayeredPane im so glad ive found this.

    I hope you dont mind if I use this for uni project?

  9. Jeff said

    I tried this layout but I was unable to implement it without violating the principles of MVC. (I would occasionally lose the visual display of cards – they were still in the layout but not on the screen). I solved this problem by painting cards. I added grid coordinates to the Card object. One thread just loops and paints. It asks each card: where do you belong? And it paints them there. The model manages their coordinates. I don’t mean to disparage this layout, I’d just like to offer an alternative solution. You can find the code here: https://github.com/JoeDog/pinochle

    • Rob Camick said

      I would occasionally lose the visual display of cards – have never seen that with any layout. Sounds like problems with your code not executing on the EDT. One thread just loops and paints. – threads should not paint. All GUI components should be updated on the EDT. Your looping code would set the location of the Cards in your List. Then you invoke repaint() on the panel the does the painting. This will invoke the paintComponent() method of your custom card panel and the painting code would then loop through each card in the List and paint it.

      • Jeff said

        I didn’t describe that well. The painting takes place on the EDT. The view loops and paints cards which contain location coordinates. Those coordinates are managed by the model.

  10. Webter said

    Hey Rob Camick,

    can you say me how I can change the distance to the top of an object in JPanel with OverlapLayout ? It seem that the layout aligns it to the horizontal center with small distance to the top.

    Thank you in advance

    • Rob Camick said

      In the picture is alignment is 20 pixels from the top. If the “popup insets” is 0, then the alignment is from the top.

      • Webter said

        I dont use the “popup insets” because I have already written my own code to that. It is possible to define the distance to the top without it ?

      • Rob Camick said

        The layout is from the top of the panel. Again in my example I use an EmptyBorder to give spacing from the panel edge. If your components are not at the top it is a layout issue with your code. Maybe you are using Borders or are nesting panels. Set the background of your panel to a different color to see the actual layout.

      • Webter said

        Appreciate it. Thanks a lot

Leave a comment