分享

JAVA.SWT/JFace: SWT布局管理器

 qiujingLib 2012-02-09

7.1 布局管理器概述

FillLayout(充满式布局):在单行或单列中放置相同大小的控件,是最简单的布局。
RowLayout(行列式布局):在单行或者多行中放置控件,应用了fill、wrap和spacing等选项。
GridLayout(网格式布局):向表格一样放置控件。
FormLayout(表格式布局):与GridLayout功能差不多的布局,可以通过定义4个边的“附加值”来放置控件。
StackLayout(堆栈式布局):类似堆栈式的布局,只显示最上方的控件。

7.2 FillLayout(充满式布局)

规则:试图填充一行或一列,尽可能的充满整个面板,并且强制所有控件平均分配大小。FillLayout不会自动执行,也不能设置每个控件之间的空隙,但能够指定面板的四周的空白。

FillLayout layout = new FillLayout(SWT.VERTICAL);

FillLayout layout = new FillLayout();
layout.type = SWT.VERTICAL; // 默认为:SWT.HORIZONTAL

水平填充(SWT.HORIZONTAL):

垂直填充(SWT.VERTICAL):


设置四周补白:
   FillLayout layout = new FillLayout();
   layout.type=SWT.VERTICAL;
   layout.marginHeight = 10; // 设置上下补白高度
   layout.marginWidth = 20; // 设置左右
   layout.spacing = 5;    // 设置控件之间的空隙

   shell.setLayout( layout );
显示效果:

7.3 RowLayout(行列式布局)

RowLayout填充控件时可以折行显示,并且可以使用RowData设置某一个指定控件的大小。
package www..ch7;

import org.eclipse.swt.SWT;

public class RowLayoutSample {

public static void main(String[] args) {
   Display display = new Display();
   Shell shell = new Shell(display, SWT.SHELL_TRIM);

   RowLayout layout = new RowLayout();
   layout.type = SWT.HORIZONTAL;// 设置水平填充
   layout.marginLeft = 5;// 左补白
   layout.marginTop = 5;// 上补白
   layout.marginRight = 5;// 右补白
   layout.marginBottom = 5;// 下补白
   layout.spacing = 2;// 控件的间隙
   layout.wrap = true;// 是否折行显示
   layout.pack = false;// false:控件平均分配大小
   layout.justify = true;// 是否充满整个一行
   shell.setLayout(layout);

   new Button(shell, SWT.NONE).setText("B1");
   new Button(shell, SWT.NONE).setText("Button2");
   new Button(shell, SWT.NONE).setText("Wide Button3");
   new Button(shell, SWT.NONE).setText("B4");

   shell.layout();
   shell.pack();
   shell.open();
   while (!shell.isDisposed()) {
    if (!display.readAndDispatch())
     display.sleep();
   }
   display.dispose();

}
}

显示效果:

设置控件的大小:
//   layout.pack = false;// false:控件平均分配大小
   Button b = new Button(shell, SWT.NONE);
   b.setText("RowData");
   b.setLayoutData(new RowData(100, 30));

显示效果:


设置是否等宽或等高:fill属性
当以水平方式填充时,fill属性试图使所用控件具有同样高度;当以垂直方式显示时,试图使用所有控件具有同样宽度。
水平填充,设置等高:layout.fill = true;


垂直填充,设置等宽:layout.fill = true;

7.4 GridLayout(网格式布局)

使用GridLayout布局,控件将会按照网格的方式进行填充。GridLayout所放置的控件可以有一个关联的布局数据对象GridData。GridLayout的强大功能在于,可以使用GridData为每一个控件设置不同的布局。

package www..ch7;

import org.eclipse.swt.SWT;

public class GridLayoutSample {

public static void main(String[] args) {
   Display display = new Display();
   Shell shell = new Shell(display, SWT.SHELL_TRIM);

   GridLayout gridLayout = new GridLayout();
   gridLayout.numColumns = 3; //设置网格的列数
   gridLayout.makeColumnsEqualWidth = true; //设置网格等宽
   gridLayout.verticalSpacing = 10;
   gridLayout.horizontalSpacing = 10;
   shell.setLayout(gridLayout);
   new Button(shell, SWT.PUSH).setText("B1");
   new Button(shell, SWT.PUSH).setText("Wide Button 2");
   new Button(shell, SWT.PUSH).setText("Button 3");
   new Button(shell, SWT.PUSH).setText("B4");
   new Button(shell, SWT.PUSH).setText("Button 5");
   new Button(shell, SWT.PUSH).setText("B6");

   //GridData
   Button button = new Button(shell, SWT.PUSH);
   button.setText("GridData");
   button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL|GridData.GRAB_HORIZONTAL));

   Button b1 = new Button(shell, SWT.PUSH);
   b1.setText("GridData2");
   GridData gridData = new GridData();
//   gridData.horizontalIndent = 20; // 缩进
   gridData.horizontalSpan =2; // 水平跨越两个单元格
   gridData.horizontalAlignment = SWT.FILL; //充满
   b1.setLayoutData(gridData);
  
   Button b2 = new Button(shell, SWT.PUSH);
   b2.setText("GridData3");
   GridData gridData2 = new GridData();
   gridData2.horizontalSpan =2; // 水平跨越两个单元格
   gridData2.verticalSpan = 2;   // 垂直跨越两个单元格
   gridData2.horizontalAlignment = SWT.FILL; //水平充满
   gridData2.verticalAlignment = SWT.FILL;   //垂直充满
   gridData2.grabExcessHorizontalSpace = true; //设置水平抢占
   gridData2.grabExcessVerticalSpace = true; //设置垂直抢占
   gridData2.minimumHeight=100; //最小高度
   gridData2.minimumWidth =100; //最小宽度
   gridData2.widthHint=100; //设置宽度
   gridData2.heightHint=100; //设置高度
  
   b2.setLayoutData(gridData2);
  
   shell.layout();
   shell.pack();
   shell.open();
   while (!shell.isDisposed()) {
    if (!display.readAndDispatch())
     display.sleep();
   }
   display.dispose();

}
}

注意:不要重用GridData对象。每一个面板(Composite)对象中被GridLayout管理的控件必须有一个唯一的GridData对象。如果在设置布局时一个GridLayout中的控件的GridData为null,就会为它创建一个唯一的GridData对象。

注意:设置了widthHint和heightHint属性后只是在程序刚一运行时才会起作用,随着窗口的改变,会重新计算控件大小。

显示效果:


去掉下面两行后的效果:
   gridData2.grabExcessHorizontalSpace = true; //设置水平抢占
   gridData2.grabExcessVerticalSpace = true; //设置垂直抢占

按钮“GridData3”未随着窗口大小调整高度。

样式常量对照表
样式常量 <==> 对应属性值
GRAB_HORIZONTAL <==> grabExcessHorizontalSpace=true
GRAB_VERTICAL <==> grabExcellVerticalSpace=true
HORIZONTAL_ALIGN_BEGINNING   <==> horizontalAlignment=SWT.BEGINNING
HORIZONTAL_ALIGN_CENTER <==> horizontalAlignment=SWT.CENTER
HORIZONTAL_ALIGN_END <==> horizontalAlignment=SWT.END
HORIZONTAL_ALIGN_FILL <==> horizontalAlignment=SWT.FILL
VERTICAL_ALIGN_BEGINNING   <==> verticalAlignment=SWT.BEGINNING
VERTICAL_ALIGN_CENTER <==> verticalAlignment=SWT.CENTER
VERTICAL_ALIGN_END <==> verticalAlignment=SWT.END
VERTICAL_ALIGN_FILL <==> verticalAlignment=SWT.FILL
FILL_BOTH <==> horizontalAlignment=SWT.FILL + verticalAlignment=SWT.FILL

7.5 FormLayout(表格式布局)

FormLayout通过设置FormData四边的附加值(FormAttachment对象)来设置控件的布局。一个附加值让一个控件指定的一边附件到父面板容器类(Composite)的位置或者其他控件上。所以,这种布局可以指定某两个控件的相对位置,并且能随着窗口的改变而改变。

FormAttachment 的使用说明及示例参考:SWT 之 FormAttachment

7.6 StackLayout(堆栈式布局)

StackLayout堆栈式布局类似于选项卡(TabFolder),当前只显示最上方的控件。例如,面板上有10个文本框,面板设置为StackLayout布局,当单击“显示下一个文本框”按钮时,下一个文本框就显示出来,这样面板中始终只有一个文本框,设置最上方显示控件的属性是layout.topControl。

package www.;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class testStackLayout {

/**
* @param args
*/
public static void main(String[] args) {
   Display display = new Display();
   Shell shell = new Shell(display);
   shell.setLayout(new GridLayout());

   // 创建放置文本框的面板
   final Composite parent = new Composite(shell, SWT.NONE);
   // 设置面板的布局数据
   parent.setLayoutData(new GridData(GridData.FILL_BOTH));
   // 创建堆栈式布局
   final StackLayout layout = new StackLayout();
   // 将堆栈式布局应用于模板
   parent.setLayout(layout);
   // 创建10个文本框
   final Text[] textArray = new Text[10];
   for (int i = 0; i < textArray.length; i++) {
    textArray[i] = new Text(parent, SWT.MULTI);
    textArray[i].setText("这是第 " + i + " 个文本框");
   }
   // 设置堆栈中当前显示的控件
   layout.topControl = textArray[0];

   Button b = new Button(shell, SWT.PUSH);
   b.setText("显示下一个文本框");
   // 保存当前显示的文本框的索引值
   final int[] index = new int[1];
   // 为按钮添加单击事件
   b.addListener(SWT.Selection, new Listener() {

    @Override
    public void handleEvent(Event arg0) {
     // 计算出下一个文本框的索引数
     index[0] = (index[0] + 1) % textArray.length;
     // 设置当前显示的控件
     layout.topControl = textArray[index[0]];
     // 重新刷新布局
     parent.layout();
    }
   });
   shell.setSize(200, 150);
   shell.open();
   shell.layout();
//   shell.pack();
  
   while (!shell.isDisposed()) {
    if (!display.readAndDispatch()) {
     display.sleep();
    }
   }
   display.dispose();  
}
}

显示效果:
点击按钮后:

7.7 自定义布局管理器

任何布局类都是Lyaout的子类,Layout是一个抽象类,源代码如下:

package org.eclipse.swt.widgets;
import org.eclipse.swt.graphics.*;

public abstract class Layout {
protected abstract Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache);
protected boolean flushCache (Control control) {
return false;
}
protected abstract void layout (Composite composite, boolean flushCache);
}

创建一个自定义的布局类要继承Layout类,并且要实现Layout中的抽象方法。以下代码创建的是一个最简单的自定义类MyLayout:
package www.;

import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Layout;

public class MyLayout extends Layout {

// 该方法计算面板显示的大小
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
   return new Point(wHint, hHint);
}

// 设置子控件的位置
protected void layout(Composite composite, boolean flushCache) {
  
}
}

Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache)方法:该方法是计算布局的大小,也就是按照一定的规则算法计算出最终布局的长和宽,其中wHint和hHint是设置默认的宽和高。例如:当计算出来的长和宽小于默认的宽和高时,就可以使用默认的宽和高。flushCache参数设置是否使用缓存的数据。
void layout(Composite composite, boolean flushCache)方法:该方法是对该面板(参数Composite)中所有的控件(Control)设置显示的具体位置,通常获得该面板中的所有控件的方法是composite.getChildren()。这样就可以根据指定的计算规则来放置每个控件的位置了。

综上所述,创建一个自定义布局关键是实现这两个方法,而具体布局的设置要根据设定的计算方法来实现。

布局计算的常用方法
1. 控件(Control)类中的常用方法
◆ 计算控件合适的大小的方法:Point computeSize(int wHint, int hHint)和Point computeSize(int wHint, int hHint, boolean changed)。例如:
   Point point = control.computeSize(SWT.DEFAULT, SWT.DEFAULT);
   int width = point.x;
   int height = point.y;

◆ 获得控件当前坐标位置的方法:Rectangle getBounds()。例如:
   Rectangle rect = control.getBounds();
   int left = rect.x;
   int right = rect.width;
   int top = rect.y;
   int bottom = rect.height;

◆ 设置控件位置的方法:setBounds(int x, int y, int width, int height)或setBounds(Rectangle rect)。
2. 面板(Composite)类中的常用方法
◆ 获得面板的大小区域的方法:Rectangle getClientArea()。
◆ 获得所有子控件的方法:Control[] getChildren()。
◆ 获得面板的布局对象:Layout getLayout()。

自定义布局类(BorderLayout)

BorderLayout布局将控件按东、南、西、北、中5个区域放置,每个方向最多只能放置一个控件,随着窗口大小的改变,整个窗口会不断撑大。
(1)创建一个BorderData类,该类设置控件所在位置。
package www..ch7.testBorderLayout;

import org.eclipse.swt.SWT;

public final class BorderData {
public int region = SWT.CENTER; // 默认为中间

public BorderData() {
}
public BorderData(int region) {
   this.region = region;
}
}

(2)编写最重要的BorderLayout类,该类的详细代码如下:
package www..ch7.testBorderLayout;

import org.eclipse.swt.SWT;

public class BorderLayout extends Layout {
// 定义存放在不同位置的5个控件
private Control north;
private Control south;
private Control east;
private Control west;
private Control center;

@Override
protected Point computeSize(Composite composite, int wHint, int hHint,
    boolean flushCache) {
   getControls(composite);
   // 定义面板的宽和高
   int width = 0, height = 0;
   // 计算面板的宽度
   width += west == null ? 0 : getSize(west, flushCache).x;
   width += east == null ? 0 : getSize(east, flushCache).x;
   width += center == null ? 0 : getSize(center, flushCache).x;
   // 如果上部和下部都有控件,则宽取较大值
   if (north != null) {
    Point pt = getSize(north, flushCache);
    width = Math.max(width, pt.x);
   }
   if (south != null) {
    Point pt = getSize(south, flushCache);
    width = Math.max(width, pt.x);
   }

   // 计算面板的高度
   height += north == null ? 0 : getSize(north, flushCache).y;
   height += south == null ? 0 : getSize(south, flushCache).y;

   int heightOther = center == null ? 0 : getSize(center, flushCache).y;
   if (west != null) {
    Point pt = getSize(west, flushCache);
    heightOther = Math.max(heightOther, pt.y);
   }
   if (east != null) {
    Point pt = getSize(east, flushCache);
    heightOther = Math.max(heightOther, pt.y);
   }
   height += heightOther;

   // 计算的宽和高与默认的宽和高作比较,返回之中较大的
   return new Point(Math.max(width, wHint), Math.max(height, hHint));
}

@Override
protected void layout(Composite composite, boolean flushCache) {
   getControls(composite);
   // 获得当前面板可显示的区域
   Rectangle rect = composite.getClientArea();
   int left = rect.x, right = rect.width, top = rect.y, bottom = rect.height;
   // 将各个控件放置到面板中
   if (north != null) {
    Point pt = getSize(north, flushCache);
    north.setBounds(left, top, rect.width, pt.y);
    top += pt.y;
   }
   if (south != null) {
    Point pt = getSize(south, flushCache);
    south.setBounds(left, rect.height - pt.y, rect.width, pt.y);
    bottom -= pt.y;
   }
   if (east != null) {
    Point pt = getSize(east, flushCache);
    east.setBounds(rect.width - pt.x, top, pt.x, (bottom - top));
    right -= pt.x;
   }
   if (west != null) {
    Point pt = getSize(west, flushCache);
    west.setBounds(left, top, pt.x, (bottom - top));
    left += pt.x;
   }
   if (center != null) {
    center.setBounds(left, top, (right - left), (bottom - top));
   }
}

// 计算某一控件当前的大小,长和宽
protected Point getSize(Control control, boolean flushCache) {
   return control.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
}

// 设置该类中每个位置控件的属性的方法
protected void getControls(Composite composite) {
   // 获得当前面板中所有的控件对象
   Control[] children = composite.getChildren();
   // 循环所有控件,并将每个控件所放的位置对号入座
   for (int i = 0; i < children.length; i++) {
    Control child = children[i];
    BorderData borderData = (BorderData) child.getLayoutData();
    if (borderData.region == SWT.TOP) {
     north = child;
    } else if (borderData.region == SWT.BOTTOM) {
     south = child;
    } else if (borderData.region == SWT.RIGHT) {
     east = child;
    } else if (borderData.region == SWT.LEFT) {
     west = child;
    } else {
     center = child;
    }
   }
}
}
(3)最后创建一个测试类:
package www..ch7.testBorderLayout;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class TestBorderLayout {

/**
* @param args
*/
public static void main(String[] args) {
   Display display = new Display();
   Shell shell = new Shell(display);
   shell.setSize(200, 150);
   shell.setLayout(new BorderLayout());

   Button buttonWest = new Button(shell, SWT.PUSH);
   buttonWest.setText("左");
   buttonWest.setLayoutData(new BorderData(SWT.LEFT));
  
   Button buttonEast = new Button(shell, SWT.PUSH);
   buttonEast.setText("右");
   buttonEast.setLayoutData(new BorderData(SWT.RIGHT));
  
   Button buttonNorth = new Button(shell, SWT.PUSH);
   buttonNorth.setText("上");
   buttonNorth.setLayoutData(new BorderData(SWT.TOP));

   Button buttonSouth = new Button(shell, SWT.PUSH);
   buttonSouth.setText("下");
   buttonSouth.setLayoutData(new BorderData(SWT.BOTTOM));
  
   Text text = new Text(shell, SWT.MULTI);
   text.setText("中间");
   text.setLayoutData(new BorderData());
  
   shell.pack();
   shell.open();
  
   while (!shell.isDisposed()) {
    if (!display.readAndDispatch()) {
     display.sleep();
    }
   }
   display.dispose();
}
}

显示效果:
拖放后的效果:

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多