Jface中form组件中水平排列支持自动换行

来源:互联网 发布:足球财富网数据频道 编辑:程序博客网 时间:2024/05/26 02:55

最近做项目有需求,要求在form组件中添加多个Section,Section的数量不固定,Section的大小固定,并且根据窗口的大小自动将排列超出一行的Section换到下一行。

第一个想到的是用布局中的RowLayout,使用RowLayout确实自动换行了,但是如果内容太多,超过窗口的高度时,form组件不显示垂直滚动条,我的form确实是ScrolledForm啊,为什么不显示出滚动条呢?我想可能是ScrolledForm不完全支持RowLayout布局,毕竟RowLayout不是专门为form组件设计的在这个问题上我没去深入的研究,在form中使用RowLayout失败。


第二个想到的是用布局中的ColumnLayout,使用ColumnLayout自动换行并且超出窗口的内容自动显示滚动条。但问题又来了,用户希望按照我们看书写字的习惯,从左到右显示一行,显示不下换第二行。而ColumnLayout是从上到下显示,显示不下再换到第二列显示。


ColumnLayout布局


现成的布局不能满足,只能动手自已写一个满足我需要的布局了,在参照了ColumnLayout布局后,开始写自己的布局。我把它命名为ColumnHorizontalLayout,代码如下:

