言归正传,这是一个工作流系统中的流程设计器,整个设计完全面向接口,所以有比较好的扩展性,小弟我水平有限,一些功能就没有加上,有兴趣的XD可以完善这些代码(其实大部分功能都已经实现了,包括流程图路径验证,只是现在只能绘制流程生成XML,没有反向去实现将XML生成流程图),可能自己想起来会再把他翻出来改改,在某个时间将图形转换成HTML能够直接在网页上显示,为什么这么干,你懂得!!
package com.applet.flows; import java.awt.Container; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Vector; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.event.MouseInputAdapter; import com.applet.flows.attr.NodeAttribute; import com.applet.flows.panel.FlowLine; import com.applet.flows.panel.FlowPanel; public abstract class NodeButtonSupport extends JButton implements INodeButton { private Collection<INodeButton> childs = null; private Collection<INodeButton> parents = null; private int id = -1; protected DropTarget dropTarget = null; public NodeButtonSupport(){ this.childs = new Vector<INodeButton>(); this.parents = new Vector<INodeButton>(); this.dropTarget = new DropTarget(this,DnDConstants.ACTION_COPY,this,true); new MouseApdaterListener(this); this.setSize(100, 40); this.setLocation(0, 45); } public ImageIcon getIcon(){ return null; } public final String[] asXML() { String xml = ""; xml = "<Node id=\"" + this.getID() + "\" type=\"" + this.getType() + "\" name=\"" + this.getText() + "\" x=\"" + this.getX() + "\" y=\"" + this.getY() + "\" />"; String to = ""; for(INodeButton nb:this.getChilds()){ to += nb.getID() + ","; } String line = ""; if(!to.trim().equals("")){ line = "<Line from=\"" + this.getID() + "\" to=\"" + to.substring(0,to.length() - 1) + "\" />"; } return new String[]{xml,line}; } public final void init(int id) { this.id = id; ImageIcon icon = this.getIcon(); if(icon != null){ this.setIcon(icon); } this.init(); } public INodeButton getThis(){ return this; } /** * 判断当某个对象接入子对象后,是否会成为一个循环流程 * @param button 接入后的对象 * @return true:是一个循环,false:不是循环 */ public boolean isPathed(INodeButton button){ for(INodeButton nb : this.getChilds()){ if(nb == button){ return true; } if(nb instanceof NodeButtonSupport){ return ((NodeButtonSupport)nb).isPathed(button); } } return false; } /** * 找到Frame对象 * @param jc * @return */ public FlowPanel getFrame(Container jc){ if(jc == null){ return null; } if(jc instanceof FlowPanel){ return (FlowPanel)jc; } return this.getFrame(jc.getParent()); } protected abstract void init(); public int getID(){ return this.id; } public abstract boolean link(INodeButton button); public void addParent(INodeButton nb){ this.parents.add(nb); } public void addChild(INodeButton nb){ this.childs.add(nb); } public void dragEnter(DropTargetDragEvent arg0) { } public void dragExit(DropTargetEvent arg0) { } public void dragOver(DropTargetDragEvent arg0) { } public final void drop(DropTargetDropEvent e) { DataFlavor ob = e.getCurrentDataFlavors()[0]; try { if(!e.getTransferable().isDataFlavorSupported(ob)){ JOptionPane.showMessageDialog(null, "应用程序不支持对象序列化\n[" + ob.toString() + "]","错误",JOptionPane.ERROR_MESSAGE); return; } INodeButton db = (INodeButton)e.getTransferable().getTransferData(ob); if(db == null){ JOptionPane.showMessageDialog(null, "应用程序无法读取对象序列化\n[" + ob.toString() + "]","错误",JOptionPane.ERROR_MESSAGE); return; } if(isPathed(db)){ return; } if(db.report(this) && this.link(db)){ this.addParent(db); db.addChild(this); FlowPanel frame = this.getFrame(this.getParent()); if(frame == null){ JOptionPane.showMessageDialog(null, "程序无法找到父容器","错误",JOptionPane.ERROR_MESSAGE); }else{ FlowLine line = new FlowLine(new JButton[]{(JButton)db,this}); line.setSize(frame.getSize()); line.setLocation(0,0); frame.add(line); frame.setText("[" + this.getText() + "]连线成功"); frame.repaint(); } } } catch (Exception e1) { this.getFrame(this.getParent()).setText(e1.getClass().getName()); JOptionPane.showMessageDialog(null, e1.toString(),"错误",JOptionPane.ERROR_MESSAGE); e1.printStackTrace(); } } @Override public void setToolTipText(String str) { str = "[ID=" + this.getID() + "]\n" + str; super.setToolTipText(str); } public void dropActionChanged(DropTargetDragEvent arg0) { } public final Collection<INodeButton> getChilds(){ return this.childs; } public final Collection<INodeButton> getParents(){ return this.parents; } /** * 获取节点类型 * @return 节点类型 */ public abstract String getType(); public final class MouseApdaterListener extends MouseInputAdapter{ private NodeButtonSupport support = null; private Point point = new Point(0, 0); public MouseApdaterListener(NodeButtonSupport support){ this.support = support; this.support.addMouseListener(this); this.support.addMouseMotionListener(this); } @Override public void mouseClicked(MouseEvent e) { new NodeAttribute(this.support).setVisible(true); } public void mousePressed(MouseEvent e) { if(e.getButton() != 1){ JComponent com=(JComponent)e.getSource(); TransferHandler handler = new TransferHandler("this"); com.setTransferHandler(handler); handler.exportAsDrag(com, e, DnDConstants.ACTION_COPY); }else{ this.point = SwingUtilities.convertPoint(this.support, e.getPoint(), this.support.getParent()); } } @Override public void mouseDragged(MouseEvent e) { if(e.getButton() != 1){ Point newPoint = SwingUtilities.convertPoint(this.support, e.getPoint(), this.support.getParent()); Point btnPoint = new Point((this.support.getX() + (newPoint.x - this.point.x )),(this.support.getY() + (newPoint.y - this.point.y))); this.support.setLocation(btnPoint); point = newPoint; FlowPanel frame = this.support.getFrame(this.support.getParent()); frame.repaint(); } } } }
强调一下,NodeButtonSupport是所有节点的抽象类,如果希望扩展节点类型的XD在增加类型的时候一定别忘了继承他,里面很多算法我都已经实现好了,当然还有两个接口。
package com.applet.flows; /** * 基本XML流程对象 * * @author 熊浩华 - xionghh * * 2011-3-10:下午12:29:47- */ public interface IFlow { /** * 返回该对象的XML分为{节点,连接线} * @return */ public String[] asXML(); /** * 判断节点是否是合法 * @return 当节点存在异常时抛出节点异常信息,否则返回true */ public boolean checkNode() throws Exception; }
导出XML文档的接口,所有的节点都实现该接口用于保存时生成XML文档方便,代码派生起来也容易。
package com.applet.flows; import java.awt.dnd.DropTargetListener; import java.util.Collection; /** * 流程节点接口,用于处理流程的节点 * * @author 熊浩华 - xionghh * * 2011-3-10:上午10:02:53- */ public interface INodeButton extends DropTargetListener,IFlow { /** * 初始化节点对象 * @param id 对象ID */ public void init(int id); /** * 某个节点拖入,是否允许他和你进行链接 * @param button 发起链接的对象 * @return 判断是否允许链接,返回true表示允许,false表示不允许操作 */ public boolean link(INodeButton button); /** * 通知节点,你被拖动到某个节点上,需要你和他进行链接 * @param button 需要链接的对象 * @return 允许链接返回true,不允许链接返回false */ public boolean report(INodeButton button); /** * 获取对象ID * @return 对象ID */ public int getID(); /** * 添加一个父对象 * @param nb */ public void addParent(INodeButton nb); /** * 添加一个子对象 * @param nb */ public void addChild(INodeButton nb); /** * 获取所有子对象 * @return */ public Collection<INodeButton> getChilds(); /** * 获取所有父对象 * @return */ public Collection<INodeButton> getParents(); }
INodeButton是所有节点的抽象接口,其实很多方法都是想到的时候补上的,开始打算写这个东西的时候没有想把他做成什么样,就当玩票,没仔细去对接口进行详细的设计,很粗略的就描绘了一下。所以看起来有点似是而非。
最后说一下Demo,压缩包里有tomcat的工程目录,XFLow直接Copy到tomcat下的webapps里就能运行,里面两个包flows.jar和dom4j-1.6.1.jar是签名过的,否则无法当成Applet运行,源码在src里,修改代码后想要在Applet测试通过记得打成包然后对包进行签名。
PS:呵呵,老规矩,CSDN(最近穷疯了,分不够了,希望各位给小弟我捧个场)
下载地址 : http://download.csdn.net/source/3082444