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

《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 模式实现的表单就完成了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值