Java Tips Weblog

  • Blog Stats

    • 2,569,690 hits
  • Categories

  • Archives

Relative Layout

Posted by Rob Camick on November 2, 2008

Layout managers are a great feature of Java. I am not a big fan of layout managers that use multiple constraints to achieve a layout. This entry will propose a simple layout manager that will use a single constraint, yet provide flexibility. It was designed around the notion that you want a container that will use all the space available to it. As a component is added to the container you specify the relative amount of space that component requires. You could divide your container 50/50, or like most cars today you could provide a 60/40 split.

This RelativeLayout currently supports two layout orientations:

  • RelativeLayout.X_AXIS – components are aligned on the X axis from left to right
  • RelativeLayout.Y_AXIS – components are aligned on the Y axis from top to bottom

When using the RelativeLayout as the layout manager you will specify a single constraint as each component is added to the container.

The following shows how 3 equal sized components could be added:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS, 5);
JPanel panel = new JPanel( rl );
panel.add(red, new Float(1));
panel.add(green, new Float(1));
panel.add(blue, new Float(1));

Or, this example shows how to allocate more space to the middle component:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(red, new Float(1));
panel.add(green, new Float(3));
panel.add(blue, new Float(1));

But the flexibility doesn’t stop there. You are not forced to use a relative constraint. If you don’t use a constraint, then the component is added at its preferred size. In this case the preferred size of the component is subtracted from the space available to the container. The remaining space is then allocated to the components using relative spacing. This gives you a pseudo BorderLayout look:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(red);
panel.add(green, new Float(3));
panel.add(blue);

For what its worth if none of the components added to the container contain a constraint then the layout will be like a FlowLayout.

You can also use the RelativeLayout to mimic a BoxLayout. The Box invisible components can be added to the container like any other component. For example, you can center a component in the container using:

RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
JPanel panel = new JPanel( rl );
panel.add(Box.createGlue(), new Float(1));
panel.add(green, new Float(3));
panel.add(Box.createGlue(), new Float(1));

By default the RelativeLayout will layout components without a gap between each component. However, following in the footsteps of the FlowLayout you can provide a gap before and after every component by using the setGap(…) method. As an added feature you can use the setBorderGap(…) method to control the gap before the leading component and after the trailing component. The border gap will default to the gap value at class construction time. Afterwards the two methods work independently of one another.

The RelativeLayout class will also allow you to customize the layout at the container level. You can control the alignment of the components on the opposite axis using the setAlignment(…) method. Four common values are listed below:

  • RelativeLayout.LEADING – component is aligned to the Left or Top depending on the layout axis
  • RelativeLayout.CENTER (default) – component is aligned in the center
  • RelativeLayout.TRAILING – component is aligned to the Right or Bottom depending on the layout axis
  • RelativeLayout.COMPONENT – the value specified in the setAligntmentX/Y method of the component is used

The size of the component on the opposite axis can be filled to occupy the entire space available using the setFill(true) method. In addition, you can prevent the component from completely filling the space available by using the setFillGap(int) method.

When allocating space to components using relative sizing it can’t be guaranteed that all the space will be allocated due to rounding errors. You can control how the few remaining pixels should be allocated by using the setRoundingPolicy(…) method. The following polices are currently supported:

  • RelativeLayout.DO_NOTHING -remaining pixels will not be allocated to any component
  • RelativeLayout.FIRST – rounding pixels applied to the first relative component.
  • RelativeLayout.LAST – rounding pixels applied to the last relative component
  • RelativeLayout.LARGEST (default) – rounding pixels applied to the largest relative component. When more than one component is the same size, then the last one found will be used.
  • RelativeLayout.EQUAL – a single pixel will be applied to each relative component (starting at the first) until the rounding pixels have been used

The RelativeLayout started out as a simple layout manager to allocate relative sizes dynamically to components. However by additionally supporting invisible components and components with preferred sizes you may find other interesting uses for it as well.

Get The Code

RelativeLayout.java

Related Reading

Using Layout Managers

15 Responses to “Relative Layout”

  1. Anonymous said

    2009 and we are still working on awt layouts.
    you should reconsider your initial sentence:
    “Layout managers are a great feature of Java”

    Your layout however is a pretty one. well done.

    Lorenzo

  2. Andre Uhres said

    In this demo:
    http://www.java-forum.org/de/userfiles/user3690/RelativeLayoutDemo.java
    I have included this statement:
    layoutX.setFill(false);//with true, layout looks bad

    When I change the statement to
    layoutX.setFill(true);
    then the layout looks bad indeed: part of the borders are gone.

    Also, it would be nice if we could somehow set a gap for the setFill method, so that the components don’t expand to all the available space, but leave a gap to the right and left (respectively to the top and bottom).

  3. Rob Camick said

    Thanks for the feedback, the layout bug related to the setFill method has been fixed.

    As a general rule I would suggest that if you don’t want the component to fill the entire space then you would add an EmptyBorder to the parent Container. However, once I fixed the above bug it was only a further one line change so I also added a setFillGap() method which will decrease the fill size of the component by the specified amount.

  4. Andre Uhres said

    Thanks, works well. I think you have done a great job with this user friendly layout manager.

  5. Andre Uhres said

    For Java Beans conventions, and for making it more easy to integrate the layout into a builder tool, could you please make the following trivial changes:

    rename the property “isFill” to “fill”

    Add a default constructor (no argument), for example:

    public RelativeLayout()
    {
        this(X_AXIS, 0);
    }
    

    Add the following seven getter/setter methods:

    public int getAxis()
    public void setAxis(int axis)
    public HashMap getConstraints()
    public void setConstraints(HashMap constraints)
    public float getAlignment()
    public int getFillGap()
    public int getRoundingPolicy()
    public boolean isFill()
    

    Thanks in advance.

  6. Rob Camick said

    Yes, I know I’ve been lazy at not providing getter methods, that will be no problem.

    I’m not sure about the getContraints() getter/setter. Somehow it doesn’t seem right to have access to all the constraints in the HashMap (ie. it doesn’t make sense to expose the storage of the constraints). I’ve taken a look at BorderLayout, GridBagLayout and SpringLayout. None of them provide the suggested getter/setter methods.

    I’ve never tried to integrate a class into a layout builder tool so I don’t know what is required. Do the standard layout managers integrate without the above methods? If you really need it I can add it in, otherwise I’d like to follow the lead of the standard JDK classes and not include it.

  7. Andre Uhres said

    With Netbeans, the standard layout managers are already integrated (I suppose without getter/setter methods for the contraints). Custom layouts without constraints can also be easily integrated into NetBeans. However, custom layouts with constraints don’t integrate without a special support class which is not evident to develop and integrate. In fact, till now I have no idea how to do this. So it would be a workaround (ugly, I know) if we include the getter/setter methods for the contraints.

    In this case, NetBeans generates code like this:
    jPanel1 = new JPanel();
    jPanel2 = new JPanel();
    RelativeLayout relativeLayout1 = new RelativeLayout();
    relativeLayout1.setFill(true);
    getContentPane().setLayout(relativeLayout1);
    getContentPane().add(jPanel1);//unhappily this cannot be customized (guarded block)
    getContentPane().add(jPanel2);

    and we can include this custom code for the constraints:
    setRelative1(relativeLayout1);

    private void setRelative1(LayoutManager layout){
    HashMap map = new HashMap();
    map.put(jPanel1, new Float(1));
    map.put(jPanel2, new Float(3));
    ((RelativeLayout)layout).setConstraints(map);
    }

    Since the problem is more with NetBeans, I’m not sure if you should really include the getter/setter methods for the contraints. Another approach would be to change the visibility to “protected”, so we can add getter/setter by extending your class.

  8. Rob Camick said

    You can add a constraint to a component after the fact:

    private void setRelative1(LayoutManager2 layout)
    {
        layout.addLayoutComponent(jPanel1, new Float(1));
        layout.addLayoutComponent(jPanel2, new Float(3));
    }
    

    I think this should satisfy your requirements. I have uploaded the class with the other getters/setters requested above.

  9. Andre Uhres said

    Yes, I didn’t think about addLayoutComponent which is of course the right solution. Works well with the default constructor and the other getters/setters. Thanks.

  10. I’ve been struggling with the Box and GridBag layouts until I found this post !
    I know this is an old post, but I’d still like to thank you for your contribution. :)

  11. Gerard said

    Awesome! I’ve been have a problematic screen (content is build dynamically based on problem domain logic and user choice) and none of the other/existing layout managers seemed to work well and simply. What’s the licensing of the code?

  12. jarrettbillingsley said

    I am still maintaining a Java Swing app that I use in a course I teach, and this is absolutely amazing. It’s like CSS Flexbox, but you made it 5 years before that existed. My layouts are SO much better and easier. Thank you! (the app is on Github, MARS_Assembler, under my name)

Leave a comment