John Jiang

a cup of Java, cheers!
https://github.com/johnshajiang/blog

   :: 首页 ::  :: 联系 :: 聚合  :: 管理 ::
  131 随笔 :: 1 文章 :: 530 评论 :: 0 Trackbacks
Custom Layout Manager: PyramidLayout
    已有太多关于自定义部局管理器的文章了。本文仅是一篇学习笔记,描述了如何实现一种像堆金字塔似的部局管理器,很简单,也有点儿意思,可能你也会感兴趣的。(2012.07.17最后更新)   

    I have developed Swing application for several years, although I'm not professional GUI developer, I'm shamed of never creating any custom layout manager. Maybe the existing Swing layout managers are too powerful to create new ones. At least, GridBagLayout is powerful enough for my real works. And we have much more flexible GroupLayout and SpringLayout, of course, both of them are too complex, in fact I never use them directly. However I indirectly take advantage of GroupLayout due to using NetBeans' GUI designer Matisse.

1. Layout Manager basics
    Let's start with some layout manager foundation. Before this time I learn to customize layout, I always think layout manager is very mysterious. Layout is like a magic player that put a variety of components to right positions in containers. I haven't browsed any code of any layout, event the simplest one. That's why I think layout is mystery. But it's simple for me now.
    Generally, all of layout implements one or both of LayoutManager and LayoutManager2 interfaces. LayoutManager2 is LayoutManager's sub-interface, then if someone implements LayoutManager2 that means it really implements LayoutManager. Mostly all modern layouts implements LayoutManager2.
    Interface LayoutManager defines the basic methods must be implemented by every layout, all of them are intuitional: add new component--addLayoutComponent(); remove component--removeLayoutComponent(); calculate preferred size--preferredLayoutSize(); calculate minimum size--minimumLayoutSize(); how to layout the components--layoutContainer(). Absolutely, the layoutContainer() method is essential, you must instruct the parent container how to allocate space(bounds) for every component.
    The extension interface LayoutManager2 introduces more methods that if you have to: support constraints--addLayoutComponent(Component, Object); specify maximum size--maximumLayoutSize(); specify alignment--getLayoutAlignmentX() and getLayoutAlignmentY(); destroy specific caches or reset some variables when invaliding container--invalidateLayout().

2. PyramidLayout
    Now let's feature a simple and funny layout manager--PyramidLayout. The layout allows container to add components like building a Pyramid, as shown as the image below,

    As the above, PyramidLayout puts the first component on the bottom, then puts the second on top of the first, but its bounds is smaller, ... It looks like a Pyramid, doesn't it? Here is the full codes,
public class PyramidLayout implements LayoutManager2 {

    
private List<Component> comps = new LinkedList<Component>();

    
public void addLayoutComponent(final Component comp,
            
final Object constraints) {
        
synchronized (comp.getTreeLock()) {
            comps.add(comp);
        }
    }

    
public void addLayoutComponent(final String name, final Component comp) {
        addLayoutComponent(comp, null);
    }

    
public void removeLayoutComponent(final Component comp) {
        
synchronized (comp.getTreeLock()) {
            comps.remove(comp);
        }
    }

    
public float getLayoutAlignmentX(final Container target) {
        
return 0.5F;
    }

    
public float getLayoutAlignmentY(final Container target) {
        
return 0.5F;
    }

    
public void invalidateLayout(final Container target) {
        System.out.println();
    }

    
public Dimension preferredLayoutSize(final Container parent) {
        
synchronized (parent.getTreeLock()) {
            Insets insets = parent.getInsets();
            
int width = insets.left + insets.right;
            
int height = insets.top + insets.bottom;
            
if (comps.size() == 0) {
                
return new Dimension(width, height);
            }

            Dimension size = comps.get(0).getPreferredSize();
            width += size.width;
            height += size.height;

            
return new Dimension(width, height);
        }
    }

    
public Dimension minimumLayoutSize(final Container parent) {
        
synchronized (parent.getTreeLock()) {
            Insets insets = parent.getInsets();
            
int width = insets.left + insets.right;
            
int height = insets.top + insets.bottom;
            
if (comps.size() == 0) {
                
return new Dimension(width, height);
            }

            Dimension size = comps.get(0).getMinimumSize();
            width += size.width;
            height += size.height;

            
return new Dimension(width, height);
        }
    }

    
public Dimension maximumLayoutSize(final Container target) {
        
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    
public void layoutContainer(final Container parent) {
        
synchronized (parent.getTreeLock()) {
            Dimension parentSize = parent.getSize();
            
int compsCount = comps.size();
            Dimension step = new Dimension(parentSize.width / (2 * compsCount),
                    parentSize.height / (2 * compsCount));

            
for (int i = 0; i < compsCount; i++) {
                Component comp = comps.get(i);
                comp.setBounds(calcBounds(parentSize, step, i));
                parent.setComponentZOrder(comp, compsCount - i - 1);
            }
        }
    }

   
private Rectangle calcBounds(Dimension parentSize, Dimension step, int index) {
        
int x = step.width * index;
        
int y = step.height * index;
        
int width = parentSize.width - step.width * 2 * index;
        
int height = parentSize.height - step.height * 2 * index;
        
return new Rectangle(x, y, width, height);
    }
}
    Collection instance "comps" manages all of components, in this case, I take a LinkedList object to add and remove UI components. The layout doesn't concern any constraint, so the two addLayoutComponent() methods have the same actions. Please see the codes for details.
    As aforementioned, layoutContainer() method really takes charge of layouting the components. The key work is allocating space for each component, namely, specifying the bounds. Calculating bounds values just applies the simplest arithmetic operations.
    According to the intention, the bottom component fills the whole parent container, so it determines the preferred and the minimum sizes. For details, please take a look at methods preferredLayoutSize() and minimumLayoutSize(). Since the layout manager doesn't take care of the maximum size, the maximumLayoutSize() method simply returns a constant value.
posted on 2012-07-15 22:14 John Jiang 阅读(1107) 评论(3)  编辑  收藏 所属分类: EnglishJavaSEJavaSwingGUI

评论

# re: Custom Layout Manager: PyramidLayout 2012-07-15 22:16 Sha Jiang
第一次写英文文章,字数不多,以后还会多练习^_^  回复  更多评论
  

# re: Custom Layout Manager: PyramidLayout 2012-07-16 08:50 chenth
@Sha Jiang
英语大有长进啊  回复  更多评论
  

# re: Custom Layout Manager: PyramidLayout 2012-07-16 09:52 Sha Jiang
@chenth
阅读、写作倒在其次,最令我兴奋的是,听力有了实质性的提高:D 不过,离流利的听、说还有不小的差距,以后路还很长...  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: