Java Tips Weblog

  • Blog Stats

    • 1,249,923 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.

Try The Demo

Launch – Using Java™ Web Start (JRE 6 required)

Get The Code

OverlapLayout.java

About these ads

14 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?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 95 other followers

%d bloggers like this: