Activti流程框架_项目实战06高亮流程图

  该篇小白想聊一聊项目中如何高亮流程图,在默认情况下,activiti提供的API只能将历史活动节点和当前活动节点按红色边框默认设置,那么如何自定义出给定的颜色边框以及字体颜色呢?下面是两种高亮流程图的对比。
注:本篇的内容是借鉴了其他博主的博客,并通过实际项目验证了可行性;
在这里插入图片描述

1、自定义类和接口
1.1 定义ICustomProcessDiagramGenerator接口
package com.hlmc.webapp.activiti;

import java.awt.Color;
import java.io.InputStream;
import java.util.List;
import java.util.Set;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.image.ProcessDiagramGenerator;

public interface ICustomProcessDiagramGenerator extends ProcessDiagramGenerator {
	InputStream generateDiagram(BpmnModel bpmnModel,String imageType,List<String> highLightedActivities,List<String> highLightedFlows
			,String activityFontName,String labelFontName,String annotationFontName,ClassLoader classLoader,double scaleFactor,Color[] colors,Set<String> currIds);
}

1.2 定义CustomProcessDiagramGenerator类
package com.hlmc.webapp.activiti;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.Activity;
import org.activiti.bpmn.model.Artifact;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.bpmn.model.Lane;
import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
import org.activiti.bpmn.model.Pool;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

/**
* @autor 作者 E-mail: 
* @date  创建时间:2020年8月12日 上午9:34:11 @version 1.0
* @parameter
* @since
* @return
*/
@Component
public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator
		implements ICustomProcessDiagramGenerator {
	
	 //预初始化流程图绘制,大大提升了系统启动后首次查看流程图的速度
    static {
        new CustomProcessDiagramCanvas(10,10,0,0,"png", "宋体","宋体","宋体",null);
    }

    public CustomProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
                                                                                                     List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName,
                                                                                                     String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor,
                                                                                                     Color [] colors, Set<String> currIds) {

        if(null == highLightedActivities) {
            highLightedActivities = Collections.<String>emptyList();
        }
        if(null == highLightedFlows) {
            highLightedFlows = Collections.<String>emptyList();
        }

        prepareBpmnModel(bpmnModel);

        CustomProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);

        // Draw pool shape, if process is participant in collaboration
        for (Pool pool : bpmnModel.getPools()) {
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);
        }

        // Draw lanes
        for (Process process : bpmnModel.getProcesses()) {
            for (Lane lane : process.getLanes()) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
                processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);
            }
        }

        // Draw activities and their sequence-flows
        for (Process process: bpmnModel.getProcesses()) {
            List<FlowNode> flowNodeList= process.findFlowElementsOfType(FlowNode.class);
            for (FlowNode flowNode : flowNodeList) {
                drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, colors, currIds);
            }
        }

        // Draw artifacts
        for (Process process : bpmnModel.getProcesses()) {

            for (Artifact artifact : process.getArtifacts()) {
                drawArtifact(processDiagramCanvas, bpmnModel, artifact);
            }

            List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
            if (subProcesses != null) {
                for (SubProcess subProcess : subProcesses) {
                    for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
                        drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
                    }
                }
            }
        }

        return processDiagramCanvas;
    }

    protected void drawActivity(CustomProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode,
                                List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Color[] colors, Set<String> currIds) {
        ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
        if (drawInstruction != null) {

            drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);

            // Gather info on the multi instance marker
            boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
            if (flowNode instanceof Activity) {
                Activity activity = (Activity) flowNode;
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
                if (multiInstanceLoopCharacteristics != null) {
                    multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
                    multiInstanceParallel = !multiInstanceSequential;
                }
            }

            // Gather info on the collapsed marker
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
            if (flowNode instanceof SubProcess) {
                collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
            } else if (flowNode instanceof CallActivity) {
                collapsed = true;
            }

            if (scaleFactor == 1.0) {
                // Actually draw the markers
                processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
                        multiInstanceSequential, multiInstanceParallel, collapsed);
            }

            // Draw highlighted activities
            if (highLightedActivities.contains(flowNode.getId())) {
                if(!CollectionUtils.isEmpty(currIds)
                        &&currIds.contains(flowNode.getId())
                        && !(flowNode instanceof Gateway)) {//非结束节点,并且是当前节点
                    drawHighLight((flowNode instanceof StartEvent), processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()), colors[1]);
                }else {//普通节点
                    drawHighLight((flowNode instanceof StartEvent)||(flowNode instanceof EndEvent),processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()), colors[0]);
                }
            }

        }

        // Outgoing transitions of activity
        for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
            String flowId = sequenceFlow.getId();
            boolean highLighted = (highLightedFlows.contains(flowId));
            String defaultFlow = null;
            if (flowNode instanceof Activity) {
                defaultFlow = ((Activity) flowNode).getDefaultFlow();
            } else if (flowNode instanceof Gateway) {
                defaultFlow = ((Gateway) flowNode).getDefaultFlow();
            }

            boolean isDefault = false;
            if (defaultFlow != null && defaultFlow.equalsIgnoreCase(flowId)) {
                isDefault = true;
            }

            String sourceRef = sequenceFlow.getSourceRef();
            String targetRef = sequenceFlow.getTargetRef();
            FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
            FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(flowId);
            if (graphicInfoList != null && graphicInfoList.size() > 0) {
                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
                int xPoints[]= new int[graphicInfoList.size()];
                int yPoints[]= new int[graphicInfoList.size()];

                for (int i=1; i<graphicInfoList.size(); i++) {
                    GraphicInfo graphicInfo = graphicInfoList.get(i);
                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i-1);

                    if (i == 1) {
                        xPoints[0] = (int) previousGraphicInfo.getX();
                        yPoints[0] = (int) previousGraphicInfo.getY();
                    }
                    xPoints[i] = (int) graphicInfo.getX();
                    yPoints[i] = (int) graphicInfo.getY();

                }
                //画高亮线
                processDiagramCanvas.drawSequenceflow(xPoints, yPoints, false, isDefault, highLighted, scaleFactor, colors[0]);
                GraphicInfo lineCenter = getLineCenter(graphicInfoList);
                processDiagramCanvas.drawLabel(highLighted, sequenceFlow.getName(), lineCenter, Math.abs(xPoints[1]-xPoints[0]) >= 5);
            }
        }

        // Nested elements
        if (flowNode instanceof FlowElementsContainer) {
            for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
                if (nestedFlowElement instanceof FlowNode) {
                    drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
                            highLightedActivities, highLightedFlows, scaleFactor);
                }
            }
        }
    }
    protected void drawHighLight(boolean isStartOrEnd, CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo, Color color) {
        processDiagramCanvas.drawHighLight(isStartOrEnd, (int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), color);
    }

    protected static CustomProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
                                                                                                                 String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {

        // We need to calculate maximum values to know how big the image will be in its entirety
        double minX = Double.MAX_VALUE;
        double maxX = 0;
        double minY = Double.MAX_VALUE;
        double maxY = 0;

        for (Pool pool : bpmnModel.getPools()) {
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            minX = graphicInfo.getX();
            maxX = graphicInfo.getX() + graphicInfo.getWidth();
            minY = graphicInfo.getY();
            maxY = graphicInfo.getY() + graphicInfo.getHeight();
        }

        List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
        for (FlowNode flowNode : flowNodes) {

            GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());

            // width
            if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
                maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
            }
            if (flowNodeGraphicInfo.getX() < minX) {
                minX = flowNodeGraphicInfo.getX();
            }
            // height
            if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
                maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
            }
            if (flowNodeGraphicInfo.getY() < minY) {
                minY = flowNodeGraphicInfo.getY();
            }

            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
                if (graphicInfoList != null) {
                    for (GraphicInfo graphicInfo : graphicInfoList) {
                        // width
                        if (graphicInfo.getX() > maxX) {
                            maxX = graphicInfo.getX();
                        }
                        if (graphicInfo.getX() < minX) {
                            minX = graphicInfo.getX();
                        }
                        // height
                        if (graphicInfo.getY() > maxY) {
                            maxY = graphicInfo.getY();
                        }
                        if (graphicInfo.getY()< minY) {
                            minY = graphicInfo.getY();
                        }
                    }
                }
            }
        }

        List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
        for (Artifact artifact : artifacts) {

            GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());

            if (artifactGraphicInfo != null) {
                // width
                if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
                    maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
                }
                if (artifactGraphicInfo.getX() < minX) {
                    minX = artifactGraphicInfo.getX();
                }
                // height
                if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
                    maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
                }
                if (artifactGraphicInfo.getY() < minY) {
                    minY = artifactGraphicInfo.getY();
                }
            }

            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
            if (graphicInfoList != null) {
                for (GraphicInfo graphicInfo : graphicInfoList) {
                    // width
                    if (graphicInfo.getX() > maxX) {
                        maxX = graphicInfo.getX();
                    }
                    if (graphicInfo.getX() < minX) {
                        minX = graphicInfo.getX();
                    }
                    // height
                    if (graphicInfo.getY() > maxY) {
                        maxY = graphicInfo.getY();
                    }
                    if (graphicInfo.getY()< minY) {
                        minY = graphicInfo.getY();
                    }
                }
            }
        }

        int nrOfLanes = 0;
        for (Process process : bpmnModel.getProcesses()) {
            for (Lane l : process.getLanes()) {

                nrOfLanes++;

                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
                // // width
                if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
                    maxX = graphicInfo.getX() + graphicInfo.getWidth();
                }
                if (graphicInfo.getX() < minX) {
                    minX = graphicInfo.getX();
                }
                // height
                if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
                    maxY = graphicInfo.getY() + graphicInfo.getHeight();
                }
                if (graphicInfo.getY() < minY) {
                    minY = graphicInfo.getY();
                }
            }
        }

        // Special case, see https://activiti.atlassian.net/browse/ACT-1431
        if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
            // Nothing to show
            minX = 0;
            minY = 0;
        }

        return new CustomProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY,
                imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    }

    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities,
                                       List<String> highLightedFlows, String activityFontName, String labelFontName, String annotationFontName,
                                       ClassLoader customClassLoader, double scaleFactor, Color[] colors, Set<String> currIds) {
        CustomProcessDiagramCanvas customProcessDiagramCanvas = generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
                activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor,colors, currIds);
        BufferedImage bufferedImage = customProcessDiagramCanvas.generateBufferedImage(imageType);
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        ImageOutputStream imOut;
        try {
            imOut = ImageIO.createImageOutputStream(bs);
            ImageIO.write(bufferedImage, "PNG", imOut);
        } catch (IOException e) {
            e.printStackTrace();
        }

        InputStream is = new ByteArrayInputStream(bs.toByteArray());
        return is;
    }
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
        return generateDiagram(bpmnModel, imageType, Collections.<String>emptyList(), Collections.<String>emptyList(),
                activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, new Color[] {Color.BLACK, Color.BLACK}, null);
    }


