The Goal
Keep walking…… |
當一個GC方法在一個Drawabel對象上畫出一個圖案,它僅執(zhí)行這個painting過程一次。如果用戶改變對象尺寸或是用另一個窗口去覆蓋它,則圖形會被消除。因此,應用程序能否在外界事件影響下維持其外觀這一點相當重要。
這些外部事件被稱為PaintEvents,接收它們的程序接口是PaintListener。一個Control在任何時候當其外觀被應用程序或是外界活動改變都會觸發(fā)一個PaintEvent。這些類對于事件和監(jiān)聽器的使用方式都和我們在第四章內(nèi)提到的類似。由于PaintListener只有一個事件處理方法,所以不需要使用adapter類
Canvas canvas = new Canvas(shell, SWT.NONE);
canvas.setSize(150, 150);
canvas.setLocation(20, 20);
canvas.addPaintListener(new PaintListener()
{
public void paintControl(PaintEvent pe)
{
GC gc = pe.gc;//每一個PaintEvent對象都包含有其自己的GC
gc.drawPolyline(new int[] {10,120,70,100,100,130,130,75});
}
});
shell.open();
每一個PaintEvent對象都包含有其自己的GC,主要有2個原因:1.因為這個GC instance是由事件產(chǎn)生的,所以PaintEvent會負責釋放他。2.應用程序可以在shell open之前創(chuàng)建GC,這樣可以使圖形在一個獨立的類中被創(chuàng)建。
SWT在PaintListener接口內(nèi)優(yōu)化painting過程,SWT的開發(fā)者強烈建議Control的painting僅對PaintEvent作出反應。如果一個應用程序因為其他原因必須更新其圖形,則他們推薦使用control的redraw()方法,這會在隊列中加入一個paint請求。之后,你可以調(diào)用update()方法來處理所有的綁定于該對象的paint請求。
需要牢記的是,雖然對于Control對象推薦在一個PaintListener內(nèi)painting,但是由于Device和Image對象并不能在該接口內(nèi)使用。如果你需要在一個image或device內(nèi)生成圖形,你必須單獨地生成一個GC對象并在使用結(jié)束后將其銷毀。
要客制化layout,需要繼承抽象類Layout,需要寫2個方法——computeSize() 和layout().
computeSize()
protected Point computeSize(Composite composite,
int wHint, int hHint,
boolean flushCache)
{
Point maxDimensions =
calculateMaxDimensions(composite.getChildren());
int stepsPerHemisphere =
stepsPerHemisphere(composite.getChildren().length);
int maxWidth = maxDimensions.x;
int maxHeight = maxDimensions.y;
int dimensionMultiplier = (stepsPerHemisphere + 1);
int controlWidth = maxWidth * dimensionMultiplier;
int controlHeight = maxHeight * dimensionMultiplier;
int diameter = Math.max(controlWidth, controlHeight);
Point preferredSize = new Point(diameter,
diameter);
... // code to handle case when our calculations
// are too large
return preferredSize;
}
參數(shù):
1.composite--The object we’re going to populate. At the time this method is called, it has children, but neither the composite nor the children have been sized or positioned on the screen.
2.wHint and hHint--layout所需的最大長寬。若帶有參數(shù)SWT.DEFAULT,表示此layout可以隨意使用use whatever sizes it decides it needs.
3.flushCache--作為flag,to tell the layout whether it’s safe to use any cached values that it may be maintaining.
computeSize()的目的主要在于計算我們要layout的composite有多大
layout()
……
與之前所述的layout不同,form layout不是基于行和列的,它是基于與其他control之間的相對位置的。
FormLayout十分簡單,你只要:1.設定頁邊距(高,寬)屬性。 2.設定spacing屬性,即所有control間的距離(in pixels)
同樣可以使用FormData來配置單個的control。
FormData
如果一個control沒有一個FormData實例來描述它的話,就會默認放在composite的右上角
width和height屬性指定了control的尺寸,in pixels.
top, bottom, right, 和left屬性,每一個都有一個FormAttachment實例,這些attachments描述了control與其他control之間的關(guān)系。
FormAttachment
有2個使用途徑:
1.通過使用percentage of the parent composite.
2.通過設定一個control和另一個control之間的相對位置?
《圖》
package com.swtjface.Ch6;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class Ch6FormLayoutComposite extends Composite {
public Ch6FormLayoutComposite(Composite parent) {
super(parent, SWT.NONE);
FormLayout layout = new FormLayout();
setLayout(layout);
Text t = new Text(this, SWT.MULTI);
FormData data = new FormData();
data.top = new FormAttachment(0, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100);
data.bottom = new FormAttachment(75);//確定text的位置,因為左上角是坐標原點,所以right的百分數(shù)為100。
t.setLayoutData(data);
Button ok = new Button(this, SWT.NONE);
ok.setText("Ok");
Button cancel = new Button(this, SWT.NONE);
cancel.setText("Cancel");
data = new FormData();
data.top = new FormAttachment(t);
data.right = new FormAttachment(cancel);//ok按鈕在text下面,cancel左邊
ok.setLayoutData(data);
data = new FormData();
data.top = new FormAttachment(t);
data.right = new FormAttachment(100);//cancel按鈕在text下面,在最右邊
cancel.setLayoutData(data);
}
}
最常用的一種layout.以row layout為基礎。
package com.swtjface.Ch6;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class Ch6GridLayoutComposite extends Composite {
public Ch6GridLayoutComposite(Composite parent) {
super(parent, SWT.NONE);
GridLayout layout = new GridLayout(4,false);//每一行有4個control,后一個參數(shù)是a
boolean to indicate whether the columns should take up an even amount of
space. By passing false, you tell the layout to only use the minimum amount of
space needed for each column.
setLayout(layout);
for (int i = 0; i < 16; ++i) {
Button button = new Button(this, SWT.NONE);
button.setText("Cell " + i);
}
}
}
Using GridData styles
十分類似于RowData對象。可通過其構(gòu)造函數(shù)來設定STYLE,這些STYLE可分為3類:FILL, HORIZONTAL_ALIGN, and VERTICAL_ALIGN.
1.FILL:此cell是否fill所有的availabe的空間。可用的值還包括FILL_HORIZONTAL(水平擴張),FILL_VERTICAL(垂直擴張),FILL_BOTH。
2.ALIGN,用來指定control在cell中的什么位置。值包括BEGINNING, END, CENTER和FILL。
具體參見下表
Using GridData size attributes
與RowData不同,GridData還有很多的public屬性。其中有些是布爾值類型的,一般會根據(jù)所設置的不同styles而自動管理,所以無需對其直接操作。還有一些是integer值,用來確定單個cells的大小。具體件下表:
默認為從左到右排放的,根據(jù)每個control實際所需的大小來分配空間,此composite中多于出來的空間,再平攤到每個control上。隨著composite的大小調(diào)整,control的大小也會跟著調(diào)整。
package com.swtjface.Ch6;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
public class Ch6FillLayoutComposite extends Composite {
public Ch6FillLayoutComposite(Composite parent) {
super(parent, SWT.NONE);
FillLayout layout = new FillLayout( SWT.VERTICAL); //默認是SWT.HORIZONTAL
setLayout(layout);//為此Composite設定一個layout.如果漏了此語句,會顯示不出child control。
for (int i = 0; i < 8; ++i) {
Button button = new Button(this, SWT.NONE);
button.setText("Sample Text");
}
}
}
是JFace的類,繼承自ContributionManager,凡是繼承了IAction或IContribution接口的對象都可被加至ToolBarManager.你只要花時間為ToolBarManager添加Action,Toolbar和ToolItem實例會自動產(chǎn)生。
你可通過調(diào)用ApplicationWindow的createToolBarManager()來為你的應用程序添加一個toolbar。與MenuManager不同的是,createToolBarManager()需要一個style參數(shù),這個參數(shù)用來設定ToolBar所用的按鈕的風格:flat或normal。
除了MenuManager所用的ContributionItems之外,還有一個新的ContributionItem,只能被ToolBarManager使用——ControlContribution。這個類可將任何能被用于toolbar的Control打包進去。
要使用ControlContribution類,必須要實現(xiàn)抽象方法createControl().
toolBarManager.add(new ControlContribution("Custom") {
protected Control createControl(Composite parent) {
SashForm sf = new SashForm(parent, SWT.NONE);
Button b1 = new Button(sf, SWT.PUSH);
b1.setText("Hello");
Button b2 = new Button(sf, SWT.PUSH);
b2.setText("World");
b2.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Selected:" + e);
}
});
return sf;
}
});
如果你希望有任何的事件發(fā)生,必須在你的controls上實現(xiàn)SelectionListeners
如果你不想ToolBarManager來創(chuàng)建toolbar的話,可以手動創(chuàng)建,需要用到ToolBar和ToolItem類.
Toolbar
是一個composite control,包含多個ToolItems.Toolbar由多個小圖標按鈕組成,一般是16-by-16bitmap圖片。每個按鈕都對應一個ToolItem。Toolbar可以是水平的也可以是垂直的,默認為水平
ToolItem
每一個ToolItem都有一個圖片,如果沒有,默認為紅色方塊。When the user selects a ToolItem from the menu, it broadcasts the event to any registered SelectionListeners.Your application should register a listener with each ToolItem and use that listener to perform whatever logic corresponds to the menu item.
三種類型的Combo control:
1.Simple:默認類型,一個可編輯的text field和一個供選擇的list
2.Drop-down:下拉列表,文本框可編輯
3.Read-only:文本框不可編輯的下拉列表,可用select( 0 )來將其默認選中列表中的首項。
以上三種類型可在構(gòu)造函數(shù)中通過STYLE.*來設置。
先要將org.eclipse.text_x.y.z和org.eclipse.jface.text_x.y.z加到classpath
兩個重要的接口:IDocument和ITextViewer。JFace為其提供了默認的實現(xiàn)。
一個IDocument的實例持有被編輯的真實的文本信息。它的主要實現(xiàn)是Document類。AbstractDocument提供了部分實現(xiàn),你可通過繼承它來添加自己的實現(xiàn)。IDocument允許通過IDocumentListener接口來獲取內(nèi)容編輯的通知。
IDocument還提供了以下功能
Positions
可以給每一個text區(qū)域分配一個記號來作為它的Position。當被指定給某個ducument時一個Position對象有an offset and a length of text。如果document的text被更新的話,Position也會同步更新,所以他永遠都是指向同一段文字。Position類本身提供了一些基本的功能,可通過繼承他來完善更多有用的功能。
Partition content types
每個document由一個或多個partitions組成,通過ITypedRegion接口來表現(xiàn)。每一個partition可以有各自的內(nèi)容類型,如plain text, rich text, or HTML。要使用它,你要創(chuàng)建一個IDocumentPartitioner然后assign給你的document,然后document的partitioner就會負責響應對指定位置內(nèi)容類型的查詢,它必須通過實現(xiàn)computePartitioning()來返回包含此document中所有ITypedRegions的一個數(shù)組。不需要實現(xiàn)你自己的document partitioner。如果沒有創(chuàng)建,整個document就是一個區(qū)域,類型為IDocument.DEFAULT_CONTENT_TYPE。
Searching
IDocument通過search()提供了搜索的功能。不支持regular expressions or other patterns,但提供了search start location,direction, and case sensitivity and whether to match whole words only.
ITextViewer將一個標準的text widget轉(zhuǎn)換成一個基于document的text widget
ITextViewer的默認實現(xiàn)是TextViewer,它使用StyledText來顯示數(shù)據(jù)。ITextViewer支持text modifications的listener,也支持visual events(如改變viewport,即text的當前可視區(qū)域)的監(jiān)聽器。
雖然作為ITextViewer的默認應用,如果你想要修改顯示,TextViewer允許你直接accessStyledText,但建議你使用TextPresentation,因為它可以收集該文檔中帶有的各個不同的StyleRanges。
ITextViewer還支持很多不同類型的插件,可用來修改widget的行為。可以被customized的功能有:
1.通過IUndoManager來支持undo
2.通過ITextDoubleClickStrategy來支持對鼠標雙擊的處理
3.通過IAutoIndentStrategy來支持文本的自動縮進
4.通過ITextHover來實現(xiàn),當鼠標停留在document的一個section上時,顯示text.
要使用上述插件,你需要分配一個適當?shù)慕涌趯嵗otext viewer,然后調(diào)用activatePlugins().
如下列出了org.eclipse.jface.text的子包及其作用
用于文本編輯的control有2個:Text和StyledText.后者可以為文本和control本身設定顏色,格式等。這兩個control之間毫無關(guān)聯(lián),除了都是Composite的子類之外。
package com.swtjface.Ch5;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
public class Ch5Capitalizer extends Composite {
public Ch5Capitalizer(Composite parent) {
super(parent, SWT.NONE);
buildControls();
}
private void buildControls() {
this.setLayout(new FillLayout());
Text text = new Text(this, SWT.MULTI | SWT.V_SCROLL);
text.addVerifyListener(new VerifyListener() { //每當text被改變,任何以注冊的VerifyListeners便會被調(diào)用。此處每按一次鍵盤,此方法就被調(diào)用。如果是同時輸入多個字符,也調(diào)用一次
public void verifyText(VerifyEvent e) {
if( e.text.startsWith("1") ) {
e.doit = false;
} //如果文本以1開頭,即不允許編輯
else {
e.text = e.text.toUpperCase();
}
}
});
}
}
Text的重要方法,見下圖
insert()--doesn’t allow you to insert text into the existing content.
StyledText包含了一系列的應用到該小部件的預定義的動作,這些是常規(guī)的東西如:剪切、粘貼、移動至下一個詞、移動至文末。代表這些動作的常量在org.eclipse.swt.custom程序包中的ST類中有定義。這些常量在兩種情況下發(fā)揮功效:首先,你可以使用它們程序性地使用invokeAction()方法調(diào)用任一的這些方法;其次,你也可以使用setKeyBinding()方法來將它們綁定于鍵擊行為。setKeyBinding()選定一個鍵(可以通過諸如Shift或是Ctrl之類的編輯鍵來修改SWT常量之一)綁定于指定的動作。如下的例子中組合鍵Ctrl-Q綁定于粘貼動作。引起注意的是這并不意味著會將默認鍵的綁定清除,該兩個綁定都會生效。
相對于Text而言,還添加了drawing line backgrounds and line styles的事件,可以通過此事件來改變整行的style或背景顏色。注意:如果使用了LineStyleListener,就不能在StyledText實例上調(diào)用get/setStyleRange(), 如果使用了LineBackgroundListener,那你就不能調(diào)用getLineBackground() or setLineBackground().
可以通過使用一個StyledText的StyleRanges來改變顯示的風格
StyleRange
StyledText通過使用StyleRange類來管理當前所顯示的不同styles。其所有的欄位都是public的可隨意修改,但是要一直到當此StyledText實例的setStyleRange()被調(diào)用之后才會生效。
StyleRanges通過開始偏移量和長度來設定text的區(qū)域范圍。
StyleRange可設定背景和前景色,默認為null,還可設定字體,SWT.NORMAL 或者SWT.BOLD.
similarTo()可用來判斷兩個StyleRange實例是否有同樣的前景、背景和字體。
當我們保存text之后,可通過styledText.getStyleRanges()來獲取style信息,此函數(shù)會返回an array of StyleRange
toggleBold()--將已輸入的文本在bold和normal之間切換,是被一個KeyListener調(diào)用的,此KeyListener會監(jiān)聽F1是否被按下
A StyledText example
復制、粘貼功能不需要通過代碼便可使用,是和platform的標準鍵盤快捷方式相關(guān)聯(lián)的
ExtendedModifyListener和ModifyListener不同,前者提供了關(guān)于what was done的event細節(jié),而后者只是當編輯懂作產(chǎn)生時notify,不會去準確的辨別到底何種修改發(fā)生了。
package com.swtjface.Ch5;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
public class Ch5Undoable extends Composite {
private static final int MAX_STACK_SIZE = 25;
private List undoStack;
private List redoStack;
private StyledText styledText;
public Ch5Undoable(Composite parent) {
super(parent, SWT.NONE);
undoStack = new LinkedList();
redoStack = new LinkedList();
buildControls();
}
private void buildControls() {
this.setLayout(new FillLayout());
styledText = new StyledText(this, SWT.MULTI | SWT.V_SCROLL);
styledText.addExtendedModifyListener(
new ExtendedModifyListener() { //每次text被編輯的時候,都會調(diào)用此listener
public void modifyText(ExtendedModifyEvent event) {
String currText = styledText.getText();
String newText = currText.substring(event.start,
event.start + event.length); //獲得新插入的文本
if( newText != null && newText.length() > 0 ) {
if( undoStack.size() == MAX_STACK_SIZE ) {
undoStack.remove( undoStack.size() - 1 );
}
undoStack.add(0, newText);//將新插入的文本保存到undoStack中
}
}
}); //關(guān)鍵部分
styledText.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
switch(e.keyCode) {
case SWT.F1:
undo(); break;
case SWT.F2:
redo(); break;
default: //ignore everything else
}
}
});
}
private void undo() {
if( undoStack.size() > 0 ) {
String lastEdit = (String)undoStack.remove(0);//得到要undo的字符
int editLength = lastEdit.length();
String currText = styledText.getText();
int startReplaceIndex = currText.length() - editLength;
styledText.replaceTextRange(startReplaceIndex, editLength, ""); //將最后輸入的字符替換成空
redoStack.add(0, lastEdit);//把最后的這個undo的字符加到redoStack中
}
}
private void redo() {
if( redoStack.size() > 0 ) {
String text = (String)redoStack.remove(0);//得到要恢復的字符
moveCursorToEnd();
styledText.append(text);//將要恢復的字符加至文本的最后
moveCursorToEnd();
}
}
private void moveCursorToEnd() {
styledText.setCaretOffset(styledText.getText().length());
}
}
| |||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
---|---|---|---|---|---|---|---|---|---|
26 | 27 | 28 | 1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 | |||
12 | 13 | 14 | 15 | 16 | 17 | 18 | |||
19 | 20 | 21 | 22 | 23 | 24 | 25 | |||
26 | 27 | 28 | 29 | 30 | 31 | 1 | |||
2 | 3 | 4 | 5 | 6 | 7 | 8 |