LayoutManager 直接子类:GridLayout, FlowLayout等 调用:java.awt.Container调用doLayout时调用 代码如下 (参考java.awt.Container类) LayoutManager layoutMgr = this.layoutMgr; if (layoutMgr != null) { layoutMgr.layoutContainer(this); } 接口方法说明:
//目前还没研究出其更多用处 void addLayoutComponent(String name, Component comp);
//目前还没研究出其更多用处 void removeLayoutComponent(Component comp);
//得到此container 的理想尺寸,实现的时候常常根据该container中各个component //的理想尺寸和布局原则来确定 Dimension preferredLayoutSize(Container parent);
//得到该container的最小尺寸。 Dimension minimumLayoutSize(Container parent);
//container调用此方法用于布局 void layoutContainer(Container parent);
FlowLayout 分析
预备知识(熟悉的人可以忽略这一步) 1.设定的component的大小位置 我们常常看到有很多size方法,如setBounds(),setSize(),setPreferredSize()等,真正在布局用的是 setSize()确定其大小,容器将如实按照此大小来绘制 该component.setLoaction用于确定该component 在container中的的位置,同样,容器将如实按照此位置绘制该component 2.跨平台:由于java是垮平台语言,所以在布局的时候考虑的各个系统坐标方向可能是从左到右,也可能是 从右到左,因此,java在component中包含了getComponentOrientation()用于判断系统的方向,布局的时候, 因充分考虑到此特性
分析
在做任何布局管理器前都应确定布局原则,如FlowLayout的原则就是将component依次加入到container中 如果一行排不满,则换到下一行,可选择从左往右加,也可以选择从右往左加,还可以选择从中间开始排
[pre]//原则是将所有的component取出,并排成一行,取各个component的宽度之和(包括hgap)为container理想宽度 //取最高component的为container理想的高度 public Dimension preferredLayoutSize(Container target) { //获得锁 synchronized (target.getTreeLock()) { Dimension dim = new Dimension(0, 0); //得到容器下的所有的component int nmembers = target.getComponentCount(); boolean firstVisibleComponent = true;
for (int i = 0 ; i < nmembers ; i++) { Component m = target.getComponent(i); if (m.visible) { //不可见的component将不显示 Dimension d = m.getPreferredSize(); dim.height = Math.max(dim.height, d.height); if (firstVisibleComponent) { firstVisibleComponent = false; } else { //component间应有hgap间隙 dim.width += hgap; } dim.width += d.width; } } Insets insets = target.getInsets(); dim.width += insets.left + insets.right + hgap*2; dim.height += insets.top + insets.bottom + vgap*2; return dim; } } //container将调用此方法来布局 //原则是总是试图将component排到一行,如果container的宽度已经不够,则另起一行,开始排 //下一行 public void layoutContainer(Container target) { synchronized (target.getTreeLock()) { Insets insets = target.getInsets(); //最大宽度 int maxwidth = target.width - (insets.left + insets.right + hgap*2); int nmembers = target.getComponentCount(); int x = 0, y = insets.top + vgap; //rowh 用于只是下一行应从哪(y坐标)开始 //start 用于指示新的一行应该是从第几个component开始 int rowh = 0, start = 0;
boolean ltr = target.getComponentOrientation().isLeftToRight();
for (int i = 0 ; i < nmembers ; i++) { Component m = target.getComponent(i); if (m.visible) { Dimension d = m.getPreferredSize(); m.setSize(d.width, d.height);
if ((x == 0) || ((x + d.width) <= maxwidth)) { if (x > 0) { x += hgap; } x += d.width; //确定一行的高度 rowh = Math.max(rowh, d.height); } else { //布局该行 //x坐标应是 insets.left + hgap //y 坐标 vgap + rowh //从第start开始排,一直排到第i个 moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, i, ltr); x = d.width; y += vgap + rowh; rowh = d.height; start = i; } } } //布局剩下的一行 moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, nmembers, ltr); } }[/pre]
制作自己的布局管理器首先,应确定自己的布局原则是什么,如下 1.将JLabel,JTextField(JTextArea,JPanel),JLabel做为一个布局单元 第一个label常用于前缀(为了方便叙说,通称为preLabel), 第二个component则作为输入内容(为了叙说方便,成为inputComponent), 第三个label作为附加说明,通常做为后缀(为了方便叙说,通成为suffixLabel。
2。容器可以设定列数,表示每行可以放多少个布局单元,每列的宽度为容器实际宽度/列数
3。对于容器中的每列, 实际是又按照preLable,inputComponent,suffixLabel分成三列 第一列的宽度为所有属于该列的布局单元中所有preLabel的最大宽度 第三列宽度同上,为有属于该列的布局单元中所有suffixLabel的最大宽度 第二列宽度为剩下的值,随着容器大小而动态改变
[pre]import java.awt.*; import java.util.*; import javax.swing.*; import dl.*;
public class DialogLayout implements LayoutManager { protected int m_divider = -1; protected int m_hGap = 10; protected int m_vGap = 5; //如果inputComponent为JTextField则认为该布局单元的高度为25 public static int HEIGHT = 25; //容器平均分成多少列 int col = 2;
public DialogLayout(int col) { this.col = col; }
public DialogLayout() { this.col = 2; }
public DialogLayout(int hGap, int vGap,int col) { m_hGap = hGap; m_vGap = vGap; this.col = col; }
public void addLayoutComponent(String name, Component comp) {}
public void removeLayoutComponent(Component comp) {}
public Dimension preferredLayoutSize(Container parent) {
int thisWidth=0;; int thisHeight = 0; Insets insets = parent.getInsets(); Vector[] v = this.getSimpleComponents(parent,col); for(int i =0 ;i<col;i++) { Dimension d = this.getPreferredSize(v[i],parent);
thisWidth = thisWidth+d.width; thisHeight = Math.max(thisHeight,d.height); }
Vector ve = this.getComplexComponents(parent); int height = 0; for(int i= 0;i<ve.size();i++) { Component jc = (Component)ve.get(i); Dimension d = jc.getPreferredSize(); height+=d.height+m_vGap; //height+=HEIGHTER+m_vGap; } return new Dimension(thisWidth,thisHeight+height);
}
public Dimension minimumLayoutSize(Container parent) { return preferredLayoutSize(parent); }
public void layoutContainer(Container parent) { Insets insets = parent.getInsets(); int startx = insets.left; int starty = insets.top; //得到实际尺寸;?? Dimension d =parent.getSize(); int partWidth = d.width/col; Vector[] v = this.getSimpleComponents(parent,col);
Divider[] divider = new Divider[col]; for(int i = 0;i<col;i++) { divider[i] = this.getDivider(v[i],parent); if(i==0) { //比较第一列的label最大值 divider[i].left = Math.max(divider[i].left,this.getComplexDivider(parent)); } } int cols=-1; ;
for(int i=1,count=0;i<parent.getComponentCount();i+=3) { Component mjc = (Component)parent.getComponent(i); Component label = (Component)parent.getComponent(i-1);
Component suffix = (Component)parent.getComponent(i+1);
if(isLongTextFields(mjc)) { startx = insets.left; //starty =starty+this.m_vGap; label.setBounds(startx,starty,divider[0].left,HEIGHT);
mjc.setBounds(startx+divider[0].left+m_hGap,starty, d.width-(insets.right+insets.left+divider[divider.length-1].right+divider[0].left+2*m_hGap),mjc.getPreferredSize().height); starty = starty + mjc.getPreferredSize().height+this.m_vGap; //找到一个textArea,计数器加一 //mjc.setBounds(startx,starty,d.width,HEIGHT); //count++; cols = -1 ;
continue;
} else { //int cols = (i-1-count)/col;//得到所在列 cols = (cols+1)%col;
startx = partWidth*cols+insets.left; int w = partWidth - divider[cols].left - divider[cols].right-2*this.m_hGap; label.setBounds(startx,starty,divider[cols].left,HEIGHT); //label.setBounds(3,3,30,20); mjc.setBounds(startx+divider[cols].left+m_hGap,starty,w,HEIGHT); suffix.setBounds(startx+w+divider[cols].left+2*m_hGap,starty,divider[cols].right,HEIGHT); //mjc.setBounds(startx,starty,partWidth,HEIGHT); //starty+=HEIGHT+m_vGap; if(cols == divider.length-1) { starty+=HEIGHT+m_vGap;//下一行 cols = -1; continue; }
if(parent.getComponentCount()>(i+3)) { Component mjc1 = (Component)parent.getComponent(i+3); if(isLongTextFields(mjc1)) { starty+=HEIGHT+m_vGap; } }
}
} }
public int getHGap() { return m_hGap; }
public int getVGap() { return m_vGap; }
public String toString() { return "javamonkey"; } //----------------- protected Dimension getPreferredSize(Vector v,Container parent) { int iSize = v.size(); //int cSize = parent.getPreferredSize(); int lbWidth = 0; int sfWidth = 0; int mjcWidth = 0; int height = 0; Insets insets = parent.getInsets(); for(int i = 0;i<iSize;i++) { Component mjc = (Component)v.get(i); int count = this.find(parent,mjc); Component label = parent.getComponent(count-1); Component suffix = parent.getComponent(count+1); lbWidth = Math.max(lbWidth,label.getPreferredSize().width); sfWidth = Math.max(sfWidth,suffix.getPreferredSize().width); mjcWidth = Math.max(mjcWidth,mjc.getPreferredSize().width); //高度都定为label高度 //height = height+label.getPreferredSize().height+m_vGap; height = height+HEIGHT+m_vGap; } int thisWidth = lbWidth+m_hGap+mjcWidth+m_hGap+sfWidth; int thisHeight = height; return new Dimension(thisWidth,thisHeight); }
private Divider getDivider(Vector v,Container parent) { int iSize = v.size(); //int cSize = parent.getPreferredSize(); int lbWidth = 0; int sfWidth = 0; int mjcWidth = 0;
for(int i = 0;i<iSize;i++) { Component mjc = (Component)v.get(i); int count = this.find(parent,mjc); Component label = parent.getComponent(count-1); Component suffix = parent.getComponent(count+1); lbWidth = Math.max(lbWidth,label.getPreferredSize().width); sfWidth = Math.max(sfWidth,suffix.getPreferredSize().width); mjcWidth = Math.max(mjcWidth,mjc.getPreferredSize().width); } return new Divider(lbWidth,sfWidth); }
private Vector[] getSimpleComponents(Container parent,int col) { Vector v = new Vector(); for (int k=1 ; k<parent.getComponentCount(); k+=3) {
Component mjc = (Component)parent.getComponent(k); if(!isLongTextFields(mjc))v.add(mjc);;
} Vector[] array = new Vector[col];
for (int k=0 ; k<col; k++) { array[k] = new Vector(); } for (int k=0 ; k<v.size(); k++) { int cols = k%col;
Component mjc = (Component)v.get(k);
array[cols].add(mjc);
}
return array;
}
private Vector getComplexComponents(Container parent) { Vector v = new Vector(); for (int k=1 ; k<parent.getComponentCount(); k+=3) {
Component mjc = (Component)parent.getComponent(k); if(isLongTextFields(mjc))v.add(mjc);;
}
return v;
}
private int getComplexDivider(Container parent) { Vector v = getComplexComponents(parent); int iSize = v.size();
int lbWidth = 0;
for(int i = 0;i<iSize;i++) { Component mjc = (Component)v.get(i); int count = this.find(parent,mjc); Component label = parent.getComponent(count-1); lbWidth = Math.max(lbWidth,label.getPreferredSize().width);
} return lbWidth;
} private boolean isLongTextFields(Component mjc) { if(mjc instanceof javax.swing.JTextArea) return true; if(mjc instanceof javax.swing.JPanel) return true; else { return false; }
} private int find(Container parent,Component cp) { Component[] cps = parent.getComponents(); for(int i=0;i<cps.length;i++) { if(cp.equals(cps[i]))return i; } return -1; } } //用于表示每个列的所有preLable,suffixLabel最大值 class Divider { int left; int right; public Divider(int left,int right) { this.left = left; this.right =right; }
}[/pre]
应用布局管理器
凡是刚接触swing的,无不对布局头疼,以下是flowLayout和DialogLayout的综合使用,可以看到,只用它们俩个(有时候,还需要用BorderLayout用于宏观布局),就可以轻松做出比较美观的复杂界面
列子如下
[pre]import java.awt.*; import java.awt.event.*; import javax.swing.*;
/** * <p>Title: </p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2002</p> * <p>Company: </p> * @author javamonkey * @version 1.0 */
public class Frame4 extends JFrame { private JPanel contentPane;
//Construct the frame public Frame4() { enableEvents(AWTEvent.WINDOW_EVENT_MASK); try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } //Component initialization private void jbInit() throws Exception { //setIconImage(Toolkit.getDefaultToolkit().createImage(Frame4.class.getResource("[Your Icon]"))); contentPane = (JPanel) this.getContentPane(); contentPane.setLayout(new DialogLayout()); //第一组布局单元 contentPane.add(new JLabel("date:")); contentPane.add( new JTextField()); contentPane.add( new suffixLabel()); //第二组布局单元 contentPane.add( new JLabel("from:")); contentPane.add( new JTextField()); contentPane.add( new suffixLabel()); //第三组布局单元 contentPane.add( new JLabel("memo:")); JTextArea tx = new JTextArea(); tx.setPreferredSize(new Dimension(0,50)); contentPane.add(tx ); contentPane.add( new suffixLabel()); //第四组布局单元 contentPane.add( new JLabel("cash:")); contentPane.add(new JTextField()); contentPane.add( new suffixLabel("$"));
//第五组布局单元 JPanel pl = new JPanel(); pl.setLayout(new FlowLayout(FlowLayout.LEFT)); pl.setBorder(BorderFactory.createEtchedBorder()); pl.add(new JLabel("性别:")); pl.add(new JRadioButton("男")); pl.add(new JRadioButton("女")); contentPane.add(new Label()); contentPane.add(pl); contentPane.add(new suffixLabel());
//
this.setSize(new Dimension(400, 300)); this.setTitle("Frame Title"); } //Overridden so we can exit when window is closed protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if (e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); } } }
class suffixLabel extends JLabel { public suffixLabel(String str) { super(str); this.setPreferredSize(new Dimension(10,0));
} public suffixLabel() { super(); } }[/pre]
|