1.3 CustomProcessDiagramCanvas类
package com.hlmc.webapp.activiti;

import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

import javax.imageio.ImageIO;

import org.activiti.bpmn.model.AssociationDirection;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.image.exception.ActivitiImageException;
import org.activiti.image.impl.DefaultProcessDiagramCanvas;
import org.activiti.image.util.ReflectUtil;

/**
* @autor 作者 E-mail: 
* @date  创建时间:2020年8月12日 下午1:36:52 @version 1.0
* @parameter
* @since
* @return
*/
public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
	  protected static Color LABEL_COLOR = new Color(0, 0, 0);

	    //font
	    protected String activityFontName = "宋体";
	    protected String labelFontName = "宋体";
	    protected String annotationFontName = "宋体";

	    private static volatile boolean flag = false;

	    public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
	        super(width, height, minX, minY, imageType);
	    }

	    public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType,
	                                      String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
	        super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName,
	                customClassLoader);
	    }

	    public void drawHighLight(boolean isStartOrEnd, int x, int y, int width, int height, Color color) {
	        Paint originalPaint = g.getPaint();
	        Stroke originalStroke = g.getStroke();

	        g.setPaint(color);
	        g.setStroke(MULTI_INSTANCE_STROKE);
	        if (isStartOrEnd) {
	            // 开始、结束节点画圆
	            g.drawOval(x, y, width, height);
	        } else {
	            // 非开始、结束节点画圆角矩形
	            RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 5, 5);
	            g.draw(rect);
	        }
	        g.setPaint(originalPaint);
	        g.setStroke(originalStroke);
	    }

	    public void drawSequenceflow(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault,
	                                 boolean highLighted, double scaleFactor, Color color) {
	        drawConnection(xPoints, yPoints, conditional, isDefault, "sequenceFlow", AssociationDirection.ONE, highLighted,
	                scaleFactor, color);
	    }

	    public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault,
	                               String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor,
	                               Color color) {

	        Paint originalPaint = g.getPaint();
	        Stroke originalStroke = g.getStroke();

	        g.setPaint(CONNECTION_COLOR);
	        if (connectionType.equals("association")) {
	            g.setStroke(ASSOCIATION_STROKE);
	        } else if (highLighted) {
	            g.setPaint(color);
	            g.setStroke(HIGHLIGHT_FLOW_STROKE);
	        }

	        for (int i = 1; i < xPoints.length; i++) {
	            Integer sourceX = xPoints[i - 1];
	            Integer sourceY = yPoints[i - 1];
	            Integer targetX = xPoints[i];
	            Integer targetY = yPoints[i];
	            Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);
	            g.draw(line);
	        }

	        if (isDefault) {
	            Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
	            drawDefaultSequenceFlowIndicator(line, scaleFactor);
	        }

	        if (conditional) {
	            Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
	            drawConditionalSequenceFlowIndicator(line, scaleFactor);
	        }

	        if (associationDirection.equals(AssociationDirection.ONE)
	                || associationDirection.equals(AssociationDirection.BOTH)) {
	            Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2],
	                    xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);
	            drawArrowHead(line, scaleFactor);
	        }
	        if (associationDirection.equals(AssociationDirection.BOTH)) {
	            Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);
	            drawArrowHead(line, scaleFactor);
	        }
	        g.setPaint(originalPaint);
	        g.setStroke(originalStroke);
	    }

	    public void drawLabel(boolean highLighted, String text, GraphicInfo graphicInfo, boolean centered) {
	        float interline = 1.0f;

	        // text
	        if (text != null && text.length() > 0) {
	            Paint originalPaint = g.getPaint();
	            Font originalFont = g.getFont();
	            if (highLighted) {
	                g.setPaint(WorkflowConstants.COLOR_NORMAL);
	            } else {
	                g.setPaint(LABEL_COLOR);
	            }
	            g.setFont(new Font(labelFontName, Font.BOLD, 10));

	            int wrapWidth = 100;
	            int textY = (int) graphicInfo.getY();

	            // TODO: use drawMultilineText()
	            AttributedString as = new AttributedString(text);
	            as.addAttribute(TextAttribute.FOREGROUND, g.getPaint());
	            as.addAttribute(TextAttribute.FONT, g.getFont());
	            AttributedCharacterIterator aci = as.getIterator();
	            FontRenderContext frc = new FontRenderContext(null, true, false);
	            LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);

	            while (lbm.getPosition() < text.length()) {
	                TextLayout tl = lbm.nextLayout(wrapWidth);
	                textY += tl.getAscent();
	                Rectangle2D bb = tl.getBounds();
	                double tX = graphicInfo.getX();
	                if (centered) {
	                    tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2);
	                }
	                tl.draw(g, (float) tX, textY);
	                textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent();
	            }

	            // restore originals
	            g.setFont(originalFont);
	            g.setPaint(originalPaint);
	        }
	    }

	    @Override
	    public BufferedImage generateBufferedImage(String imageType) {
	        if (closed) {
	            throw new ActivitiImageException("ProcessDiagramGenerator already closed");
	        }

	        // Try to remove white space
	        minX = (minX <= WorkflowConstants.PROCESS_PADDING) ? WorkflowConstants.PROCESS_PADDING : minX;
	        minY = (minY <= WorkflowConstants.PROCESS_PADDING) ? WorkflowConstants.PROCESS_PADDING : minY;
	        BufferedImage imageToSerialize = processDiagram;
	        if (minX >= 0 && minY >= 0) {
	            imageToSerialize = processDiagram.getSubimage(
	                    minX - WorkflowConstants.PROCESS_PADDING,
	                    minY - WorkflowConstants.PROCESS_PADDING,
	                    canvasWidth - minX + WorkflowConstants.PROCESS_PADDING,
	                    canvasHeight - minY + WorkflowConstants.PROCESS_PADDING);
	        }
	        return imageToSerialize;
	    }

	    @Override
	    public void initialize(String imageType) {
	        this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB);
	        this.g = processDiagram.createGraphics();

	        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
	        g.setPaint(Color.black);

	        Font font = new Font(activityFontName, Font.BOLD, FONT_SIZE);
	        g.setFont(font);
	        this.fontMetrics = g.getFontMetrics();

	        LABEL_FONT = new Font(labelFontName, Font.ITALIC, 10);
	        ANNOTATION_FONT = new Font(annotationFontName, Font.PLAIN, FONT_SIZE);
	        //优化加载速度
	        if(flag) {
	            return;
	        }
	        try {
	            USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/userTask.png", customClassLoader));
	            SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/scriptTask.png", customClassLoader));
	            SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/serviceTask.png", customClassLoader));
	            RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/receiveTask.png", customClassLoader));
	            SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/sendTask.png", customClassLoader));
	            MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/manualTask.png", customClassLoader));
	            BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/businessRuleTask.png", customClassLoader));
	            SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/shellTask.png", customClassLoader));
	            CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/camelTask.png", customClassLoader));
	            MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/muleTask.png", customClassLoader));

	            TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/timer.png", customClassLoader));
	            COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate-throw.png", customClassLoader));
	            COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate.png", customClassLoader));
	            ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error-throw.png", customClassLoader));
	            ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error.png", customClassLoader));
	            MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message-throw.png", customClassLoader));
	            MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message.png", customClassLoader));
	            SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal-throw.png", customClassLoader));
	            SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal.png", customClassLoader));
	/*        String baseUrl = Thread.currentThread().getContextClassLoader().getResource("static/img/activiti/").getPath();
	          SCRIPTTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"scriptTask.png"));
	          USERTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"userTask.png"));
	          SERVICETASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"serviceTask.png"));
	          RECEIVETASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"receiveTask.png"));
	          SENDTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"sendTask.png"));
	          MANUALTASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"manualTask.png"));
	          BUSINESS_RULE_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"businessRuleTask.png"));
	          SHELL_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"shellTask.png"));
	          CAMEL_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"camelTask.png"));
	          MULE_TASK_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"muleTask.png"));

	          TIMER_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"timer.png"));
	          COMPENSATE_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"compensate-throw.png"));
	          COMPENSATE_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"compensate.png"));
	          ERROR_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"error-throw.png"));
	          ERROR_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"error.png"));
	          MESSAGE_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"message-throw.png"));
	          MESSAGE_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"message.png"));
	          SIGNAL_THROW_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"signal-throw.png"));
	          SIGNAL_CATCH_IMAGE = ImageIO.read(new FileInputStream(baseUrl+"signal.png"));*/
	            flag = true;
	        } catch (IOException e) {
	            flag = false;
	            LOGGER.warn("Could not load image for process diagram creation: {}", e.getMessage());
	        }
	    }

}

1.4 定义 WorkflowConstants常量类
package com.hlmc.webapp.activiti;

import java.awt.Color;

/**
* @autor 作者 E-mail: 
* @date  创建时间:2020年8月12日 下午1:39:21 @version 1.0
* @parameter
* @since
* @return
*/
public final class WorkflowConstants {
	 /**businessKey**/
    public static final String WORKLOW_BUSINESS_KEY = "businessKey"; 
    /** 动态流程图颜色定义 **/
    public static final Color COLOR_NORMAL = new Color(0, 205, 0);
    public static final Color COLOR_CURRENT = new Color(255, 0, 0);
    /** 定义生成流程图时的边距(像素) **/
    public static final int PROCESS_PADDING = 5;
    /**提交按钮的名称**/
    public static final String SUBMIT_NAME="sp_outcome";
    /**审批意见**/
    public static final String APPROVAL_OPINION="sp_advise";
    /**审批通过标志名称**/
    public static final String APPROVAL="同意、通过、归档";
    /**撤销申请标志名称**/
    public static final String CANCEL="放弃、放弃申请";

}

2. 定义获取高亮连线的方法
/**commonWorkFlowServiceImpl.getHighLightedFlows**/
private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity,
			List<HistoricActivityInstance> historicActivityInstances) {
		List<String> highLightedFlows=new ArrayList<>();
		
		for(int i=0;i<historicActivityInstances.size()-1;i++) {
			//获取节点的详细信息
			ActivityImpl activityImpl=processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());
			List<ActivityImpl> sameStartTimeNodes=new ArrayList<>();//保存后续开始时间一样的节点
			ActivityImpl sameActivityImpl=processDefinitionEntity.findActivity(historicActivityInstances.get(i+1).getActivityId());
			//将后续第一个节点放在时间相同节点的集合里
			sameStartTimeNodes.add(sameActivityImpl);
			for(int j=i+1;j<historicActivityInstances.size()-1;j++) {
				HistoricActivityInstance activityImpl1=historicActivityInstances.get(j);
				HistoricActivityInstance activityImpl2=historicActivityInstances.get(j+1);
				if(Math.abs(activityImpl1.getStartTime().getTime()-activityImpl2.getStartTime().getTime())<200) {
				//if(activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
					//如果第一个节点和第二个节点开始时间相同保存
					ActivityImpl sameActivityImpl2=processDefinitionEntity.findActivity(activityImpl2.getActivityId());
					sameStartTimeNodes.add(sameActivityImpl2);
				}else {
					//不同就跳出循环
					break;
				}
			}
			List<PvmTransition> pvmTransitions=activityImpl.getOutgoingTransitions();
			for(PvmTransition pvmTransition:pvmTransitions) {
				//对所有的线进行遍历
				ActivityImpl pvmActivityImpl=(ActivityImpl)pvmTransition.getDestination();
				//如果取出来的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
				if(sameStartTimeNodes.contains(pvmActivityImpl)) {
					
					highLightedFlows.add(pvmTransition.getId());
				}
			}
			
		}
		return highLightedFlows;
	}

3. 定义获取流程状态图的方法
    @Override
	public InputStream findProcessStatusDiagram(String processInstanceId) {
		//获取流程实例
		HistoricProcessInstance processInstance	=historyService.createHistoricProcessInstanceQuery()
				.processInstanceId(processInstanceId)
				.singleResult();
		//获取流程中已经执行的节点,按照执行先后顺序排序
		List<HistoricActivityInstance> historicActivityInstances=historyService.createHistoricActivityInstanceQuery()
				.processInstanceId(processInstanceId)
				.orderByHistoricActivityInstanceId()
				.asc()
				.list();
		//构造已执行的节点Id集合
		List<String> executedActivityIds=new ArrayList<>();
		for(HistoricActivityInstance activityInstance:historicActivityInstances) {
			executedActivityIds.add(activityInstance.getActivityId());
		}		
		//获取bpmnModel
		BpmnModel bpmnModel=repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
		//获取流程定义实体
		ProcessDefinitionEntity processDefinitionEntity=(ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
		//获取流程已经发生流转的线Id集合
		List<String> hightListedFlows=this.getHighLightedFlows(processDefinitionEntity,historicActivityInstances);
		 
		Set<String> currIds=runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list()
				.stream().map(e->e.getActivityId()).collect(Collectors.toSet());	
		//默认方法
		//ProcessDiagramGenerator p=new DefaultProcessDiagramGenerator();
		//ProcessDiagramGenerator p=processEngineConfiguration.getProcessDiagramGenerator();
		//InputStream imageStream=p.generateDiagram(bpmnModel, "png", executedActivityIds,hightListedFlows,"","宋体","宋体",null,1.0);				
		//自定义方法
		processEngineConfiguration.setProcessDiagramGenerator(customProcessDiagramGenerator);
		ICustomProcessDiagramGenerator diagramGenerator=(ICustomProcessDiagramGenerator)processEngineConfiguration.getProcessDiagramGenerator();
		InputStream imageStream=diagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIds,hightListedFlows,"","宋体","宋体",null,1.0,new Color[] {WorkflowConstants.COLOR_NORMAL,WorkflowConstants.COLOR_CURRENT},currIds);		
		return imageStream;
	}

4. 控制请求映射
	@RequestMapping("/processstatus")
	@ResponseBody
	public void processStatus(@RequestParam("processInstanceId")String processInstanceId)   {
		
		InputStream imageStream=commonWorkFlowService.findProcessStatusDiagram(processInstanceId);		
		byte[] b=new byte[1024];
		int len=-1;
		getResponse().setContentType("image/png;charset=UTF-8");
		try {
			while((len=imageStream.read(b, 0, 1024))!=-1) {
				getResponse().getOutputStream().write(b, 0, len);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				imageStream.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}

5.前端获取img

  前端通过配置img标签的src属性从服务器端获取图片流资源,然后解析呈现给用户;

<img title="" width="100%" height="100%" src="${base}/admin/business/processstatus?processInstanceId=${processInstanceId}" />

在这里插入图片描述

6、总结
  1. 当activiti默认高亮流程图不满足需求时,可以自定义外框、字体颜色;
  2. 前端通过设置img标签的src属性获取流程图资源;

(更多有关activiti的项目开发内容请等下次更新,欢迎点评,共同进步哦!)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值