溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java 2D 繪圖的開放源代碼備用方案 (轉)

發(fā)布時間:2020-08-09 11:37:07 來源:ITPUB博客 閱讀:171 作者:worldblog 欄目:編程語言
Java 2D 繪圖的開放源代碼備用方案 (轉)[@more@]
Java 2D 可能是在 Java 程序中編寫 2D 圖形程序的最顯著的解決方案,但它不是唯一的一個。在本文中,Java 開發(fā)者 John Carr 提出了一種優(yōu)秀的備用方案 — “Java 科學對象”(Java objects for Science(JSci)),一個開放源代碼的包,它使您能夠在 swing 中創(chuàng)建 2D 條形圖、餅形圖和折線圖。請在Javascript:void%20forumWindow()">討論論壇與本文作者和其他讀者分享您對本文的心得。

對于大多數 Java 開發(fā)者,任何類型的圖形開發(fā)在本質上都與 Java 2D 和 3D api 以及 java.awt.Graphics 有緊密聯(lián)系。雖然 Java 2D 和 3D API 為在 Swing 中創(chuàng)建圖形提供優(yōu)秀的工具,但您并非只可以任意使用它們,當然它們也不是最容易學的。對于那些沒有時間、需要或有興趣熬夜深刻了解 java.awt.Graphics 的人,我向您推薦一個開放源代碼的備用方案:JSci。

Java 科學對象(JSci)開放源代碼項目是 Durham(英國 Durham)大學粒子理論中心的三年級研究生 Mark Hale 創(chuàng)立的。JSci 是一個包集合,包含數學和科學類。 在撰寫本文時,JSci 的版本是 .87,運行在 Java 1.1.8、1.2.x 或 1.3.x 上,但將來可能為 Java 1.4 寫更新版本的 JSci。這個項目的目的是以可能有助于基于科學的軟件開發(fā)的最自然方式封裝科學方法和原則。支持 JSci 的設計哲學是基于這樣一種思想 — “直接從黑板到代碼”。也就是,數學概念和構造應該以某種方式封裝在代碼中。在某種程度上,JSci 作為對象設計實驗與作為數學庫差不多。

使用 JSci,您既可以在 AWT 中也可以在 Swing 中創(chuàng)建簡單的條形圖、折線圖和餅形圖。JSci.swing.JBarGraph、 JSci.swing.JPieChartJSci.swing.JLineGraph API 組件設計得也很好,這些組件和 AWT 繪圖類都遵守 Mvc 體系結構。

在本文中,我將介紹 JSci.swing 包并向您展示如何使用它的類和方法創(chuàng)建條形圖、餅形圖和折線圖。我們將首先看一下組成包的核心的類。

JSci.swing 包
用于在 Swing 中創(chuàng)建圖形的類位于 JSci.swing 包中。圖 1 顯示了 JSci.swing 的類圖表。JSci.swing 中的類,除 JImageCanvas 之外,都從 JDoubleBufferedComponent 繼承。注意:JDoubleBufferedComponentJImageCanvas 都是從 javax.swing.JComponent 繼承的。

我們將在下面部分詳細討論每個類的功能。

JDoubleBufferedComponent
JSci.swing 的超類是一個抽象類,被稱為 JDoubleBufferedComponent。這個類相對來說比較簡單,它為自己將要建立于其上的圖形提供雙緩沖功能。 雙緩沖指出接收組件是否應該使用緩沖區(qū)來繪畫。如果雙緩沖被設置為 true,那么來自這個組件的所有圖畫都將在屏外(offscreen)繪畫緩沖區(qū)完成。屏外繪畫緩沖區(qū)稍后將被復制到屏幕上。根據 javadocs,Swing 繪畫系統(tǒng)總是使用最大的雙緩沖區(qū)。如果一個組件有緩沖,而且它的其中一個父組件也有緩沖,那么就使用它的父組件的緩沖區(qū)。

JDoubleBufferedComponent 依靠自己而不是 Swing 的雙緩沖實現 JComponent 處理雙緩沖。這為使用 JSci.swing 包的開發(fā)者提供了比只使用 Swing 更細粒度的對雙緩沖的控制。

JImageCanvas
JImageCanvas 是另一個簡單的(straightforward)類。它的目的是允許圖像被直接添加到容器。 JImageCanvas 創(chuàng)建一個 java.awt.MediaTracker 實例來裝入和跟蹤圖像。

JContourPlot
contour plot 是 3 個數字變量之間關系的二維圖解表示。兩個變量用于 x 軸和 y 軸,第 3 個變量 z 用于等高位。等高位被繪制成曲線;曲線之間區(qū)域的顏色編碼表示內插值。

JGraph3D
JGraph3D 超類提供 2D 圖形的抽象封裝。JScatterGraphJLineGraph 都繼承了 JGraph3D。散點圖是一種統(tǒng)計圖,被繪制用來比較兩個數據集??捎盟鼇戆l(fā)現兩個數據集之間的相關性。 折線圖顯示兩段信息之間的關系,以及它們是如何依賴另一方發(fā)生變化的。 沿著折線圖一條邊的數字被稱為刻度。在后面我將更詳細地討論折線圖以及如何構造一個折線圖。

JCategoryGraph3D
JCategoryGraph3D 超類提供 2D 圖形分類的抽象封裝。JBarGraphJPieChart 都繼承了 JCategoryGraph3D。一個條形圖由一個坐標軸和一系列帶標簽的水平或垂直條組成,它們顯示每個條的不同值。 餅形圖是一個被分成若干部分的圓形圖,每部分顯示一些相關信息片段的大小。餅形圖被用于顯示組成整體的各部分的大小。我們將在后面討論關于構造餅形圖和條形圖的更多內容。

JLineGraph4D 和 JL.NETrace
如果您覺得自己需要挑戰(zhàn),那么請嘗試 JLineGraph4D。如名稱所暗示的那樣,JLineGraph4D 允許您向折線圖中添加第三維。JLineGraph4D 不單是讓您按照 x(水平)和 y(垂直)來考慮,而是讓您把 z(深度)也考慮在內。JLineTrace 允許您使用鼠標偵聽器跟蹤 2D 折線圖。

構造一個 JPieChart
要構造任何形狀的圖形,您都必須理解如何用一種有意義的方式來組織數據。不管是構造條形圖還是構造餅形圖,當涉及到數據建模時,其過程都是相同的。圖 2 提供一個數據示例,它可用于創(chuàng)建條形圖或餅形圖。

圖 2. 用于創(chuàng)建條形圖或餅形圖的示例數據
2003-10-241628580.gif" width=165>

一個 series 表示一個數字數組。對于 JPieChart,series 可以是 float 類型或 double 類型。一個 category 表示一個 series 內的數據列標識符。對于 JPieChart,category 用 String 形式表示。當然,您希望有一個對您的觀眾來說有些意義的 category,這就是為什么一個城市名看起來好象適合 PieGraph.java 中餅形圖示例中的每個 category 的原因所在。為簡單起見,餅形圖將只用一個數據 series。清單 1 分別包含 category 和 series 類變量。

清單 1. 為餅形圖定義 x 軸和數據 series

// ... public class PieGraph extends JFrame implements ItemListener { // ... private static final String CATEGORY_NAMES[] = { "London", "Paris", "New York" }; private static final float CONSUMERS_SERIES[] = { 45.3f, 27.1f, 55.5f }; // ... }



現在,已經定義了 CONSUMERS_NAMESSALES_SERIES 數組,我們就可以開始構造餅形圖。這個示例中使用的模型是 DefaultCategoryGraph3DModel。在下一部分,我將更深入地討論該模型的工作機制。清單 2 顯示如何創(chuàng)建一個 DefaultCategoryGraph3DModel 實例。

清單 2. 創(chuàng)建一個 DefaultCategoryGraph3DModel 實例

// ... private JPieChart getPieGraph() { // ... DefaultCategoryGraph3DModel model = new DefaultCategoryGraph3DModel(); model.setCategories(CATEGORY_NAMES); model.addSeries(CONSUMERS_SERIES); // ... } // ...



在實例化 DefaultCategoryGraph3DModel 之后,category 和 series 一定包含在這個模型中。通過 setCategories(String categoryNames[]) 方法設置一個 category。因為這個模型允許多個 series,所以方法 addSeries(float series[])addSeries(double series[]) 可用于這個目的。DefaultCategoryGraph3DModel 將每個增加的 series 存儲在 Vector 中。

這個 model 實例現在可用作 JPieChart 的構造器的一個輸入參數。 在創(chuàng)建了 JPieChart 實例后,可用 setColor(int category, java.awt.Color c) 方法定義每個 category 的顏色。 清單 3 顯示了如何創(chuàng)建 JPieChart 實例和設置 category 顏色。

清單 3. 設置餅形圖扇形塊顏色

// ... public class PieGraph extends JFrame implements ItemListener { // ... private static final int LONDON = 0; private static final int PARIS = 1; private static final int NEW_YORK = 2; // ... private JPieChart getPieGraph() { // ... JPieChart pieChart = new JPieChart(model); pieChart.setColor(LONDON,Color.blue); pieChart.setColor(PARIS,Color.yellow); pieChart.setColor(NEW_YORK,Color.orange); // ... } // ...



圖 3 用圖解形式說明了使用 PieGraph.java 創(chuàng)建的餅形圖。當用戶請求更改 category 的顏色(通過應用程序右邊的列表框)時,方法 JPieChart.setColor(int category, java.awt.Color c) 被調用來實現這種更改。要使這種更改可見,在設置了新的扇形塊顏色后調用 JPieChart.redraw() 方法。

圖 3. PieGraph.java 生成的餅形圖的輸出示例
PieGraph.java 生成的餅形圖的輸出示例

我們只討論了 DefaultCategoryGraph3DModel 用途的一些皮毛。其它還有許多有用的方法,包括允許您隱藏 series 的方法(setSeriesVisible(int series, boolean visible))或更改 series 數據的方法(changeSeries(int series, float newSeries[]))。 我鼓勵您在有空的時候更深入地研究這些類(請參閱sources">參考資料)。

構造一個 JBarGraph
構造一個條形圖首先必須象在構造餅形圖中那樣為數據建模。條形圖,和餅形圖一樣,有一個數據 series 數組(或多個)和一個 category 數組。我不使用 DefaultCategoryGraph3DModel 作為條形圖的模型,而是將說明如何為您的數據創(chuàng)建一個定制模型,以及 JBarGraph 怎樣在運行時期間使用這個模型生成條形圖。為使這個練習更加有趣,我將向您展示如何動態(tài)地更改 series 元素的值和重畫條形圖。

如圖 4 所示,一個定制 category 圖模型必須繼承 AbstractGraphModel 并實現 CategoryGraph3DModel(參見 SalesGraphModel.java)。注意:這部分涉及的方法繼承自 CategoryGraph3DModel 接口。

圖 4. 創(chuàng)建一個定制 category 圖模型
創(chuàng)建一個定制 category 圖模型

表 1 展示了 CategoryGraph3DModel 接口的方法以及 JBarGraph 如何在運行時利用模型。

表 1. CategoryGraph3DModel 接口

方法描述 public abstract void addGraphDataListener(GraphDataListener) 添加一個偵聽器 public abstract void firstSeries() 選擇第 1 個數據 series public abstract String getCategory(int i) 返回第 i 個 category public abstract float getValue(int i) 返回第 i 個 category 的值 public abstract boolean nextSeries() 選擇下一個數據 series public abstract void removeGraphDataListener(GraphDataListener) 除去一個偵聽器 public abstract int seriesLength() 返回當前 series 的長度

讓我們看一下這些方法的較為詳細一點的信息。

在運行時,JBarGraph 調用 public void firstSeries(),它選擇第 1 個數據 series。如果您正在處理多個數據 series,將它們存儲在 Vector 中是有意義的。調用的下一個方法是 public int seriesLength();這個方法返回當前 series 的長度。seriesLength() 方法返回的整型值被用于形成一個循環(huán)構造來獲取每個 category 以及那個 series 的相應整型值。在這個 series 循環(huán)中,調用 public String getCategory(int i) 獲取第 i 個 category 的名稱,并調用 public float getValue(int i) 獲取第 i 個 category 的值。(術語第 i 個是指 category 的順序,比如第 1、第 2、第 3 等等。)為當前 series 收集了全部數據元素后,調用 public boolean nextSeries() 方法選擇下一個數據 series。如果 nextSeries() 方法返回 true,那么為新數據 series 收集數據元素的過程將重新開始 — 從 seriesLength() 方法開始。當 nextSeries() 返回 false 時,模型中不再有數據 series。

BarGraph.javaSalesGraphModel 繼承 AbstractGraphModel 并實現 CategoryGraph3DModel。為簡單起見,將 SalesGraphModel 配置為只處理一個數據 series。如果您查看 SalesGraphModel 中的 nextSeries() 方法,您會注意到它只返回 false;這表明這個模型內只包含一個數據 series。傳給 SalesGraphModel 構造器的輸入參數包括 category 的名稱和一個數據 series。創(chuàng)建 SalesGraphModelJBarGraph 實例的代碼可在清單 4 的 getBarGraph() 方法中找到。

清單 4. 創(chuàng)建一個 JBarGraph

// ... public class BarGraph extends JFrame implements ActionListener { // ... private static final String CATEGORY_NAMES[] = { "London", "Paris", "New York" }; // ... private static final float SALES_SERIES[] = { 24.1f, 14.4f, 36.8f }; // ... private JBarGraph getBarGraph() { SalesGraphModel model = new SalesGraphModel(CATEGORY_NAMES, SALES_SERIES); barGraph = new JBarGraph(model); return barGraph; } // ...


圖 5 顯示 BarGraph.javaSalesGraphModel.java 產生的輸出。您或許已注意到,所有的 category 條顏色都一樣,這與餅形圖示例相反,在餅形圖示例中,每個 category 的顏色都不同。這是因為 JBarGraph 只允許您改變數據 series 的顏色表示,而不是 series 內的 category。

圖 5. BarGraph.java 生成的條形圖的輸出示例
BarGraph.java 生成的條形圖的輸出示例

SalesGraphModel 具有允許在數據 series 內增加 category 值的功能。當用戶從 BarGraph 示例選擇了一個 category(通過單選按鈕)并按下“Add 1 to total”按鈕時,public void incrementCategoryTotal(int i) 方法被調用。這個方法把選中要增加的 category 作為方法參數傳遞,傳遞后會有一個通知被發(fā)送到 JBarGraph 實例(通過 public void dataChanged(GraphDataEvent) 方法):模型中的數據已經發(fā)生改變,必須重畫此條形圖。清單 5 顯示了增加 category 總數的過程。

清單 5. 增加 category 總數

// ... public class BarGraph extends JFrame implements ActionListener { private void addToCategoryTotal(int category) { SalesGraphModel model = (SalesGraphModel)barGraph.getModel(); model.incrementCategoryTotal(category); } // ... } // ... public class SalesGraphModel extends AbstractGraphModel implements CategoryGraph3DModel { // ... private float seriesTotals[]; // ... public void incrementCategoryTotal(int i) { seriesTotals[i]++; } // ... }



構造一個 JLineGraph
JLineGraph 的模型與 JPieChartJBarGraph 的模型相似;這兩個模型都有多個數據 series 和一個標識 series 內數據的 category。但 JLineGraph 的模型引入了 x 軸和 y 軸坐標系的復雜性,在這一部分,我將研究如何創(chuàng)建一個定制的模型,并展示 JLineGraph 是如何在運行時期間使用這個模型生成一個折線圖的。注意:這一部分涉及的方法繼承自 Graph3DModel 接口。

一個定制的折線圖模型必須要從 AbstractGraphModel 繼承并實現 Graph3DModel 接口,如表 2 中所示。

表 2. Graph3DModel 接口

方法描述 public abstract void addGraphDataListener(GraphDataListener) 添加一個偵聽器 public abstract void firstSeries() 選擇第 1 個數據 series public abstract float getXCoord(int i) 返回第 i 個 category public abstract float getYCoord(int i) 返回第 i 個 category 的值 public abstract boolean nextSeries() 選擇下一個數據 series public abstract void removeGraphDataListener(GraphDataListener) 除去一個偵聽器 public abstract int seriesLength() 返回當前 series 的長度

這些方法的執(zhí)行與表 1 中所示的相似。

在運行時期間,JLineGraph 調用 public void firstSeries() 方法;這選擇第 1 個數據 series。調用的下一個方法是 public int seriesLength();它返回當前 series 的長度。seriesLength() 方法返回的整型值被用于形成一個循環(huán)構造以獲取那個 series 的 x 軸和 y 軸坐標值。在這個 series 循環(huán)中,調用 public float getXCoord(int i) 獲取第 i 個 category,并調用 public float getYCoord(int i) 獲取第 i 個 category 的值。為當前 series 收集了全部數據元素后,調用 public boolean nextSeries() 方法選擇下一個數據 series。如果 nextSeries() 方法返回 true,那么為新數據 series 收集數據元素的過程將重新開始 — 從 seriesLength() 方法開始。如果 nextSeries() 返回 false,那是因為模型中不再有數據 series。

缺省情況下,JLineGraph 只接受 x 軸和 y 軸的浮點值。在我的折線圖中,x 軸應該包含代表一年中前六月(一月到六月)的 String。要完成這個任務,必須發(fā)生兩件事:第一,必須繼承 Graph3DModel 接口以包含 public String getXLabel(float i) 方法。這樣您就能夠獲得 x 軸的 String 表示。第二,必須繼承 JLineGraph,并覆蓋 drawLabeledAxes(Graphics g) 方法。這一步將允許您使用新的接口訪問 getXLabel(float f) 方法。圖 6 是一個類圖表, 顯示創(chuàng)建一個帶標簽的折線圖涉及的類。

圖 6. 帶標簽折線圖的類圖表

創(chuàng)建 DemandGraphModelJLineGraph 類的實例代碼可在 getLineGraph() 方法中找到,清單 6 中顯示了這些代碼。

清單 6. 創(chuàng)建一個 JLineGraph 實例

// ... public class LineGraph extends JFrame { private JLineGraph lineGraph; private static final String MONTH_NAMES[] = { "Jan", "Feb", //... }; private static final int MONTH_NUMBERING[] = {0, 1, 2, 3, 4, 5 }; private static final int LONDON_SERIES = 0; private static final int PARIS_SERIES = 1; private static final int NEW_YORK_SERIES= 2; private static final float LONDON_DEMAND[] = { 1.2f, 3.7f, 6.7f, // ... }; private static final float PARIS_DEMAND[] = { 12.6f, 15.0f, 13.7f, // ... }; private static final float NEW_YORK_DEMAND[] = { 15.0f, 13.9f, 10.1f, // ... }; // ... private JLineGraph getLineGraph() { DemandGraphModel model = new DemandGraphModel(); model.addSeries(LONDON_DEMAND); model.addSeries(PARIS_DEMAND); model.addSeries(NEW_YORK_DEMAND); model.setXAxisLabel(MONTH_NAMES); model.setXAxis(MONTH_NUMBERING); lineGraph = new LabeledLineGraph(model); lineGraph.setColor(LONDON_SERIES, Color.red); lineGraph.setColor(PARIS_SERIES, Color.yellow); lineGraph.setColor(NEW_YORK_SERIES, Color.blue); lineGraph.setXIncrement(1); return lineGraph; } // ... }



清單 6 的最后一行顯示了對 LabeledLineGraph setXIncrement(int i) 方法的一次調用。完成這次調用是為了確保 x 軸是按照嚴格的增量畫的。換句話說,JLabeledLineGraph 將用最大化應用程序窗口或浮動窗口的相同坐標來標記 x 軸上的每一“格”。圖 7 顯示了 LineGraph.java 產生的輸出示例。調整應用程序窗口的大小,看看折線圖是如何被重畫以適合窗口新尺寸的。

圖 7. LineGraph.java 生成的折線圖的輸出示例
LineGraph.java 生成的折線圖的輸出

結論
JSci 是一個開放源代碼成果,主要用于科學應用。在本文中,我已經介紹了作為創(chuàng)建 2D 圖形工具的 JSci 的一些可能用途。JSci 是專業(yè)繪圖包的備用的免費軟件。雖然 JSci 并不提供專業(yè)畫圖包所帶的內建類型支持,您卻的確可以訪問源代碼,并且可以自由修改代碼,然后將它們提交回 JSci 社區(qū)。JSci 是一個不斷發(fā)展的成果,我認為它是一個很好的備用方案,您在用 Java 創(chuàng)建 2D 圖形時可以考慮使用它。


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI