您好,登錄后才能下訂單哦!
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.JPieChart
和 JSci.swing.JLineGraph
API 組件設計得也很好,這些組件和 AWT 繪圖類都遵守 Mvc 體系結構。
在本文中,我將介紹 JSci.swing
包并向您展示如何使用它的類和方法創(chuàng)建條形圖、餅形圖和折線圖。我們將首先看一下組成包的核心的類。
JSci.swing 包
用于在 Swing 中創(chuàng)建圖形的類位于 JSci.swing
包中。圖 1 顯示了 JSci.swing
的類圖表。JSci.swing
中的類,除 JImageCanvas
之外,都從 JDoubleBufferedComponent
繼承。注意:JDoubleBufferedComponent
和 JImageCanvas
都是從 javax.swing.JComponent
繼承的。
我們將在下面部分詳細討論每個類的功能。
JDoubleBufferedComponentJSci.swing
的超類是一個抽象類,被稱為 JDoubleBufferedComponent
。這個類相對來說比較簡單,它為自己將要建立于其上的圖形提供雙緩沖功能。 雙緩沖指出接收組件是否應該使用緩沖區(qū)來繪畫。如果雙緩沖被設置為 true,那么來自這個組件的所有圖畫都將在屏外(offscreen)繪畫緩沖區(qū)完成。屏外繪畫緩沖區(qū)稍后將被復制到屏幕上。根據 javadocs,Swing 繪畫系統(tǒng)總是使用最大的雙緩沖區(qū)。如果一個組件有緩沖,而且它的其中一個父組件也有緩沖,那么就使用它的父組件的緩沖區(qū)。
JDoubleBufferedComponent
依靠自己而不是 Swing 的雙緩沖實現 JComponent
處理雙緩沖。這為使用 JSci.swing
包的開發(fā)者提供了比只使用 Swing 更細粒度的對雙緩沖的控制。
JImageCanvasJImageCanvas
是另一個簡單的(straightforward)類。它的目的是允許圖像被直接添加到容器。 JImageCanvas
創(chuàng)建一個 java.awt.MediaTracker
實例來裝入和跟蹤圖像。
JContourPlot
contour plot 是 3 個數字變量之間關系的二維圖解表示。兩個變量用于 x 軸和 y 軸,第 3 個變量 z 用于等高位。等高位被繪制成曲線;曲線之間區(qū)域的顏色編碼表示內插值。
JGraph3DJGraph3D
超類提供 2D 圖形的抽象封裝。JScatterGraph
和 JLineGraph
都繼承了 JGraph3D
。散點圖是一種統(tǒng)計圖,被繪制用來比較兩個數據集??捎盟鼇戆l(fā)現兩個數據集之間的相關性。 折線圖顯示兩段信息之間的關系,以及它們是如何依賴另一方發(fā)生變化的。 沿著折線圖一條邊的數字被稱為刻度。在后面我將更詳細地討論折線圖以及如何構造一個折線圖。
JCategoryGraph3DJCategoryGraph3D
超類提供 2D 圖形分類的抽象封裝。JBarGraph
和 JPieChart
都繼承了 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_NAMES
和 SALES_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 生成的餅形圖的輸出示例
我們只討論了 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 圖模型
表 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.java
中 SalesGraphModel
繼承 AbstractGraphModel
并實現 CategoryGraph3DModel
。為簡單起見,將 SalesGraphModel
配置為只處理一個數據 series。如果您查看 SalesGraphModel
中的 nextSeries()
方法,您會注意到它只返回 false;這表明這個模型內只包含一個數據 series。傳給 SalesGraphModel
構造器的輸入參數包括 category 的名稱和一個數據 series。創(chuàng)建 SalesGraphModel
和 JBarGraph
實例的代碼可在清單 4 的 getBarGraph()
方法中找到。
// ... 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.java
和 SalesGraphModel.java
產生的輸出。您或許已注意到,所有的 category 條顏色都一樣,這與餅形圖示例相反,在餅形圖示例中,每個 category 的顏色都不同。這是因為 JBarGraph
只允許您改變數據 series 的顏色表示,而不是 series 內的 category。
圖 5. 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]++; } // ... }
構造一個 JLineGraphJLineGraph
的模型與 JPieChart
或 JBarGraph
的模型相似;這兩個模型都有多個數據 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)建 DemandGraphModel
和 JLineGraph
類的實例代碼可在 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 生成的折線圖的輸出示例
結論
JSci 是一個開放源代碼成果,主要用于科學應用。在本文中,我已經介紹了作為創(chuàng)建 2D 圖形工具的 JSci 的一些可能用途。JSci 是專業(yè)繪圖包的備用的免費軟件。雖然 JSci 并不提供專業(yè)畫圖包所帶的內建類型支持,您卻的確可以訪問源代碼,并且可以自由修改代碼,然后將它們提交回 JSci 社區(qū)。JSci 是一個不斷發(fā)展的成果,我認為它是一個很好的備用方案,您在用 Java 創(chuàng)建 2D 圖形時可以考慮使用它。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。