《Eclipse SWT/JFACE 核心應(yīng)用》 清華大學(xué)出版社 19 MVC的表格,、樹和列表
19.1 MVC概述
MVC是模型-視圖=控制器(Model-View-Controller)設(shè)計模式的簡稱。MVC模式將數(shù)據(jù)與視圖分開,,通過控制器來控制與數(shù)據(jù)交互,。
JFace中有關(guān)實現(xiàn)MVC模式的控件都在org.eclipse.jface.viewers包下。所有的MVC控件都繼承自View類,,該類是一個抽
象類,,不能直接創(chuàng)建對象,需使用其實現(xiàn)的子類,。常用的類如下:
◆ ListViewer類:列表控件,。
◆ TreeViewer類:樹控件。
◆ TableViewer類:表格控件,。
◆ TextViewer,、SourceViewer、ProjectionViewer為編輯文本的控件,。
19.2 表格(TableViewer)
表格程序的代碼:
package www.swt.com.ch19;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;
import www.swt.com.util.ImageFactory;
public class TableWindow extends ApplicationWindow {
private TableViewer table;//聲明一個表格對象
private List<PersonEO> persons ;//表格所存儲的數(shù)據(jù)
//表格中列的索引號
public static final int ID = 0;
public static final int NAME = 1;
public static final int GENDER = 2;
public static final int COLOR = 3;
//表格個的列名稱
public static final String[] COLUMN_NAME = { "ID號" ,"姓名","性別","喜歡顏色"};
public TableWindow() {
super(null);
initPersons();
}
//初始化表格數(shù)據(jù),,通常是從數(shù)據(jù)庫中讀出
private void initPersons() {
persons = new ArrayList<PersonEO>();
persons.add( new PersonEO(1,"張三","男","灰色"));
persons.add( new PersonEO(2,"李四","女","白色"));
}
//設(shè)置窗口的標(biāo)題和大小
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setSize(300, 200);
shell.setText("TableViewer程序示例");
}
//創(chuàng)建窗口的控件
protected Control createContents(Composite parent) {
//創(chuàng)建TableViewer對象
table = new TableViewer(parent ,SWT.FULL_SELECTION);
//table = CheckboxTableViewer.newCheckList( parent , SWT.FULL_SELECTION);
//創(chuàng)建表頭
for ( int i =0; i<COLUMN_NAME.length;i++){
new TableColumn(table.getTable(), SWT.LEFT).setText(COLUMN_NAME[i]);
table.getTable().getColumn(i).pack();
}
//設(shè)置排序器
table.setSorter( new TableSorter());
//分別為表頭的每一列注冊事件
TableColumn column = table.getTable().getColumn(0);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
//單擊時,重新設(shè)置排序?qū)ο髮傩?br>
((TableSorter)table.getSorter()).doSort(TableWindow.ID);
//刷新表格數(shù)據(jù)
table.refresh();
}
});
column = table.getTable().getColumn(1);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.NAME);
table.refresh();
}
});
column = table.getTable().getColumn(2);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.GENDER);
table.refresh();
}
});
column = table.getTable().getColumn(3);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.COLOR);
table.refresh();
}
});
//設(shè)置表頭和表格線可見
table.getTable().setHeaderVisible(true);
table.getTable().setLinesVisible( true );
//設(shè)置數(shù)據(jù)
table.setContentProvider(new MyContentProvider());
//設(shè)置視圖
table.setLabelProvider( new MyLabelProvider());
//設(shè)置表格數(shù)據(jù)對象,,該方法非常重要,,是表格數(shù)據(jù)入口
table.setInput(persons);
createContextMenu();
//設(shè)置列屬性
table.setColumnProperties( COLUMN_NAME );
//設(shè)置單元格編輯器對象數(shù)組
CellEditor[] editors = new CellEditor[4];
editors[0] = null;
editors[1] = new TextCellEditor(table.getTable());
editors[2] = new TextCellEditor(table.getTable());
editors[3] = new TextCellEditor(table.getTable());
//設(shè)置單元格編輯器
table.setCellEditors(editors);
//設(shè)置單元個修改器
table.setCellModifier( new ICellModifier(){
//如果該列為第一列,設(shè)置不能修改
public boolean canModify(Object element, String property) {
if ( property.equals(COLUMN_NAME[0]))
return false;
return true;
}
//當(dāng)處于編輯狀態(tài)時所顯示的值
public Object getValue(Object element, String property) {
PersonEO p = (PersonEO) element;
if ( property.equals(COLUMN_NAME[1]))
return p.getName();
else if ( property.equals(COLUMN_NAME[2]))
return p.getGender();
else if ( property.equals(COLUMN_NAME[3]))
return p.getColor();
return null;
}
//當(dāng)修改后設(shè)置更新數(shù)據(jù)
public void modify(Object element, String property, Object value) {
if (element instanceof Item)
element = ((Item) element).getData();
PersonEO p = (PersonEO) element;
if ( property.equals(COLUMN_NAME[1]))
p.setName( (String)value );
else if ( property.equals(COLUMN_NAME[2]))
p.setGender( (String)value );
else if ( property.equals(COLUMN_NAME[3]))
p.setColor( (String)value );
//刷新表格
table.refresh();
}
});
table.addDoubleClickListener( new IDoubleClickListener(){
public void doubleClick(DoubleClickEvent event) {
StructuredSelection select = (StructuredSelection) event.getSelection();
PersonEO p = (PersonEO) select.getFirstElement();
System.out.println(p.getName());
}
} );
return parent;
}
//創(chuàng)建上下文菜單
private void createContextMenu() {
MenuManager menu = new MenuManager();
menu.add( new AddAction() );
menu.add( new DelAction() );
//menu.add( new FileterAction() );
//獲得一個Menu對象
Menu m = menu.createContextMenu(getShell());
//將該對象設(shè)置為表格的菜單
table.getTable().setMenu( m );
}
public static void main(String[] args) {
TableWindow test = new TableWindow();
test.setBlockOnOpen(true);
test.open();
Display.getCurrent().dispose();
}
public class MyContentProvider implements IStructuredContentProvider {
//將初始化數(shù)據(jù)的入口對象轉(zhuǎn)換成表格使用的數(shù)組對象
public Object[] getElements(Object inputElement) {
if ( inputElement instanceof List )
return ((List) inputElement).toArray();
return null;
}
//釋放該對象時調(diào)用的方法
public void dispose() {
}
//當(dāng)表格中的數(shù)據(jù)改變時調(diào)用該方法
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
public class MyLabelProvider implements ITableLabelProvider {
//設(shè)置每個單元格所顯示的圖標(biāo)
public Image getColumnImage(Object element, int columnIndex) {
/*
//如果是性別所在的列
if (columnIndex == GENDER){
//類型轉(zhuǎn)換,,element代表表格中的一行
PersonEO person = (PersonEO)element;
//根據(jù)不同的值設(shè)置不同的圖標(biāo)
if ( person.getGender().equals("男"))
return ImageFactory.loadImage( Display.getCurrent(),ImageFactory.ICON_BOY);
else if ( person.getGender().equals("女"))
return ImageFactory.loadImage( Display.getCurrent(),ImageFactory.ICON_GIRL);
}
*/
return null;
}
//設(shè)置每個單元格所顯示的文字
public String getColumnText(Object element, int columnIndex) {
//類型轉(zhuǎn)換,,element代表表格中的一行
PersonEO person = (PersonEO)element;
if ( columnIndex == ID)//如果是第一列
return person.getID()+"";
else if ( columnIndex == NAME)//如果是第二列
return person.getName()+"";
else if ( columnIndex == GENDER)//如果是第三列
return person.getGender();
else if ( columnIndex == COLOR)//如果是第四列
return person.getColor()+"";
return "";
}
//釋放對象時釋放圖像資源
public void dispose() {
ImageFactory.dispose();
}
//以下方法為空實現(xiàn)
public void addListener(ILabelProviderListener listener) {
}
public boolean isLabelProperty(Object element, String property) {
return false;
}
public void removeListener(ILabelProviderListener listener) {
}
}
public class AddAction extends Action{
public AddAction(){
setText("添加");
}
public void run() {
//新建一個實體對象
PersonEO person = new PersonEO();
person.setID( table.getTable().getItemCount()+1);
person.setName("Janet");
person.setGender("女");
person.setColor("紅色");
//添加一行數(shù)據(jù)
table.add( person );
persons.add(person); // smx:如果沒有這行代碼,在點擊表頭排序時新添加的記錄會丟失
}
}
public class DelAction extends Action{
public DelAction(){
setText("刪除");
}
public void run() {
//獲得當(dāng)前的所選中的行
StructuredSelection selection = (StructuredSelection) table.getSelection();
//注意,,同時選中的可能是多行,,這是返回的是數(shù)組對象
//獲得數(shù)組對象的一個元素,也就是只有選中一行的情況
PersonEO p= (PersonEO)selection.getFirstElement();
//從表中刪除該行
table.remove(p);
}
}
public class FileterAction extends Action{
//聲明一個ViewerFilter對象
ViewerFilter femaleFilter;
public FileterAction(){
//設(shè)置標(biāo)題,,樣式為復(fù)選框樣式
super("篩選",AS_CHECK_BOX);
//內(nèi)部類創(chuàng)建該對象
femaleFilter = new ViewerFilter(){
//需實現(xiàn)ViewerFilter類中的抽象方法
public boolean select(Viewer viewer, Object parentElement, Object element) {
PersonEO p = (PersonEO)element;
return p.getGender().equals("女");
}
};
//初始化時設(shè)置為取消選中狀態(tài)
this.setChecked(false);
}
public void run() {
//如果此時選中,,則為表格設(shè)置過濾器
if (isChecked())
table.addFilter(femaleFilter );
else//否則,,移除表格過濾器
table.removeFilter(femaleFilter);
//刷新表格數(shù)據(jù)
table.refresh();
}
}
}
從以上程序的代碼中總結(jié)出使用TableViewer對象時應(yīng)注意以下問題:
◆ 創(chuàng)建TableViewer的構(gòu)造方法:
◇ TableViewer(Composite parent):使用默認(rèn)的樣式。
◇ TableViewer(Composite parent, int style):可設(shè)置表格的樣式,,樣式常量可以使用Table類使用的樣式常量,。
◇ TableViewer(Table table):可以將一個SWT表格對象作為構(gòu)造函數(shù)。
◆ TableViewer中的Table對象:TableViewer實際上封裝了SWT的Table對象,,通過table.getTable()可以獲得該Table對象,,表頭的設(shè)置和顯示與SWT的表格的方法相同。
◆
顯示表格的一般步驟:雖然TableViewer內(nèi)部使用了Table對象,,但創(chuàng)建表格數(shù)據(jù)的代碼中并不用創(chuàng)建TableItem來組織數(shù)據(jù),,因為
TableViewer將組織和顯示的數(shù)據(jù)交給了兩個接口對象IContentProvider和ITableLabelProvider。
IContentProvider負(fù)責(zé)將setInput方法傳入的對象轉(zhuǎn)化為表格所需要的對象,。ITableLabelProvider負(fù)責(zé)處理如何顯示數(shù)據(jù),。
要組織表格數(shù)據(jù)并顯示數(shù)據(jù),必須使用以下3個方法:
//設(shè)置數(shù)據(jù)
table.setContentProvider(new MyContentProvider());
//設(shè)置視圖
table.setLabelProvider( new MyLabelProvider());
//設(shè)置表格數(shù)據(jù)對象,,該方法非常重要,,是表格數(shù)據(jù)入口
table.setInput(persons);
◆ 表格中數(shù)據(jù)組織與兩個方法有關(guān):
◇ void setInput(Object input):該方法是表格數(shù)據(jù)最初的入口點,任何對象都可以設(shè)置為表格數(shù)據(jù),,因為該方法所設(shè)定的對象為Object對象,,所有的類都是Object子類。但通常使用集合類來組織數(shù)據(jù),。
在實際的項目開發(fā)中,,一般會使用實體對象(Entity Object)來裝載數(shù)據(jù)庫中的數(shù)據(jù),例如本例中的PersonEO實體對象:
package www.swt.com.ch19;
public class PersonEO {
private int ID;
private String name;
private String gender;
private String color;
public PersonEO (){}
public PersonEO ( int id , String name , String gender, String color){
this.ID = id;
this.name = name;
this.gender = gender;
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getID() {
return ID;
}
public void setID(int id) {
ID = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
從該實體類中可以看出,,該類的屬性對應(yīng)表中的一列,。除了列屬性外,還有設(shè)置和獲取屬性的方法,。
◇ setContentProvider方法為表格創(chuàng)建一個規(guī)則,,使初始化的數(shù)據(jù)對象轉(zhuǎn)化為表格中的數(shù)據(jù)。
void setContentProvider(IContentProvider provider)
IContentProvider 是一個接口,,而在表格使用時實現(xiàn)IStructuredContentProvider這個接口,,該接口繼承自IContentProvider 。
參考本文中的:MyContentProvider ,。
實現(xiàn)IStructuredContentProvider接口必須實現(xiàn)3個方法,。其中最重要的是getElements方法。該方法主要將
setInput設(shè)置的對象轉(zhuǎn)化成表格所使用的數(shù)組對象,。為防止類型轉(zhuǎn)換異常,,在不知道是何類型的情況下可以使用以下代碼進行類型檢查:
public Object[] getElements(Object inputElement) {
if ( inputElement instanceof List )
return ((List) inputElement).toArray();
return null;
}
◆ setLabelProvider(IBaseLabelProvider
labelProvider):使用該方法顯示數(shù)據(jù)。IBaseLabelProvider是一個接口,在表格中可以通過實現(xiàn)
ITableLabelProvider接口,,這個接口是IBaseLabelProvider的子類,。
參考本文中的:MyLabelProvider。
在實現(xiàn)該接口中,,最重要的是實現(xiàn)getColumnImage和getColumnText方法,。一個是設(shè)置單元格的圖標(biāo),一個是設(shè)置單元格顯示的文本,。
◆ createContextMenu():創(chuàng)建菜單,。
參考本文中的:createContextMenu、AddAction,、DelAction。
對表格操作的一些常用的方法:
◇ 添加一行數(shù)據(jù):add(Object element),。
◇ 添加多行數(shù)據(jù):add(Object[] elements),。
◇ 清空指定表格索引一行的數(shù)據(jù):clear(int index)。
◇ 在表格指定的索引行插入一行:insert(Object element, int position),。
◇ 刪除指定的行:remove(Object element),。
◇ 刪除指定的多行:remove(Object[] elements)。
◇ 替換指定行數(shù)據(jù):replace(Object element, int index),。
注意:這些方法只是對表格顯示的數(shù)據(jù)進行添加和刪除,,并未改變數(shù)據(jù)模型中提供的數(shù)據(jù),在本例中數(shù)據(jù)模型中的數(shù)據(jù)也就是List對象,。也可以通過改變模型中的數(shù)據(jù)來實現(xiàn)對表格的添加和修改,,然后使用refresh()方法刷新表格。
◆ setSorter(ViewerSorter sorter):為表格設(shè)置一個排序器,。
設(shè)置排序器:
//設(shè)置排序器
table.setSorter( new TableSorter());
排序器的代碼如下:
package www.swt.com.ch19;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
public class TableSorter extends ViewerSorter {
private static final int ASCENDING = 0;
private static final int DESCENDING = 1;
private int order;//判斷是升序還是降序
private int column;//判斷排序的列
public void doSort(int column) {
// 如果是同一列,,改變排列的順序
if (column == this.column) {
order = 1 - order;
} else {// 如果是另一列,則默認(rèn)為升序排列
this.column = column;
order = ASCENDING;
}
}
//覆蓋父類的方法,,返回比較結(jié)果有-1,0,1三種情況
public int compare(Viewer viewer, Object e1, Object e2) {
int result = 0;
PersonEO p1 = (PersonEO) e1;
PersonEO p2 = (PersonEO) e2;
//默認(rèn)是升序
switch (column) {
case TableWindow.ID:
result = p1.getID() > p2.getID() ? 1 : -1;
break;
case TableWindow.NAME:
result = collator.compare(p1.getName(), p2.getName());
break;
case TableWindow.GENDER:
result = collator.compare(p1.getGender(), p2.getGender());
break;
case TableWindow.COLOR:
result = collator.compare(p1.getColor(), p2.getColor());
break;
}
//如果此時為降序
if (order == DESCENDING)
result = -result;
return result;
}
}
doSort為單擊表頭時調(diào)用,,判斷數(shù)據(jù)排序的方法是覆蓋父類中的compare()方法,該方法的返回值用于判斷數(shù)據(jù)的順序,。
有了排序器對象后,,下面為表頭注冊單擊表頭事件,在初始化表頭對象后,,在程序中增加以下代碼:
//設(shè)置排序器
table.setSorter( new TableSorter());
//分別為表頭的每一列注冊事件
TableColumn column = table.getTable().getColumn(0);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
//單擊時,,重新設(shè)置排序?qū)ο髮傩?br>
((TableSorter)table.getSorter()).doSort(TableWindow.ID);
//刷新表格數(shù)據(jù)
table.refresh();
}
});
column = table.getTable().getColumn(1);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.NAME);
table.refresh();
}
});
column = table.getTable().getColumn(2);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.GENDER);
table.refresh();
}
});
column = table.getTable().getColumn(3);
column.addSelectionListener( new SelectionAdapter(){
public void widgetSelected(SelectionEvent e) {
((TableSorter)table.getSorter()).doSort(TableWindow.COLOR);
table.refresh();
}
});
當(dāng)不需要排序器時,setSorter(null)方法將排序器設(shè)置為null就可以了,。
◆ addFilter(ViewerFilter filter):為表格設(shè)置過濾器,。
創(chuàng)建菜單項時增加:menu.add(new FilterAction());
FilterAction的代碼參考本文中的:FileterAction。
當(dāng)創(chuàng)建過濾器對象時,必須實現(xiàn)select方法,,該方法返回布爾型,,true表示顯示該數(shù)據(jù),false表示不顯示該數(shù)據(jù),。
向表格中設(shè)置和移除過濾器的方法是addFilter和removeFilter方法,。
◆ 編輯表格單元:所有的單元格編輯器都是從CellEditor繼承而來的,該類是一個抽象類,,創(chuàng)建對象時要使用實現(xiàn)了的子類,。
//設(shè)置列屬性
table.setColumnProperties( COLUMN_NAME );
//設(shè)置單元格編輯器對象數(shù)組
CellEditor[] editors = new CellEditor[4];
editors[0] = null;
editors[1] = new TextCellEditor(table.getTable());
editors[2] = new TextCellEditor(table.getTable());
editors[3] = new TextCellEditor(table.getTable());
//設(shè)置單元格編輯器
table.setCellEditors(editors);
//設(shè)置單元個修改器
table.setCellModifier( new ICellModifier(){
//如果該列為第一列,設(shè)置不能修改
public boolean canModify(Object element, String property) {
if ( property.equals(COLUMN_NAME[0]))
return false;
return true;
}
//當(dāng)處于編輯狀態(tài)時所顯示的值
public Object getValue(Object element, String property) {
PersonEO p = (PersonEO) element;
if ( property.equals(COLUMN_NAME[1]))
return p.getName();
else if ( property.equals(COLUMN_NAME[2]))
return p.getGender();
else if ( property.equals(COLUMN_NAME[3]))
return p.getColor();
return null;
}
//當(dāng)修改后設(shè)置更新數(shù)據(jù)
public void modify(Object element, String property, Object value) {
if (element instanceof Item)
element = ((Item) element).getData();
PersonEO p = (PersonEO) element;
if ( property.equals(COLUMN_NAME[1]))
p.setName( (String)value );
else if ( property.equals(COLUMN_NAME[2]))
p.setGender( (String)value );
else if ( property.equals(COLUMN_NAME[3]))
p.setColor( (String)value );
//刷新表格
table.refresh();
}
});
◆ 表格的事件處理:
◇ 鼠標(biāo)雙擊事件:
table.addDoubleClickListener( new IDoubleClickListener(){
public void doubleClick(DoubleClickEvent event) {
StructuredSelection select = (StructuredSelection) event.getSelection();
PersonEO p = (PersonEO) select.getFirstElement();
System.out.println(p.getName());
}
} );
◇ 支持拖動事件:
addDragSupport(int operations, Transfer[] transferTypes, DragSourceListener listener)
addDropSupport(int operations, Transfer[] transferTypes, final DropTargetListener listener)
◇ 幫助事件:addHelpListener(HelpListener listener),。
◇ 選中改變事件:addSelectionChangedListener(ISelectionChangedListener listener),。
◇ 選中改變后事件:addPostSelectionChangedListener(ISelectionChangedListener listener)。
◇ 雙擊打開事件:addOpenListener(IOpenListener listener),。
◆ 帶有復(fù)選框表格(CheckBoxTableViewer)
table = CheckboxTableViewer.newCheckList( parent , SWT.FULL_SELECTION);
CheckboxTableViewer類還提供了對選中狀態(tài)操作的一些方法:
◇ 該行是否已選中:boolean getChecked(Object element),。
◇ 獲得所有已選中的行:Object[] getCheckedElements()。
◇ 該行是否灰色顯示:getGrayed(Object element),。
◇ 獲得所有灰色顯示的行:Object[] getGrayedElements(),。
◇ 設(shè)置全選和取消全選:setAllChecked(boolean state)。
◇ 設(shè)置全部灰色顯示和取消灰色顯示:setAllGrayed(boolean state),。
◇ 設(shè)置指定行的選中狀態(tài):setChecked(Object element, boolean state),。
◇ 設(shè)置選中多行:setCheckedElements(Object[] elements)。
◇ 設(shè)置指定行是否灰色顯示的狀態(tài):setGrayed(Object element, boolean state),。
◇ 設(shè)置多行灰色顯示:setGrayedElements(Object[] elements),。
界面顯示效果:
按性別排序:
篩選后,只顯示性別為“女”的: