JUNG是Java生态中常用的图可视化与图算法处理库,很多场景需要根据业务需求修改图中某条特定边的显示样式,比如高亮两个关联节点的连接边,或者根据边的权重调整线条粗细。要实现这个需求,首先需要理解JUNG中边的管理逻辑,再通过合理的方式定位目标边,最后调用对应的样式渲染接口完成修改。

JUNG中边的存储与定位逻辑
JUNG的图结构由Graph<V,E>接口定义,其中V代表节点类型,E代表边类型。边本身作为对象存储在图中,没有内置的唯一ID属性,因此不能直接通过ID快速查询。常见的定位指定边的方式有两种:一种是为边对象添加自定义属性,通过属性值匹配;另一种是通过遍历图中的所有边,根据边的两个端点节点匹配目标边。
为边添加自定义属性
如果需要频繁操作特定边,建议在创建边的时候为其添加自定义标记属性。JUNG提供了Map或者自定义边类的方式来存储额外属性,比如我们可以自定义一个边类,包含边的唯一标识字段:
// 自定义边类,包含唯一标识和业务属性
public class CustomEdge {
private String edgeId;
private String edgeType;
private int weight;
public CustomEdge(String edgeId, String edgeType, int weight) {
this.edgeId = edgeId;
this.edgeType = edgeType;
this.weight = weight;
}
// getter方法
public String getEdgeId() {
return edgeId;
}
public String getEdgeType() {
return edgeType;
}
public int getWeight() {
return weight;
}
}
创建图的时候指定边类型为自定义的CustomEdge,之后就可以通过边的edgeId属性快速匹配:
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import java.util.Collection;
public class JungEdgeDemo {
public static void main(String[] args) {
// 创建有向图,节点类型为String,边类型为自定义CustomEdge
DirectedSparseGraph<String, CustomEdge> graph = new DirectedSparseGraph<>();
// 添加节点
graph.addVertex("A");
graph.addVertex("B");
// 创建边并添加到图中
CustomEdge edge1 = new CustomEdge("edge_1", "关联边", 5);
graph.addEdge(edge1, "A", "B");
// 根据edgeId查找指定边
CustomEdge targetEdge = null;
Collection<CustomEdge> allEdges = graph.getEdges();
for (CustomEdge edge : allEdges) {
if ("edge_1".equals(edge.getEdgeId())) {
targetEdge = edge;
break;
}
}
System.out.println("找到目标边:" + (targetEdge != null ? targetEdge.getEdgeId() : "无"));
}
}
通过端点节点匹配边
如果只知道边的两个端点节点,也可以通过Graph的findEdge方法快速获取两个节点之间的边,这种方式适合不需要额外标记属性的简单场景:
import edu.uci.ics.jung.graph.DirectedSparseGraph;
public class JungEdgeDemo2 {
public static void main(String[] args) {
DirectedSparseGraph<String, String> graph = new DirectedSparseGraph<>();
graph.addVertex("A");
graph.addVertex("B");
// 添加边,边标识为"edge_1"
graph.addEdge("edge_1", "A", "B");
// 查找A到B的边
String targetEdge = graph.findEdge("A", "B");
System.out.println("A到B的边是:" + targetEdge);
}
}
修改指定边的样式
JUNG中边的样式由EdgeRenderer和Renderer.Edge<V,E>相关接口控制,我们可以通过自定义Transformer来实现不同边的差异化样式渲染。核心是给VisualizationViewer设置边的绘制转换器,根据边的属性返回不同的样式参数。
修改边的颜色和线宽
下面的示例演示如何将指定标识的边修改为红色,线宽设置为3,其余边保持默认黑色、线宽1:
import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import org.apache.commons.collections15.Transformer;
import javax.swing.*;
import java.awt.*;
public class EdgeStyleDemo {
public static void main(String[] args) {
// 创建图结构
DirectedSparseGraph<String, CustomEdge> graph = new DirectedSparseGraph<>();
graph.addVertex("A");
graph.addVertex("B");
graph.addVertex("C");
CustomEdge edge1 = new CustomEdge("edge_1", "关联边", 5);
CustomEdge edge2 = new CustomEdge("edge_2", "普通边", 2);
graph.addEdge(edge1, "A", "B");
graph.addEdge(edge2, "B", "C");
// 创建可视化查看器
VisualizationViewer<String, CustomEdge> vv = new VisualizationViewer<>(graph);
vv.setPreferredSize(new Dimension(800, 600));
// 设置节点标签
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<>());
// 设置边标签
vv.getRenderContext().setEdgeLabelTransformer(new Transformer<CustomEdge, String>() {
@Override
public String transform(CustomEdge edge) {
return edge.getEdgeId();
}
});
// 自定义边的颜色转换器:目标边红色,其余黑色
vv.getRenderContext().setEdgeDrawPaintTransformer(new Transformer<CustomEdge, Paint>() {
@Override
public Paint transform(CustomEdge edge) {
if ("edge_1".equals(edge.getEdgeId())) {
return Color.RED;
}
return Color.BLACK;
}
});
// 自定义边的线宽转换器:目标边线宽3,其余线宽1
vv.getRenderContext().setEdgeStrokeTransformer(new Transformer<CustomEdge, Stroke>() {
@Override
public Stroke transform(CustomEdge edge) {
if ("edge_1".equals(edge.getEdgeId())) {
return new BasicStroke(3.0f);
}
return new BasicStroke(1.0f);
}
});
// 添加鼠标控制
DefaultModalGraphMouse<String, CustomEdge> gm = new DefaultModalGraphMouse<>();
vv.setGraphMouse(gm);
// 显示窗口
JFrame frame = new JFrame("JUNG边样式修改示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(vv);
frame.pack();
frame.setVisible(true);
}
}
修改边的线条类型
如果需要将指定边修改为虚线,只需要调整Stroke的参数即可,比如下面的转换器可以将目标边设置为虚线:
// 虚线样式转换器
vv.getRenderContext().setEdgeStrokeTransformer(new Transformer<CustomEdge, Stroke>() {
@Override
public Stroke transform(CustomEdge edge) {
if ("edge_1".equals(edge.getEdgeId())) {
// 虚线参数:线段长度、间隔长度、相位偏移
return new BasicStroke(3.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, new float[]{10.0f, 5.0f}, 0.0f);
}
return new BasicStroke(1.0f);
}
});
注意事项
- 如果自定义边类,需要确保边类正确实现了
equals和hashCode方法,避免图中边对象匹配出现异常。 - 样式修改后如果界面没有实时更新,可以调用
vv.repaint()方法手动触发重绘。 - 如果需要动态切换边的样式,可以将目标边的标识存储在全局变量中,修改标识后重新设置转换器并刷新界面即可。