您的位置:首页 > 编程语言 > Java开发

JAVA.SWT/JFace: RCP应用篇之Eclipse表单(三)

2013-10-21 23:15 381 查看
《Eclipse SWT/JFACE 核心应用》 清华大学出版社 24 Eclipse表单

24.4.1 表格布局(TableWrapLayout)

表格布局(TableWrapLayout)是基于网格的布局,它与 SWT 的通用 GridLayout 非常类似,不同之处在于,布局规则更类似于 HTML 中的布局算法,可以支持文本的自动换行。

下面来比较一下 GridLayout 与 TableWrapLayout 两种布局的效果:



通过两种布局的比较可以看出,TableWrapLayout 可以将超出的文本折行显示,这是在 GridLayout 中所不能实现的。

与 GridLayout 对应的 GridData 类似,TableWrapLayout 对应 TableWrapData。下面就以一个复杂的示例程序来说明如何使用 TableWrapLayout 进行布局设置。

package com.testrcp.myrcp.forms;

import org.eclipse.swt.SWT;

import org.eclipse.swt.widgets.Label;

//import org.eclipse.ui.forms.IManagedForm;

import org.eclipse.ui.forms.IManagedForm;

import org.eclipse.ui.forms.editor.FormEditor;

import org.eclipse.ui.forms.editor.FormPage;

import org.eclipse.ui.forms.widgets.FormToolkit;

import org.eclipse.ui.forms.widgets.ScrolledForm;

import org.eclipse.ui.forms.widgets.TableWrapData;

import org.eclipse.ui.forms.widgets.TableWrapLayout;

public class FirstPage extends FormPage {

public static final String ID = "com.testrcp.myrcp.forms.FirstPage";

public FirstPage(FormEditor editor) {

// 构造方法,设置Form页的ID和名称

super(editor, ID, "第一页");

}

// 覆盖父类中的方法

// 在该方法中创建表单区域的各种控件

protected void createFormContent(IManagedForm managedForm) {

// 通过managedForm对象获得表单工具对象

FormToolkit toolkit = managedForm.getToolkit();

// 通过managedForm对象获得ScrolledForm可滚动的表单对象

ScrolledForm form = managedForm.getForm();

// 设置表单文本

form.setText("这是第一页,Hello, Eclipse 表单");

// 创建表格布局

TableWrapLayout layout = new TableWrapLayout();

layout.numColumns = 2;// 表格的列数

layout.bottomMargin = 10;// 下补白

layout.topMargin = 10;// 上补白

layout.leftMargin = 10;// 左补白

layout.rightMargin = 10;// 右补白

form.getBody().setLayout(layout);// 设置表格的布局

// 创建第一个标签

@SuppressWarnings("unused")

Label l1 = toolkit.createLabel(form.getBody(), "这是很长的一段文本文本1", SWT.WRAP);

// 创建第二个标签

Label l2 = toolkit.createLabel(form.getBody(), "这是文本2", SWT.WRAP);

// 创建一个TableWrapData对象,设置为水平和垂直充满式填充

TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB);

// 将布局数据应用到第二个标签

l2.setLayoutData(td);

Label l3 = toolkit.createLabel(form.getBody(), "这是文本3", SWT.WRAP);

// 第三个标签的布局数据

td = new TableWrapData();

td.colspan = 2;// 设置单元格的跨两列

l3.setLayoutData(td);

// 第四个标签的布局数据

Label l4 = toolkit.createLabel(form.getBody(), "这是文本4", SWT.WRAP);

td = new TableWrapData();

td.rowspan = 2;// 设置单元格跨两行

td.grabVertical = true;// 垂直抢占

l4.setLayoutData(td);

@SuppressWarnings("unused")

Label l5 = toolkit.createLabel(form.getBody(), "这是文本5", SWT.WRAP);

@SuppressWarnings("unused")

Label l6 = toolkit.createLabel(form.getBody(), "这是文本6", SWT.WRAP);

form.getBody().setBackground(form.getBody().getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));

}

}

效果如下:



缩放后的效果如下:



24.4.2 列布局(ColumnLayout)

列布局(ColumnLayout)与 SWT 中的 RowLayout 布局类似,相对于 TableWrapLayout 布局简单的所。该种布局的列数可根据窗口的大小进行调整,总是试图使用更多的列来显示。当窗口宽度减小时,列数也随之减少。总之,列布局(ColumnLayout)的目的是为了使各个控件都均匀的分布在界面上。

package com.testrcp.myrcp.forms;

import org.eclipse.swt.SWT;

import org.eclipse.swt.layout.GridLayout;

import org.eclipse.swt.widgets.Composite;

import org.eclipse.ui.forms.IManagedForm;

import org.eclipse.ui.forms.editor.FormEditor;

import org.eclipse.ui.forms.editor.FormPage;

import org.eclipse.ui.forms.events.ExpansionAdapter;

import org.eclipse.ui.forms.events.ExpansionEvent;

import org.eclipse.ui.forms.widgets.ColumnLayout;

import org.eclipse.ui.forms.widgets.FormToolkit;

import org.eclipse.ui.forms.widgets.ScrolledForm;

import org.eclipse.ui.forms.widgets.Section;

public class SecondPage extends FormPage {

public static final String ID = "com.testrcp.myrcp.forms.SecondPage";

public SecondPage(FormEditor editor) {

super(editor, ID, "第二页");

}

protected void createFormContent(IManagedForm managedForm) {

ScrolledForm form = managedForm.getForm();

form.setText("ColumnLayout");

//创建列布局

ColumnLayout layout = new ColumnLayout();

layout.topMargin = 0;//上补白

layout.bottomMargin = 5;//下补白

layout.leftMargin = 10;//左补白

layout.rightMargin = 10;//右补白

layout.horizontalSpacing = 10;//水平方向控件的距离

layout.verticalSpacing = 10;//垂直方向控件的距离

layout.maxNumColumns = 4;//最大的列数

layout.minNumColumns = 1;//最小的列数

form.getBody().setLayout(layout);//设置表单的布局为列布局

//创建四个内容区

for (int i = 0; i < 4; i++)

createSectionWithLinks(managedForm, "Section" + i, "This is Section " + i, i);

}

//创建内容区及其控件

private void createSectionWithLinks(IManagedForm mform, String title, String desc, int nlinks) {

//创建内容去面板

Composite client = createSection(mform, title, desc, 1);

FormToolkit toolkit = mform.getToolkit();

//创建内容区中的控件

for (int i = 1; i <= nlinks; i++)

toolkit.createHyperlink(client, "link" + i, SWT.WRAP);

}

//创建内容区的方法

private Composite createSection(IManagedForm mform, String title, String desc, int numColumns) {

final ScrolledForm form = mform.getForm();

FormToolkit toolkit = mform.getToolkit();

//创建内容区

Section section = toolkit.createSection(form.getBody(), Section.TWISTIE | Section.TITLE_BAR | Section.DESCRIPTION | Section.EXPANDED);

section.setText(title);

section.setDescription(desc);

Composite client = toolkit.createComposite(section);

GridLayout layout = new GridLayout();

layout.marginWidth = layout.marginHeight = 0;

layout.numColumns = numColumns;

client.setLayout(layout);

//设置内容区的面板

section.setClient(client);

section.addExpansionListener(new ExpansionAdapter() {

public void expansionStateChanged(ExpansionEvent e) {

form.reflow(false);

}

});

return client;

}

}

显示效果如下:



奇怪,上面的第二行显示在中间了,谁会调整的给我留言。

调整宽度后的效果:



24.5 表单的高级应用

表单的高级应用部分主要体现在提供了 Master/Details 模式的应用,通过表单可以轻松的实现这种模式。

24.5.1 Master/Details 模式

Master/Details 是 UI 设计中常见的一种模式。该种模式由一组(列表或成树状结构)Master 和一个被选中 Master 驱动的 Details 集组成。Master 是一些不同的对象,通过对 Master 的驱动,驱动 Details 的 UI 发生变化。Master/Details 非常适用于 Eclipse 表单编程。

例如,编辑 plugin.xml 文件的扩展页面时就是使用的该种模式。如下图,编辑扩展的左侧是所有的扩展列表,也就是 Master 部分。当单击左侧的不同扩展信息时,右侧会产生不同的详细信息,也就是Detail 部分。



24.5.2 实现 Master/Details 示例程序

下面就以一个具体的例子来说明如何使用 Eclipse 表单来实现 Master/Detail 的界面效果。

在这个示例程序中,作为 Master 的一个树界面,显示的是文件的结构目录,但左侧选中不同的文件或文件夹,右侧的 Detail 部分显示对应的详细信息。随着选中的不同,详细信息也不同。

显示文件夹时:



显示文件时:



选择“垂直布局”后的显示效果:



下面就来详细看一下如何实现这样的程序,步骤如下:

(1)该页面为编辑器页面的一个页面,首页首先要在对应的编辑器页面添加一个页面。在 MyMutiForm.java 类的源代码中,在 addPages 方法中添加一个页面,代码如下:

addPage(new MasterDetailPage(this));

(2)所添加的 MasterDetailPage 对象,即为本例所示的 Master/Detail 页面。与之前所学习的创建页面的方法相同,该类继承自 FormPage 类,以下为代码:

package com.testrcp.myrcp.forms;

import org.eclipse.ui.forms.IManagedForm;

import org.eclipse.ui.forms.editor.FormEditor;

import org.eclipse.ui.forms.editor.FormPage;

import org.eclipse.ui.forms.widgets.ScrolledForm;

import com.testrcp.myrcp.forms.advance.FileMasterDetailsBlock;

public class MasterDetailPage extends FormPage {

public static final String ID = "com.testrcp.myrcp.forms.MasterDetailPage";

// 声明MasterDetail页面部分对象

private FileMasterDetailsBlock block;

public MasterDetailPage(FormEditor editor) {

super(editor, ID, "Master/Detail页");

block = new FileMasterDetailsBlock(this);

}

/*

* ManagedForm封装了form元素的生命周期管理与各个form元素之间的事件通知

* ManagedForm本身并不是一个form,他包含了一个form并且可以注册IFormPart。

* 可以将ManagedForm看作是'viewers',form和managed form之间的关系就好像

* Table与TableViewer的关系一样。

*/

protected void createFormContent(IManagedForm managedForm) {

// 获得表单对象

ScrolledForm form = managedForm.getForm();

// 设置表单的标题

form.setText("这是一个浏览文件的Master/Detail页面");

// 该方法非常重要,负责创建Master和Detail区域,尽量在最后调用

block.createContent(managedForm);

}

}

该页面与之前使用的编辑器页面不同,在构造方法中创建了一个 FileMasterDetailsBlock 对象,通过该对象的 createContent 方法可以创建 Master/Detail 页面中的各种控件。稍后详细讲述该类。

(3)具体创建页面控件的部分是在 FileMasterDetailsBlock 类中实现的。该类继承自 MasterDetailsBlock,具有 Master/Detail 模式的页面类都要继承自 MasterDetailsBlock 类,并实现该类的3个抽象方法。

◇ createMasterPart:在该方法中创建 Master 部分的控件,可以是表格、树或列表等。本例中使用的是 TreeViewer。

◇ createToolBarActions:创建页面的 Action 操作的方法,本例中创建两个按钮,可以设置垂直布局或是水平布局。

◇ registerPages:注册 Master 对应的 Detail 部分的控件,可同时注册多个 Detail 页面。实际上,一个 Master/Detail 页面是通过一个 SashForm 来布局的,Master 放置在 SashForm 的左侧或上侧,而 Detail 则放置在 SashForm 的右侧或下侧。

FileMasterDetailsBlock.java:

package com.testrcp.myrcp.forms.advance;

import java.io.File;

import myrcp.Activator;

import org.eclipse.jface.action.Action;

import org.eclipse.jface.viewers.*;

import org.eclipse.swt.SWT;

import org.eclipse.swt.graphics.Image;

import org.eclipse.swt.layout.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.ui.*;

import org.eclipse.ui.forms.*;

import org.eclipse.ui.forms.editor.FormPage;

import org.eclipse.ui.forms.widgets.*;

public class FileMasterDetailsBlock extends MasterDetailsBlock {

@SuppressWarnings("unused")

private FormPage page;

public FileMasterDetailsBlock(FormPage page) {

this.page = page;

}

//父类中的抽象方法,创建Master部分

protected void createMasterPart(final IManagedForm managedForm, Composite parent) {

FormToolkit toolkit = managedForm.getToolkit();

//创建一个内容区

Section section = toolkit.createSection(parent, Section.DESCRIPTION | Section.TITLE_BAR);

section.setText("浏览文件");

section.marginWidth = 10;

section.marginHeight = 5;

//创建内容区的面板

Composite client = toolkit.createComposite(section, SWT.WRAP);

//绘制该面板的边框,与表单的风格一致

toolkit.paintBordersFor(client);

GridLayout layout = new GridLayout();

layout.numColumns = 2;

layout.marginWidth = 2;

layout.marginHeight = 2;

client.setLayout(layout);

//创建一个树,使用toolkit对象创建

Tree tree = toolkit.createTree(client, SWT.NULL);

GridData gd = new GridData(GridData.FILL_BOTH);

gd.heightHint = 20;

gd.widthHint = 100;

tree.setLayoutData(gd);

section.setClient(client); // 书上没有这行,明明连tree都显示不了嘛。。。

/*

IFormPart管理了整个Part的dirty state, saving, commit, focus, selection changes等等这样的事件。

并不是表单中的每一个-空间站都需要成为一个IFormPart,最好将一组control通过实现IFormPart变成一个Part.

一般来说Section就是一个自然形成的组,所以Eclipse Form提供了一个SectionPart的实现,

它包含一个Section的对象。

*/

final SectionPart spart = new SectionPart(section);

//注册该对象到IManagedForm表单管理器中

managedForm.addPart(spart);

//将普通的树包装成MVC的树

TreeViewer viewer = new TreeViewer(tree);

//注册树的选择事件监听器

viewer.addSelectionChangedListener(new ISelectionChangedListener() {

//当单击树中某一个节点时

public void selectionChanged(SelectionChangedEvent event) {

//通过IManagedForm来发布IFormPart所对应的事件

managedForm.fireSelectionChanged(spart, event.getSelection());

}

});

//设置树的内容

viewer.setContentProvider(new MasterContentProvider());

//设置树的标签

viewer.setLabelProvider(new MasterLabelProvider());

//设置初始化输入的类

viewer.setInput(new File("E:\\Data"));

}

//注册详细页面部分

protected void registerPages(DetailsPart detailsPart) {

//将DirectoryDetailPage对象注册

detailsPart.registerPage(File.class, new DirectoryDetailPage());

}

//创建表单区的Action

protected void createToolBarActions(IManagedForm managedForm) {

final ScrolledForm form = managedForm.getForm();

//水平布局操作

Action hAction = new Action("horizon", Action.AS_RADIO_BUTTON) {

public void run() {

sashForm.setOrientation(SWT.HORIZONTAL);

form.reflow(true);

}

};

hAction.setChecked(true);

hAction.setToolTipText("水平布局");

hAction.setImageDescriptor(Activator.getImageDescriptor("icons/hor.gif"));

//垂直布局操作

Action vAction = new Action("vertical", Action.AS_RADIO_BUTTON) {

public void run() {

sashForm.setOrientation(SWT.VERTICAL);

form.reflow(true);

}

};

vAction.setChecked(false);

vAction.setToolTipText("垂直布局"); //$NON-NLS-1$

vAction.setImageDescriptor(Activator.getImageDescriptor("icons/ver.gif"));

//将这两个操作添加到表单的工具栏管理器中

form.getToolBarManager().add(hAction);

form.getToolBarManager().add(vAction);

}

public class MasterContentProvider implements ITreeContentProvider {

public Object[] getChildren(Object element) {

return ((File) element).listFiles();

}

public Object[] getElements(Object element) {

return ((File) element).listFiles();

}

public boolean hasChildren(Object element) {

Object[] obj = getChildren(element);

return obj == null ? false : obj.length > 0;

}

public Object getParent(Object element) {

return ((File) element).getParentFile();

}

public void dispose() {

}

public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {

}

}

class MasterLabelProvider implements ILabelProvider {

public Image getImage(Object element) {

File file = (File) element;

if (file.isDirectory())

return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);

else

return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);

}

public String getText(Object element) {

String text = ((File) element).getName();

if (text.length() == 0) {

text = ((File) element).getPath();

}

return text;

}

public void addListener(ILabelProviderListener listener) {

}

public void dispose() {

}

public boolean isLabelProperty(Object element, String property) {

return false;

}

public void removeListener(ILabelProviderListener listener) {

}

}

}

通过以上代码可以看出,创建的 Master 部分其实就是之前学习的浏览文件的 TreeViewer。但注意所不同的是,单击树中的某一个节点事件处理部分,需要通过 IManagedForm 的 fireSelectionChanged 方法来发布事件。

作为接收的事件处理,需要在注册的 Detail 页面进行,所注册的详细信息的部分都在 registerPages 方法中实现。本例中 中注册了一个 Detail,如下:

detailsPart.registerPage(File.class, new DirectoryDetailPage());

当然也可以注册多个 Detail 页面。

(4)最后看一下如何实现本例中的 Detail 页面,注册的 DirecotryDetailPage 对象即为具体实现 Detail 部分。该类需要实现 IDetailsPage 接口,主要是在 createContents 方法中创建 Detail 所需使用的控件,并且在 selectionChanged 方法中处理当 Master 对象选中事件发生的代码,具体实现如下:

package com.testrcp.myrcp.forms.advance;

import java.io.File;

import java.util.Date;

import org.eclipse.jface.viewers.ISelection;

import org.eclipse.jface.viewers.IStructuredSelection;

import org.eclipse.swt.SWT;

import org.eclipse.swt.layout.GridLayout;

import org.eclipse.swt.widgets.Button;

import org.eclipse.swt.widgets.Composite;

import org.eclipse.swt.widgets.Text;

import org.eclipse.ui.forms.IDetailsPage;

import org.eclipse.ui.forms.IFormPart;

import org.eclipse.ui.forms.IManagedForm;

import org.eclipse.ui.forms.widgets.FormToolkit;

import org.eclipse.ui.forms.widgets.Section;

import org.eclipse.ui.forms.widgets.TableWrapData;

import org.eclipse.ui.forms.widgets.TableWrapLayout;

public class DirectoryDetailPage implements IDetailsPage {

private IManagedForm mform;

private File file;

private Section fileSection;

private Text fileName;

private Text filePath;

private Text lastModify;

private Button isRead;

private Button isWrite;

private Composite client;

@SuppressWarnings("deprecation")

public void createContents(Composite parent) {

//设置父类面板的布局

TableWrapLayout layout = new TableWrapLayout();

layout.topMargin = 5;

layout.leftMargin = 5;

layout.rightMargin = 2;

layout.bottomMargin = 2;

parent.setLayout(layout);

//获得表单工具对象

FormToolkit toolkit = mform.getToolkit();

//创建Detail的内容区

fileSection = toolkit.createSection(parent, Section.DESCRIPTION|Section.TITLE_BAR);

TableWrapData td = new TableWrapData(TableWrapData.FILL, TableWrapData.TOP);

td.grabHorizontal = true;

fileSection.setLayoutData(td);

//创建内容区的所设置的面板

client = toolkit.createComposite(fileSection);

fileSection.setClient( client );

GridLayout gridLayout = new GridLayout();

gridLayout.marginWidth = 0;

gridLayout.marginHeight = 0;

gridLayout.numColumns = 2;

gridLayout.horizontalSpacing=10;

client.setLayout(gridLayout);

//创建Detail部分具体的各种控件

toolkit.createLabel( client , "名称:");

fileName = toolkit.createText( client ,"");

toolkit.createLabel( client , "路径:");

filePath = toolkit.createText( client , "");

toolkit.createLabel( client , "最后修改:");

lastModify = toolkit.createText( client , file!=null?new Date(file.lastModified()).toLocaleString():"");

isRead = toolkit.createButton( client , "是否可读" ,SWT.CHECK);

isWrite = toolkit.createButton( client , "是否可写" ,SWT.CHECK);

}

public void initialize(IManagedForm form) {

this.mform = form ;

}

public void dispose() {

}

public boolean isDirty() {

return false;

}

public void commit(boolean onSave) {

}

public boolean setFormInput(Object input) {

return false;

}

public void setFocus() {

}

public boolean isStale() {

return false;

}

public void refresh() {

}

//当Master区域选中事件发生时

public void selectionChanged(IFormPart part, ISelection selection) {

//首先获得选中的对象

IStructuredSelection currentSelection = (IStructuredSelection)selection;

if (currentSelection.size()==1)

file = (File)currentSelection.getFirstElement();

//如果选中的对象不为null,则刷新控件所显示的值

if (file != null)

update();

}

//刷新值

@SuppressWarnings("deprecation")

public void update (){

//如果选中的为文件夹

if ( file.isDirectory()){

fileSection.setText("文件夹");

fileSection.setDescription("这是一个文件夹");

}else{//否则

fileSection.setText("文件");

fileSection.setDescription("这是一个文件");

}

//设置文件名

fileName.setText(file.getName());

//设置路径

filePath.setText(file.getAbsolutePath());

//设置上次修改

lastModify.setText(new Date(file.lastModified()).toLocaleString());

//设置是否只读

isRead.setSelection( file.canRead());

//设置是否可写

isWrite.setSelection( file.canWrite() );

}

}

这样,一个完整的 Master/Detail 模式实现的表单就完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: