首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Draw2D 與 SWT 繪圖實戰:從資料縮放到 XY 圖表完整實作

     編輯:Java 相關知識

為什麼選擇 Draw2D?

  • 輕量級圖形框架,建構於 SWT Canvas 之上
  • 無第三方依賴(Eclipse 平台內建)
  • 提供幾何運算、圖表佈局、連線錨點等豐富 API
  • 適合自製圖表、流程圖、拓撲圖等視覺化需求

核心架構組件

類別角色
FigureCanvasSWT Canvas 擴充,承載 Draw2D 圖形樹
Panel通用容器圖形,採用 XYLayout 絕對定位
DirectedGraph有向圖模型:Node(點)+ Edge(線)
Node / Edge圖論模型元素,儲存座標與連線關係
Dot (extends Ellipse)資料點視覺表現
PolylineConnection折線連線,支援錨點自動對齊

五步驟繪圖流程

步驟 1:資料準備

private List<double[]> generateData() {
    List<double[]> series = new ArrayList<>();
    // 系列 1:線性遞增
    double[] s1 = new double[5];
    for (int i = 0; i < 5; i++) s1[i] = i * 10 + 10;  // 10,20,30,40,50
    
    // 系列 2:隨機分布
    double[] s2 = {20, 150, 5, 90, 35, 20, 150, 5, 45};
    
    // 系列 3:線性遞增
    double[] s3 = new double[7];
    for (int i = 0; i < 7; i++) s3[i] = i * 20 + 15;
    
    series.add(s1); series.add(s2); series.add(s3);
    return series;
}

步驟 2:座標縮放演算法(核心數學)

X 軸:等距分段

private int[] calculateXCoordinates(List<double[]> series) {
    int xSpan = 800;  // 繪圖區寬度
    int maxPoints = series.stream().mapToInt(arr -> arr.length).max().orElse(0);
    int segmentWidth = xSpan / maxPoints;
    
    int[] xCoords = new int[maxPoints];
    for (int i = 0; i < maxPoints; i++) {
        xCoords[i] = (i + 1) * segmentWidth;  // 避開 Y 軸區域
    }
    return xCoords;
}

Y 軸:三值線性插值

原始範圍:[minValue, maxValue]  →  目標範圍:[0, ySpan]
公式:y = ySpan - (value - minValue) * ySpan / (maxValue - minValue)
     (減去是因為螢幕座標 Y 軸向下)
private int[] calculateYCoordinates(double[] data, int ySpan) {
    double min = Arrays.stream(data).min().orElse(0);
    double max = Arrays.stream(data).max().orElse(1);
    double range = max - min;
    
    int[] yCoords = new int[data.length];
    for (int i = 0; i < data.length; i++) {
        double ratio = (data[i] - min) / range;
        yCoords[i] = (int) (ySpan - ratio * ySpan);  // 翻轉 Y 軸
    }
    return yCoords;
}

步驟 3:建立繪圖環境

public void plot(Composite parent, List<double[]> seriesData) {
    // 1. SWT 容器
    Composite composite = new Composite(parent, SWT.BORDER);
    composite.setLayout(new FillLayout());
    
    // 2. FigureCanvas + Panel
    FigureCanvas canvas = new FigureCanvas(composite);
    Panel contents = new Panel();
    contents.setLayoutManager(new XYLayout());
    
    // 3. 計算繪圖區範圍
    Rectangle clientArea = contents.getClientArea();
    int xSpan = clientArea.width - 80;   // 保留左邊 Y 軸空間
    int ySpan = clientArea.height - 60;  // 保留底部 X 軸空間
    
    // 4. 繪製座標軸
    drawAxes(contents, xSpan, ySpan);
    
    // 5. 建立圖模型並繪製各系列
    for (int i = 0; i < seriesData.size(); i++) {
        DirectedGraph graph = buildGraph(seriesData.get(i), i, xSpan, ySpan);
        drawGraph(contents, graph, getSeriesColor(i));
    }
    
    canvas.setContents(contents);
}

步驟 4:建構 DirectedGraph 模型

private DirectedGraph buildGraph(double[] data, int seriesIndex, int xSpan, int ySpan) {
    int[] xCoords = calculateXCoordinates(Collections.singletonList(data));
    int[] yCoords = calculateYCoordinates(data, ySpan);
    
    NodeList nodes = new NodeList();
    EdgeList edges = new EdgeList();
    
    // 建立節點
    for (int i = 0; i < data.length; i++) {
        Node node = new Node(new Double(data[i]));
        node.x = xCoords[i];
        node.y = yCoords[i];
        nodes.add(node);
    }
    
    // 建立邊(連線)
    for (int i = 0; i < nodes.size() - 1; i++) {
        Node left = nodes.getNode(i);
        Node right = nodes.getNode(i + 1);
        Edge edge = new Edge(left, right);
        edge.start = new Point(left.x, left.y);
        edge.end = new Point(right.x, right.y);
        edges.add(edge);
    }
    
    DirectedGraph graph = new DirectedGraph();
    graph.nodes = nodes;
    graph.edges = edges;
    new BreakCycles().visit(graph);  // 佈局演算法
    return graph;
}

步驟 5:將模型渲染為視覺圖形

private void drawGraph(Panel contents, DirectedGraph graph, Color color) {
    // 繪製資料點
    for (int i = 0; i < graph.nodes.size(); i++) {
        Node node = graph.nodes.getNode(i);
        Dot dot = new Dot();
        dot.setForegroundColor(color);
        dot.setBackgroundColor(color);
        node.data = dot;  // 儲存視覺物件供連線使用
        contents.add(dot);
        contents.setConstraint(dot, new Rectangle(node.x - 3, node.y - 3, 6, 6));
    }
    
    // 繪製連線
    for (int i = 0; i < graph.edges.size(); i++) {
        Edge edge = graph.edges.getEdge(i);
        PolylineConnection line = new PolylineConnection();
        line.setForegroundColor(color);
        line.setLineWidth(2);
        
        // 使用橢圓錨點讓連線自動貼合圓點邊緣
        line.setSourceAnchor(new EllipseAnchor((Dot) edge.source.data));
        line.setTargetAnchor(new EllipseAnchor((Dot) edge.target.data));
        
        contents.add(line);
    }
}

座標軸繪製輔助類別

  • XRulerBar:X 軸刻度、標籤、網格線
  • YRulerBar:Y 軸刻度、標籤、網格線
  • 皆繼承 Figure,實作 paintFigure(Graphics)

擴充應用:其他圖表類型

掌握「資料→座標→模型→視覺」流程後,易於擴充:

  • 長條圖:Node 用 RectangleFigure,高度對應數值
  • 圓餅圖:極座標換算,用 ArcFigure
  • 散佈圖:X/Y 皆為資料值,不等距分段
  • 即時更新:背景執行緒更新模型 → canvas.getUpdateManager().performUpdate()

效能與記憶體管理

  • 大量資料點時啟用虛擬化:僅繪製可視區域
  • 定期呼叫 CtClass.detach() 釋放 Javassist 資源(若用於動態類別)
  • 圖形樹過大時考慮分頁或層級載入
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved