1。圖形拖動(dòng)
圖形的拖動(dòng)就是圖形選中的圖形跟著鼠標(biāo)的移動(dòng)而不斷的相應(yīng)改變位置,這是在圖形界面中是經(jīng)常見(jiàn)的一個(gè)操作,但是在SWT/JFace中來(lái)實(shí)現(xiàn)卻不是意見(jiàn)容易的事。在這里底板是一個(gè)Canvas,圖形也是建立在一個(gè)Canvas上,當(dāng)然位置的改變是建立在鼠標(biāo)移動(dòng)的監(jiān)聽(tīng)下.
1、首先建立幾個(gè)變量:
Int oldx = -1;// 拖動(dòng)前的x
Int oldy = -1;//拖動(dòng)前的y
Int xoffset = 0; //拖動(dòng)后的偏移量x
Int yoffset =0 ;// 拖動(dòng)后的偏移量y;
Int x,y;// 拖動(dòng)后圖形應(yīng)該在的位置
2、 在第一次拖動(dòng)時(shí),取得oldx和oldy
if(oldX==-1&&oldY==-1){
oldX=e.x;
oldY=e.y;
}
3、 獲得偏移量
xoffset=e.x-oldX;
yoffset=e.y-oldY;
4、得到新的圖形位置
x=ca.getBounds().x+xoffset;
y=ca.getBounds().y+yoffset;
5、 重新設(shè)置圖形的位置
ca.setBounds(x,y,ca.getSize.x,ca.getSize.y)
這樣就實(shí)現(xiàn)了圖形的拖動(dòng),不僅算法簡(jiǎn)潔,而且效果也很好。當(dāng)然為了更好的效果,還可以加上邊界控制,以保證圖形不超出邊界
2。圖形連線與畫(huà)線
2.1 圖形的連線
整個(gè)SWT中只有一個(gè)類GC與之有關(guān),就是在一個(gè)畫(huà)板上畫(huà)線,這樣簡(jiǎn)單的線根本無(wú)法滿足復(fù)雜連線的要求,要實(shí)現(xiàn)比較復(fù)雜的圖形間的連線,我們使用了eclipse的另一個(gè)插件——draw2D,在draw2D的圖形中比較容易實(shí)現(xiàn)連線,但是draw2D中連線的圖形都是IFigure類的圖形,否則就無(wú)法實(shí)現(xiàn)draw2D內(nèi)的連線,然而我們的圖形使用SWT設(shè)計(jì)的,因此出現(xiàn)了兩者的兼容問(wèn)題。因此我們自行設(shè)計(jì)了綜合使用兩者來(lái)實(shí)現(xiàn)圖形連線的方案,就是圖形是用SWT設(shè)計(jì)的,而線是用draw2D來(lái)設(shè)計(jì)的。這樣設(shè)計(jì)表面看起來(lái)能夠達(dá)到draw2D的兩線的完美效果,而又不改變我們需要是用的SWT設(shè)計(jì)的圖形。
1、 連線的簡(jiǎn)化流程
獲得起始圖形,計(jì)算出連線的起點(diǎn)
獲得指向圖形,計(jì)算出連線的終點(diǎn)
根據(jù)起點(diǎn)和終點(diǎn)來(lái)建立連線
2、 解決Canvas與Panel之間的兼容問(wèn)題
因?yàn)槲覀兊慕⒃谝粋€(gè)畫(huà)布(Canvas)上的,而連線則要建立在一個(gè)(Panel)上。因此我們要將兩者聯(lián)系起來(lái),才能實(shí)現(xiàn)圖形間的連線。這里需要用到輕量級(jí)系統(tǒng)(LightweightSystem)
lws=new LightweightSystem(editPlace);
圖形所在的面板
然后就可以新建Panel,并將他設(shè)為輕量級(jí)系統(tǒng)的內(nèi)容
panel=new Figure();
lws.setContents(panel);
這樣我們就可以在panel上添加我們的連線了
2.2 在面板上畫(huà)曲線
1、 這個(gè)曲線也同樣要建立在Panel之上,因此也要像上面一樣建立Panle,這里不再重復(fù)
2、要?jiǎng)?chuàng)建曲線則要用到PolylineConnection(),我們先創(chuàng)建一個(gè)PolylineConnection的對(duì)象pc.
3、 如果要顯示箭頭,則可以給pc添加修飾,
p2.setTargetDecoration(new PolygonDecoration());
4、接下來(lái)就是設(shè)置pc上的關(guān)鍵點(diǎn),包括起點(diǎn),終點(diǎn),拐點(diǎn)。確定這些點(diǎn)后,從起點(diǎn)到終點(diǎn)依次排列即可
p2.setPoints(new PointList(new int[]{p1, p2,p3,…}));
5、 這樣就可以建立了曲線,不過(guò)線上的個(gè)關(guān)鍵點(diǎn)的確定是一個(gè)難點(diǎn),根據(jù)不同的情況會(huì)有不同的確定方法,這里不能一一列出了
3。圖像常用處理與Canvas
3.1 如何在Canvas上添加圖像
要在一個(gè)畫(huà)布Canvas上添加,需要注冊(cè)畫(huà)布的畫(huà)圖事件,代碼如下:
canvas.addPainListener(new PaintListener(){
public void paintControl(PaintEvent e){
e.gc.drawImage(image,10,10);
}
}
3.2 圖像的創(chuàng)建
1. Image(display, “eclipse.gif”)
2. Image (display , “eclipse.gif”,SWT.IMAGE_FRAY)
3. ImageData data = new ImageData(“eclipse.gif);
Image image = new Image(display, data)
3.3 圖像的放大或縮小
通過(guò)ImageData的scaledTo可意見(jiàn)圖像放大或縮小,在這里是新建另一個(gè)圖像,而不是直接改變?cè)瓉?lái)的圖像。
ImageData data = image.getImageData();
ImageDate newData = data.scaledTo(newWidth,newHeight);
Image newImage = new Image(display, newData);
3.4 圖像的保存
因?yàn)橐粋€(gè)動(dòng)態(tài)的圖片可能有多個(gè)圖片組成,所以用數(shù)組
4文件的過(guò)濾
這里所說(shuō)的文件的過(guò)濾不是上面說(shuō)的文件對(duì)話框中的文件的過(guò)濾,在這里我們?cè)O(shè)置了一個(gè)樹(shù),讓它來(lái)顯示工程下已有的文件,在這里我們只希望看到(.grh)的文件,因此需要給它設(shè)置過(guò)濾器。先來(lái)看看我們?cè)趺磳?shí)現(xiàn)顯示文件的;
在這里使用的是TreeViewer,TreeViewer的構(gòu)建步驟如下:
1、 創(chuàng)建新對(duì)象,例如TreeViewer tv=new TreeViewer(fileComposite,SWT.NONE);
2、 設(shè)置內(nèi)容管理器,如tv.setContentProvider( new FileTreeContentProvider());
3、設(shè)置標(biāo)簽提供器,如tv.setLabelProvider(new FileTreeLabelProvider());
4、設(shè)定TreeViewer的輸入數(shù)據(jù),如tv.setInput(new File("e:\"));
其中FileTreeContentProvide,F(xiàn)ileTreeLabelProvider分別是implements了ItreeContentProvider和ILabelProvider兩個(gè)接口的。
FileTreeContentProvide中有幾個(gè)重要的方法。
① getElements();"public Object[] getElements(Object inputElement)",當(dāng)程序開(kāi)始構(gòu)建樹(shù)時(shí),首先調(diào)用它返回一個(gè)數(shù)組,此數(shù)組對(duì)象表示樹(shù)的根節(jié)點(diǎn),
② hasChildren();"public boolean hasChildren(Object element)",當(dāng)TreeViewer顯示一個(gè)節(jié)點(diǎn)(element)后會(huì)調(diào)用該函數(shù)判斷當(dāng)前節(jié)點(diǎn)是否有子節(jié)點(diǎn),若有則顯示“+”;
③ getChildren();"public Object[] getChildren(Object parentElement)",當(dāng)用戶選擇節(jié)點(diǎn)打開(kāi)子節(jié)點(diǎn)時(shí),會(huì)調(diào)用此函數(shù)返回下一層子節(jié)點(diǎn),parentElement參數(shù)為選擇的節(jié)點(diǎn);
④ getparent();"public Object getParent(Object element)"
還有幾個(gè)方法就不在此敘述,
在程序里不需要顯示根目錄的位置,只要顯示在工程目錄下有用的文件,在getElements()方法中,我們返回getChildren()中的值,這樣,我們就可以不得到輸入的路徑這個(gè)根節(jié)點(diǎn),而把該路徑下的符合條件的文件作為根節(jié)點(diǎn);
public Object[] getElements(Object element) {
return getChildren(element);
}
在getChildren方法中,我們通過(guò)一個(gè)數(shù)組列出給定路徑下的文件,然后通過(guò)一個(gè)ArrayList來(lái)存儲(chǔ)符合條件的文件對(duì)象:
Object[] kids = ((File) element).listFiles();
ArrayList<Object> outs = new ArrayList<Object>();
當(dāng)kids不是空的時(shí)候,我們就看kids中的文件后綴是不是符合我們的要求,如果是,就添加到outs中
for(int i = 0;i<kids.length;i++){
if(((File)kids[i]).getName().endsWith(".wsdl")||
((File)kids[i]).getName().endsWith(".bpel")||
((File)kids[i]).getName().endsWith(".grh")){
int j = 0;
outs.add(j,kids[i]);
j++;
}
}
現(xiàn)在我們已經(jīng)得到了我們需要得到的所有的對(duì)象,因?yàn)檫@個(gè)函數(shù)的返回值是個(gè)數(shù)組,再把outs中的元素復(fù)制到一個(gè)數(shù)組中即可;
f(outs.size()!=0){
Object[] out = new Object[outs.size()];
for(int i= 0;i<outs.size();i++){
out[i] = outs.get(i);
}
return out;
}
5。關(guān)于序列化保存
1) 關(guān)于保存
序列化的問(wèn)題是由保存引起的,要保存一個(gè)對(duì)象就必須為相關(guān)的類實(shí)現(xiàn)序列化,這本沒(méi)有什么問(wèn)題,只需要把相關(guān)的類繼承并實(shí)現(xiàn)Serializable接口就可以了,但是,在工程里用到了一些類。例如org.eclipse.swt.widgets.graphics.Image這個(gè)類,它是一個(gè)final類,不可以被繼承,但是他是節(jié)點(diǎn)的一部分,必須要顯示出來(lái);我們可以把它作為一個(gè)參數(shù)傳到節(jié)點(diǎn)的setNodeLocation函數(shù)中,它是初始化節(jié)點(diǎn)時(shí)負(fù)責(zé)顯示的函數(shù)。每創(chuàng)建一個(gè)新的節(jié)點(diǎn)時(shí)就需要先創(chuàng)建一個(gè)Image實(shí)例。
2) 關(guān)于恢復(fù)
保存成功了還需要恢復(fù)圖像,恢復(fù)圖像是個(gè)比較復(fù)雜的過(guò)程,我們要保證圖像的位置和數(shù)據(jù)等許多東西保持不變;首先找到開(kāi)始節(jié)點(diǎn),從開(kāi)始節(jié)點(diǎn)開(kāi)始逐個(gè)恢復(fù),如沒(méi)有開(kāi)始節(jié)點(diǎn),那么這個(gè)圖是不完整的,就沒(méi)有辦法完整的恢復(fù)它;恢復(fù)時(shí),不能用原來(lái)的對(duì)象,因?yàn)樵械膶?duì)象都已經(jīng)被dispose();了,并且不可以用到任何有關(guān)顯示的方法,否則就會(huì)出現(xiàn)促發(fā)異常;所以要得到節(jié)點(diǎn)的大小和位置就要另想辦法;
為了得到node原來(lái)的位置,在node中設(shè)置了一個(gè)point變量來(lái)記載node的位置,并在node的位置更改后更改point的值,這樣就可以得到node原來(lái)的位置;同理,我們可以得到保存時(shí)node的大小了;
我們可以從保存的圖中找到與開(kāi)始節(jié)點(diǎn)相關(guān)聯(lián)的下一個(gè)節(jié)點(diǎn),回復(fù)這個(gè)節(jié)點(diǎn)后再找下一個(gè)節(jié)點(diǎn),這樣可以通過(guò)一個(gè)循環(huán)一直找到結(jié)束節(jié)點(diǎn),并把這些節(jié)點(diǎn)加到一個(gè)新建的graph中,然后把相關(guān)的信息都通過(guò)已有的set和get方法添加的節(jié)點(diǎn)中去,這樣,整個(gè)圖的所有的節(jié)點(diǎn)都恢復(fù)出了;
因?yàn)槊總€(gè)節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)有不同的可能,用一個(gè)if else組合來(lái)判斷到底是哪一個(gè)節(jié)點(diǎn),然后不同的節(jié)點(diǎn)有不同的代碼;簡(jiǎn)單的節(jié)點(diǎn)如taskNode的恢復(fù)比較簡(jiǎn)單,基本的過(guò)程和創(chuàng)建一個(gè)新的節(jié)點(diǎn)相同,只是注意把原節(jié)點(diǎn)的信息傳遞給新的節(jié)點(diǎn)即可。
具體的過(guò)程可以簡(jiǎn)化為
獲得要復(fù)制的節(jié)點(diǎn)的引用
點(diǎn)擊粘貼,新建一個(gè)與要復(fù)制節(jié)點(diǎn)類型相同的節(jié)點(diǎn)
把要復(fù)制節(jié)點(diǎn)的信息傳遞到新的節(jié)點(diǎn)中去
判斷節(jié)點(diǎn)是不是容器節(jié)點(diǎn),是繼續(xù),不是結(jié)束
新建容器節(jié)點(diǎn)中的子節(jié)點(diǎn),給子節(jié)點(diǎn)傳遞信息
以下是粘貼后初始化一個(gè)工作節(jié)點(diǎn)的例子;
wfNode = new TaskNode(node.getName(), gra, web);
wfNode.setId(node.getId());
wfNode.setNodeInView(c, workspace.getPanel(), attr,workspace,web); BaseWFNodeText.setCanvasText(wfNode);
wfNode.setNodeLocation(node.getPoint().x, node.getPoint().y, image1);
gra.addNode(wfNode);
若下一個(gè)節(jié)點(diǎn)是復(fù)雜節(jié)點(diǎn),首先恢復(fù)復(fù)雜節(jié)點(diǎn)本身,然后判斷該節(jié)點(diǎn)是否有子節(jié)點(diǎn),若存在子節(jié)點(diǎn),則依次恢復(fù)其子節(jié)點(diǎn),通過(guò)一個(gè)foreach循環(huán)就可以把所有的子節(jié)點(diǎn)恢復(fù);注意的是每個(gè)父節(jié)點(diǎn)的子節(jié)點(diǎn)形式可能不同,而且他們需要傳遞的信息也不完全相同。部分代碼如下
wfNode.getCanvas().setSize(node.getSize());
//設(shè)置節(jié)點(diǎn)的大小
wfNode.setSize(node.getSize().x, node.getSize().y);
//記錄節(jié)點(diǎn)的大小
gra.addNode(wfNode);
節(jié)點(diǎn)的信息如下
wfNode.setBpelOperation(node.getBpelOperation());
wfNode.setSuppressJionFaliare(node.getSuppressJionFaliare());
wfNode.setJionCondition(node.isJionCondition());
wfNode.setOporation(node.getOporation());
wfNode.setLink(node.getLink());
//以上為所有節(jié)點(diǎn)都有的屬性
wfNode.setCondition(node.getCondition());
//FlowNode和WhileNode有的屬性
wfNode.setCaseCondiction(node.getCaseCondiction());
//FlowNodeµÄ×Ó½ÚµãÓеÄÊôÐÔ
childNode.setFromPart(element.getFromPart());
childNode.setFromVar(element.getFromVar());
childNode.setToPart(element.getToPart());
childNode.setToVar(element.getToVar());
//以上四個(gè)是copy有的節(jié)點(diǎn)
6。圖形的復(fù)制與粘貼
1) 為各個(gè)節(jié)點(diǎn)以及底層面板創(chuàng)建菜單項(xiàng),包括復(fù)制,粘貼,刪除;在工具欄以及菜單欄創(chuàng)建相應(yīng)的項(xiàng);
2) 關(guān)于復(fù)制
復(fù)制的方法比較簡(jiǎn)單,就是把一個(gè)新的變量指向要復(fù)制的對(duì)象,然后把這個(gè)變量通過(guò)get方法,讓外界可以獲得它;
public void copy(BaseWFNode node){
midNode = node;
}
3) 關(guān)于粘貼
粘貼的過(guò)程是創(chuàng)建一個(gè)新的節(jié)點(diǎn),他所攜帶的信息和復(fù)制的節(jié)點(diǎn)相同
1、 獲得要粘貼節(jié)點(diǎn)的引用;判斷是否為空,是就繼續(xù),否則就什么也不做;
2、 構(gòu)建一個(gè)新的節(jié)點(diǎn),確定節(jié)點(diǎn)的id,位置等于聲稱代碼無(wú)關(guān)的信息;
3、 判斷節(jié)點(diǎn)具體是哪一種節(jié)點(diǎn),并把原節(jié)點(diǎn)的信息賦給新的節(jié)點(diǎn);
4、 Gragh中添加這個(gè)節(jié)點(diǎn);
5、 根據(jù)第三步,若這個(gè)節(jié)點(diǎn)是某個(gè)復(fù)雜節(jié)點(diǎn),就把它的子節(jié)點(diǎn)也構(gòu)建出來(lái),并賦予相應(yīng)的信息;實(shí)現(xiàn)的過(guò)程和恢復(fù)圖形類似;
4) 工具欄復(fù)制的實(shí)現(xiàn)
1、 這里添加監(jiān)聽(tīng)的時(shí)候用了一個(gè)標(biāo)記,當(dāng)雙擊一個(gè)節(jié)點(diǎn)的時(shí)候,就記錄這個(gè)標(biāo)記為1,然后讓一個(gè)中間的變量指向這個(gè)節(jié)點(diǎn),
public void mouseDoubleClick(MouseEvent e){
WSCAttribute.showWind(work, node);
deleteFlag = 1;//當(dāng)點(diǎn)擊其他地方時(shí)設(shè)為0;
preDelNode = node;
}
2、點(diǎn)擊復(fù)制的時(shí)候判斷標(biāo)記是否為1,判斷PreDelNode是不是空,不是就根據(jù)上面說(shuō)的復(fù)制來(lái)執(zhí)行;刪除節(jié)點(diǎn)也是用這個(gè)思想;
3、 點(diǎn)擊粘貼,判斷中間節(jié)點(diǎn)是否時(shí)空,不是就在底層面板的起始處將節(jié)點(diǎn)復(fù)制下來(lái);否則就什么也不做;
客戶虐我千百遍,我待客戶如初戀!