开发者

Java/AWT/Swing: about validation and sizes

I'm a bit confused at how to manage the layout of my components in Java (I want to do it manually and not handle it by a layout manager). There are these methods in a Component:

  • layout, doLayout
  • validate, invalidate, revalidate
  • validateTree, invalidateTree
  • setSize, setBounds, setPreferredSize
  • getSize, getBounds, getPreferr开发者_如何转开发edSize
  • paint, repaint, update
  • updateUI

Earlier, I have tried to overload various combinations of the above but I was not quite sure which one to overload and what exactly I have to do inside and what functions I have to call on child components.


What I am doing now is:

  • Only overload doLayout of the above.
  • In doLayout, for all child components:
    • Call child.doLayout.
    • Call child.setBounds (sometimes before child.setBounds, sometimes after, sometimes both).
  • In doLayout, because I am doing the layout, I automatically have calculated also its preferred size.
  • In doLayout, call this.setPreferredSize.
  • In all constructors, call: this.setLayout(null).
  • In some constructors, call: this.doLayout. (And if I don't, it doesn't display correctly.)
  • When I do some operation where I must redo the layout (e.g. I dynamically have added some textfield in some container and thus I want to resize the container and also all parents accordingly), I call container.revalidate().

Remaining problems:

  • I think I still haven't really understood what function is calling what, what I have to overload and how to handle.
  • In doLayout, I call this.setPreferredSize. doLayout itself often depends on this.getSize(). For the parent, often child.setBounds depends on child.getPreferredSize(). So I have the dilema that in some cases, I first have to call child.doLayout and then child.setBounds and in some other cases the other way around. And in some cases even more complicated. So it all seems that I have to call this.setPreferredSize somewhere else. But where? Because it always has to be updated when the size changes (that is why I had overloaded setBounds earlier but that was even more ugly).
  • I have everything inside a JScrollPane which sets the scrollbars according to viewportView.getPreferredSize(). The revalidate I am calling in cases where I want to recalculate the layout causes doLayout calls which correctly call setPreferredSize for all components/containers in the hierarchy. Though, it seems that the JScrollPane sets its scrollbar before the doLayouts got called and thus it is always wrong. How can I fix that?

Some further thoughts (please comment on them) about how I maybe could fix the JScrollPane problem (haven't really tried because it would require some major rewrites, so I wanted to ask first):

  • Remove all setPreferredSize calls in doLayout.
  • Overload getPreferredSize and call doLayout from there (to get the preferred size).

-- OR --

  • Instead of calling revalidate when I do something which requires to redo the layout, call validateTree.

-- OR --

  • Instead of calling revalidate when I do something which requires to redo the layout, call all doLayout manually and then a revalidate on the JScrollPanel.

And finally, how to I go about the circular dependency of size and preferred size? I.e., I quite often have this case:

  • comp.width is fixed at the root. I can set the width on the root and recursively go down to all childs and set its width.
  • comp.height is fixed at the most inner child and depends on its width. So after I have set all widths, I can calculate and sets the heights from the bottom up.

I cannot call setPreferredSize before I haven't called setSize. And I cannot call setSize before I haven't called setPreferredSize.


All of your issues and questions could be solved if you used LayoutManagers. You seem to be trying really hard to avoid them but really they are your best ally when it comes to layout out GUI items.

A lot of people try to avoid LayoutManagers at first because of the learning curve. What I found very useful when I was learning them was the visual guide to layout managers: http://download.oracle.com/javase/tutorial/uiswing/layout/visual.html.

My suggestion would be to find a way to get your layout to work with LayoutManagers. Even you solve some of these issues, making changes or someone else having to read your code is going to be very time consuming. Plus with everything you have to do to get this to work there is a lot of room for bugs due to leaving something out on one component.


I am afraid you are in a wrong track.

Use Stewart's answer and set a null layout manager. That allows you to set the position of all components by calling setBounds().

Then, add a listener on the window which is called whenever the window is resized, and recalculate the positions and call setBounds().

This all can be automated if you implement your positioning code as a layoutmanager. (After all, positioning the components at resize == layout management, so believe or not, you are developing a layout manager, it is this simple!)

public class MyLayout implements LayoutManager,LayoutManager2 {

    @Override
    public void addLayoutComponent(Component comp, Object constraints) {
        // TODO Auto-generated method stub

    }

    @Override
    public Dimension maximumLayoutSize(Container target) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public float getLayoutAlignmentX(Container target) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public float getLayoutAlignmentY(Container target) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void invalidateLayout(Container target) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addLayoutComponent(String name, Component comp) {
        // TODO Auto-generated method stub

    }

    @Override
    public void removeLayoutComponent(Component comp) {
        // TODO Auto-generated method stub

    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void layoutContainer(Container parent) {
        // Now call setBounds of your components here
    }

}

In the layoutContainer method, you can call the setBounds of all the components. This method is called when the window is laid out initially, as well as every time when there is a resize.

Then, when you e.g. put things to a window or to a JPanel, simply setLayoutManager(new MyLayoutManager()) and you're golden.

However, a very crude question still remains. Your layout manager is a separate class, but still it has to access to the components you're created elsewhere in your window code. The brute-force solution is to simply get a reference to all components in the constructor, e.g.:

class MyWindow extends JFrame {
   public MyWindow() { 
       JLabel label=new JLabel("Hello");
       JButton button=new JButton("Ok");

       setLayoutManager(new MyLayoutManager(label,button)); // PASS THEM
       add(label);
       add(button);
       pack();
       setVisible(true);
   }
}

Of course, it is a naive approach, but could work. The proper way for handling this is to implement the addLayoutComponent in your layout manager, which is getting called whenever you add anything to a JFrame (such as when calling add(label)). This way the layout manager is aware of the components in the layout.


After reading your comments, it seems that you just want to do absolute positioning using a null layout. There's a tutorial on it, but the gist is that you set the layout manager to null for your container, and then call setBounds() on each component in that container to set its size and position, like this:

JPanel p = new JPanel();
p.setLayout(null);

JButton b = new JButton ("Hit It");
p.add(b);
b.setBounds(new Rectangle(10, 20, 100, 50));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