package org.eclipse.ui.forms.widgets;import org.eclipse.swt.SWT;import org.eclipse.swt.graphics.Point;import org.eclipse.swt.graphics.Rectangle;import org.eclipse.swt.widgets.Composite;import org.eclipse.swt.widgets.Control;import org.eclipse.swt.widgets.Layout;public class ColumnHorizontalLayout extends Layout implements ILayoutExtension {/** * Minimum number of columns (default is 1). */public int minNumColumns = 1;/** * Maximum number of columns (default is 3). */public int maxNumColumns = 3;/** * Horizontal spacing between columns (default is 5). */public int horizontalSpacing = 5;/** * Vertical spacing between controls (default is 5). */public int verticalSpacing = 5;/** * Top margin (default is 5). */public int topMargin = 5;/** * Left margin (default is 5). */public int leftMargin = 5;/** * Bottom margin (default is 5). */public int bottomMargin = 5;/** * Right margin (default is 5). */public int rightMargin = 5;/** * Creates a new instance of the column layout. */public ColumnHorizontalLayout() {}protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {if (wHint == 0)return computeSize(composite, wHint, hHint, minNumColumns);else if (wHint == SWT.DEFAULT)return computeSize(composite, wHint, hHint, maxNumColumns);elsereturn computeSize(composite, wHint, hHint, -1);}private Point computeSize(Composite parent, int wHint, int hHint, int ncolumns) {Control[] children = parent.getChildren();int cwidth = 0;int cheight = 0;Point[] sizes = new Point[children.length];int cwHint = SWT.DEFAULT;if (ncolumns != -1) {cwHint = wHint - leftMargin - rightMargin - (ncolumns - 1) * horizontalSpacing;if (cwHint <= 0)cwHint = 0;elsecwHint /= ncolumns;}for (int i = 0; i < children.length; i++) {sizes[i] = computeControlSize(children[i], cwHint);cwidth = Math.max(cwidth, sizes[i].x);//组件的最大高度cheight += sizes[i].y;}//计算出需要的创建的列数if (ncolumns == -1) {// must computencolumns = (wHint - leftMargin - rightMargin - horizontalSpacing) / (cwidth + horizontalSpacing);ncolumns = Math.min(ncolumns, children.length);ncolumns = Math.max(ncolumns, minNumColumns);ncolumns = Math.min(ncolumns, maxNumColumns);}//找出最长列的高度int perColHeight = ColumnHorizontalUtils.computeColumnHeight(ncolumns, sizes, cheight, verticalSpacing);Point size = new Point(0, 0);size.y = perColHeight;size.x = cwidth * ncolumns + (ncolumns - 1) * horizontalSpacing;size.x += leftMargin + rightMargin;size.y += topMargin + bottomMargin;return size;}private Point computeControlSize(Control c, int wHint) {ColumnLayoutData cd = (ColumnLayoutData) c.getLayoutData();int widthHint = cd != null ? cd.widthHint : wHint;int heightHint = cd != null ? cd.heightHint : SWT.DEFAULT;return c.computeSize(widthHint, heightHint);}/* * (non-Javadoc) *  * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, *      boolean) */protected void layout(Composite parent, boolean flushCache) {Control[] children = parent.getChildren();Rectangle carea = parent.getClientArea();int cwidth = 0;Point[] sizes = new Point[children.length];for (int i = 0; i < children.length; i++) {sizes[i] = computeControlSize(children[i], SWT.DEFAULT);cwidth = Math.max(cwidth, sizes[i].x);}int ncolumns = (carea.width - leftMargin - rightMargin + horizontalSpacing) / (cwidth + horizontalSpacing);ncolumns = Math.min(ncolumns, children.length);ncolumns = Math.max(ncolumns, minNumColumns);ncolumns = Math.min(ncolumns, maxNumColumns);int realWidth = (carea.width - leftMargin - rightMargin + horizontalSpacing) / ncolumns - horizontalSpacing;int fillWidth = Math.max(cwidth, realWidth);int x = leftMargin;int y=0;for (int i = 0; i < sizes.length; i++) {Control child = children[i];Point csize = sizes[i];ColumnLayoutData cd = (ColumnLayoutData) child.getLayoutData();int align = cd != null ? cd.horizontalAlignment : ColumnLayoutData.FILL;int childWidth = align == ColumnLayoutData.FILL ? fillWidth : csize.x;System.out.println(childWidth);}for (int i = 0; i < sizes.length; i++) {Control child = children[i];Point csize = sizes[i];ColumnLayoutData cd = (ColumnLayoutData) child.getLayoutData();int align = cd != null ? cd.horizontalAlignment : ColumnLayoutData.FILL;int childWidth = align == ColumnLayoutData.FILL ? fillWidth : csize.x;//当前的列int colsIndex = i%ncolumns;x = leftMargin + colsIndex * (fillWidth + horizontalSpacing);//计算组件y的位置//如果为第一行if(i/ncolumns==0){y = topMargin;}else{int aboveRowIndex = i/ncolumns-1;int aboveColIndex = aboveRowIndex*ncolumns+i%ncolumns;Control abovechild = children[aboveColIndex];y = abovechild.getBounds().height+abovechild.getBounds().y + verticalSpacing;}child.setBounds(x, y, childWidth, csize.y);}}/* * (non-Javadoc) *  * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMaximumWidth(org.eclipse.swt.widgets.Composite, *      boolean) */public int computeMaximumWidth(Composite parent, boolean changed) {return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;}/* * (non-Javadoc) *  * @see org.eclipse.ui.forms.widgets.ILayoutExtension#computeMinimumWidth(org.eclipse.swt.widgets.Composite, *      boolean) */public int computeMinimumWidth(Composite parent, boolean changed) {return computeSize(parent, 0, SWT.DEFAULT, changed).x;}}

package org.eclipse.ui.forms.widgets;import org.eclipse.swt.graphics.Point;public class ColumnHorizontalUtils {/* * Compute the minimum required height by iteration. The first guess is to *  * This method is public to allow for JUnit testing */public static int computeColumnHeight(int ncolumns, Point[] sizes, int totalHeight, int verticalMargin) {int averageHeight = ( totalHeight + sizes.length * verticalMargin ) / ncolumns;int requiredHeight = computeActualHeight(ncolumns, sizes, averageHeight, verticalMargin);if (averageHeight == requiredHeight) {return requiredHeight;}// Try making the columns shorter, repeat up to 10 times, usually one or two iterations will be sufficientfor ( int i = 0; i < 10; i++ ) {int candidateHeight = computeActualHeight(ncolumns, sizes, requiredHeight - 1, verticalMargin);if ( candidateHeight >= requiredHeight ) {return requiredHeight;}requiredHeight = candidateHeight;}return requiredHeight;}private static int computeActualHeight(int ncolumns, Point[] sizes, int candidateHeight, int verticalMargin ) {int maxHeight = 0;int[] colsHeight = new int[ncolumns];//计划每列的高度for (int i = 0; i < sizes.length; i++) {int colsIndex = i%ncolumns;int childHeight = sizes[i].y;//如果不是第一行需要加上verticalMargin;if(colsHeight[colsIndex]>0){childHeight += verticalMargin;}//累加每列字元素的高度colsHeight[colsIndex] = childHeight+colsHeight[colsIndex];}//找出最高的列for(int i=0;i<colsHeight.length-1;i++){maxHeight = Math.max(colsHeight[i], colsHeight[i+1]);}return maxHeight;}}


修改后的图:


修改后的图


以这种方式显示的布局,需要里面的controls高度都一样,否则行与行之间对不齐。如果高度不一样,也可以写成行中每个control的高度等于这一行中最高的那个就行了,只是在这里没有做实现,我的高度是固定的,这个已经能满足需求了。