構(gòu)建可擴(kuò)展的Java圖表組件_第1頁
構(gòu)建可擴(kuò)展的Java圖表組件_第2頁
構(gòu)建可擴(kuò)展的Java圖表組件_第3頁
構(gòu)建可擴(kuò)展的Java圖表組件_第4頁
構(gòu)建可擴(kuò)展的Java圖表組件_第5頁
已閱讀5頁,還剩12頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、構(gòu)建可擴(kuò)展的Java圖表組件前言Java語言所具有的面向?qū)ο筇匦?,使許多復(fù)雜的問題可以分解成相對(duì)獨(dú)立的對(duì)象來處理。本文用面向?qū)ο蟮姆椒?,將一個(gè)圖表組件從分解到如何組合,以及如何進(jìn)行擴(kuò)展作了詳細(xì)的講解。從簡(jiǎn)單的折線圖到稍復(fù)雜的多種形狀組合的圖表,讀者可以學(xué)到構(gòu)建一個(gè)可擴(kuò)展的圖表組件是多么的容易。常見的圖表類型圖表具有很直觀的視覺效果,可以方便的用來比較數(shù)據(jù)的差異、圖案和趨勢(shì)等。從外觀上來看,常用到的圖表主要有散點(diǎn)圖、(折)曲線圖、柱狀圖等。本文主要討論這幾種圖形樣式。其中這每種圖又可以與其它的類型組合產(chǎn)生更多的形式。下面以圖例來說明:先來看散點(diǎn)圖:圖1-1圖1-1是一個(gè)典型的散點(diǎn)圖,它是由一組X

2、值和一組Y值在二維坐標(biāo)中兩兩成對(duì)描繪而成。一般這種圖形反映兩組數(shù)據(jù)的相關(guān)性。例如,要考查鋼的硬度與淬火溫度的關(guān)系,假設(shè)上圖的橫軸表示淬火的溫度,縱軸表示同時(shí)測(cè)出的鋼的硬度,這時(shí)我們可從上圖看出一個(gè)趨勢(shì),即淬火的溫度越高,鋼的硬度越大。 再來看一個(gè)折線圖:圖1-2圖1-3在圖1-2的折線圖中,假設(shè)橫軸表示周一到周日,縱軸表示某商場(chǎng)的日銷售額。我們可以看出其臨近周末的銷售額呈急劇上升趨勢(shì),到周日開始回落,而最慘淡的是周四。通常折線圖也可以表示成柱狀圖的形式,如圖1-3。復(fù)雜一點(diǎn)的圖形圖1-4圖1-5圖1-6 上圖三個(gè)圖形的數(shù)據(jù)都是同樣的,但它們所能夠直觀表達(dá)的意思又不盡相同。諸如此類的圖表,形式多

3、種多樣,但它們都是由這幾種基本圖表組合而成的。接下來的一節(jié),我們來看一下組成圖表的基本元素有哪些。圖表的主要元素圖表的組成從前面的例子中我們可以看出,每種圖表都是由橫坐標(biāo)軸,縱坐標(biāo)軸,還有不同的繪圖形狀組成。為了更容易理解,大家看一下下面的分解圖: 上圖2-1 下圖2-2是一個(gè)柱狀圖和折線圖的組合圖表,我們將它分解之后(圖2-2),可以清晰的看到,它是由圖表區(qū)、坐標(biāo)軸、網(wǎng)格線、圖表形狀等組成:圖表區(qū)(Chart):包含所有其它的圖表元素。 坐標(biāo)軸(Axis):提供繪圖形狀的坐標(biāo)參考。一個(gè)圖表中通常有一個(gè)垂直和一個(gè)水平坐標(biāo)軸。而網(wǎng)格線是以坐標(biāo)軸的刻度為參考,貫穿整個(gè)繪圖區(qū)。網(wǎng)格線同坐標(biāo)軸一樣也可

4、分為水平和垂直網(wǎng)格線。 圖表形狀(Plot):也是以坐標(biāo)軸為參考,按一定的比例將數(shù)據(jù)按相應(yīng)形狀繪制出來。所以,從根本上來說,一個(gè)圖表的是由三種基本的可視元素組成的:圖表區(qū),坐標(biāo)軸,圖表形狀。實(shí)現(xiàn)基本圖表元素基本圖表元素的特征我們已經(jīng)知道了圖表的主要組成元素,現(xiàn)在再來看看這些元素有哪些特征。還是來看一個(gè)圖:圖2-3從圖上我們可以看出,一個(gè)位于屏幕坐標(biāo)系中的圖表具有寬度(Wc)和高度(Hc)以及坐標(biāo)位置(x,y)。圖表中的坐標(biāo)軸也有高度Ha、寬度Wa及坐標(biāo)位置(x,y)。同樣,圖表形狀也有相應(yīng)的高度Hp和寬度Wp和坐標(biāo)位置。一個(gè)圖表通常擁有一個(gè)橫坐標(biāo)軸和縱坐標(biāo)軸。所有的繪圖數(shù)據(jù)的坐標(biāo)都要轉(zhuǎn)化成適當(dāng)

5、的屏幕坐標(biāo),于是我們需要一個(gè)新的元素:比例尺。比例尺應(yīng)負(fù)責(zé)完成實(shí)際坐標(biāo)值到屏幕坐標(biāo)值以及屏幕坐標(biāo)值到實(shí)際坐標(biāo)值的相互轉(zhuǎn)化。而坐標(biāo)軸是用來描繪刻度用的,它應(yīng)與比例尺成對(duì)使用。一個(gè)圖表還可以有多個(gè)圖表形狀(如圖1-6和圖2-1),并且我們可以往圖表里面增加或移除形狀。一個(gè)圖表形狀應(yīng)可以表示至少一組以上的數(shù)據(jù)(如圖1-5)。由于圖表形狀要在圖表上描繪數(shù)據(jù),它需要有一個(gè)東西來記錄數(shù)據(jù),我們將它稱之為數(shù)據(jù)序列。 基本圖表元素的設(shè)計(jì)實(shí)現(xiàn) 我們的目標(biāo)是用程序來實(shí)現(xiàn)一個(gè)圖表。前面的討論我們已經(jīng)知道構(gòu)成圖表的基本的元素和它們的特性了。由此我們可以為這幾個(gè)圖表元素設(shè)計(jì)幾個(gè)接口類。在設(shè)計(jì)之前,要首先說明一下,我們不

6、打算實(shí)現(xiàn)類似于商業(yè)化圖表組件的強(qiáng)大交互功能,我們所有的設(shè)計(jì),只是為了能闡明問題。圖表元素接口(ChartWidget)因?yàn)樗械膱D表可視元素都有一些共同的屬性:位置,寬度和高度,它們還要負(fù)責(zé)繪制自己本身。所以我們?cè)O(shè)計(jì)一個(gè)ChartWidget接口,其它所有可視元素都要繼承于這個(gè)接口。這個(gè)接口的類圖如圖2-4:圖2-4由這個(gè)類圖,我們可以很容易的寫出它的代碼:public interface ChartWidget public int getX(); public int getY(); public int getWidth(); public int getHeight(); public

7、 void draw(Graphics g); 坐標(biāo)軸(Axis) 接下來的一個(gè)類是坐標(biāo)軸Axis。坐標(biāo)軸主要任務(wù)是繪制軸及其刻度(Tick)和刻度值,因?yàn)樗L制時(shí)是按一定的比例繪制的,所以它需要有一個(gè)比例尺將實(shí)際坐標(biāo)值轉(zhuǎn)換值成屏幕坐標(biāo)值。這就引出了Scale這個(gè)類。Scale類主要完成實(shí)際坐標(biāo)值到屏幕坐標(biāo)值以及屏幕坐標(biāo)值到實(shí)際坐標(biāo)值的相互轉(zhuǎn)化。由此,Axis與Scale是一對(duì)相互依賴的類。從設(shè)計(jì)模式的角度來看,Axis是視圖(View),負(fù)責(zé)界面繪制,Scale就是它的模型(Model),負(fù)責(zé)提供相應(yīng)的數(shù)據(jù)。它們的類圖見圖2-5: 圖2-5下面來分別看看Axis類與Scale類的代碼: pu

8、blic abstract class Axis implements ChartWidget protected Scale scale; protected int x; protected int y; protected int width; protected int height; protected Axis peerAxis; protected boolean drawGrid; protected Color gridColor; protected Color axisColor; protected int tickLength; protected int tickC

9、ount; public Axis() gridColor = Color.LIGHT_GRAY; axisColor = Color.BLACK; tickLength = 5; drawGrid = false; public int getTickCount() return tickCount; public void setTickCount(int tickCount)this.tickCount=tickCount; public Scale getScale() return scale; public void setScale(Scale scale) this.scale

10、 = scale; public int getX() return x; public void setX(int x)this.x = x; public int getY() return y; public void setY(int y)this.y = y; public int getHeight() return height; public void setHeight(int height)this.height = height; public int getWidth() return width; public void setWidth(int width)this

11、.width = width; public boolean isDrawGrid()return drawGrid; public void setDrawGrid(boolean drawGrid)this.drawGrid=drawGrid; 軟件開發(fā)網(wǎng) public Color getAxisColor()return axisColor; public void setAxisColor(Color axisColor) this.axisColor=axisColor; public Color getGridColor()return gridColor; public void

12、 setGridColor(Color gridColor)this.gridColor=gridColor; public int getTickLength()return tickLength; public void setTickLength(int tickLength)this.tickLength=tickLength; public Axis getPeerAxis()return peerAxis; public void setPeerAxis(Axis peerAxis)this.peerAxis = peerAxis;protected abstract int ca

13、lculateTickLabelSize(Graphics g);public abstract class Scale protected double min; protected double max; protected int screenMin; protected int screenMax; public abstract int getScreenCoordinate(double value); public double getActualValue(int value) double vrange = max - min; if(min < 0.0 &&a

14、mp; max < 0.0) vrange = (min - max) * -1.0; double i = screenMax - screenMin; i = (double)(value - screenMin) * vrange) / i; i = min; return i; public void setMax(double max)this.max = max; public void setMin(double min)this.min = min; public double getMax()return max; public double getMin()retur

15、n min; public int getScreenMax()return screenMax; public int getScreenMin()return screenMin; public void setScreenMax(int screenMax)this.screenMax =screenMax; public void setScreenMin(int screenMin)this.screenMin = screenMin;在上面的Axis類代碼中,我們?cè)谠械腃hartWidget接口的基礎(chǔ)上,為Axis添加了幾個(gè)其它的屬性:軸線的顏色axisColor,網(wǎng)格線的顏色g

16、ridColor及網(wǎng)格線的可見屬性drawGrid。還有刻度線的長(zhǎng)度和個(gè)數(shù)tickLength和tickCount。而peerAxis屬性是參考坐標(biāo)軸,在繪制坐標(biāo)軸時(shí)的會(huì)用到。 Scale類也是抽象的,因?yàn)闄M軸和縱軸的屏幕坐標(biāo)的轉(zhuǎn)換方式不一樣,所以getScreenCoordinate()方法留待子類來實(shí)現(xiàn)它。圖表形狀(Plot)組成圖表還有一個(gè)最重要的類,負(fù)責(zé)描述數(shù)據(jù)的圖表形狀,我們稱之為Plot。Plot應(yīng)能繪制多組數(shù)據(jù),而這組數(shù)據(jù)呢,我們專門用一個(gè)模型來描述它,這就是DataSeries。由于我們?cè)谶@里討論的是二維圖表,所以DataSeries應(yīng)能提供兩組分別代表X和Y坐標(biāo)的數(shù)據(jù)。還是來

17、看看它們的類圖(圖2-6): 圖2-6為了plot能繪制多組數(shù)據(jù),除了從ChartWidget繼承來的draw(Graphics)方法外,plot還提供了draw(Graphics,DataSeries,int)方法,用來繪制單組的數(shù)據(jù)。下面的代碼更能說明問題: public abstract class Plot implements ChartWidgetprotected int x; protected int y; protected int width; protected int height; protected XAxis xAxis; protected YAxis yAx

18、is; protected ArrayList dataSeries;public int getX()return x; public int getY()return y; public int getWidth()return width; public int getHeight()return height; public void addDataSeries(DataSeries ds)dataSeries.add(ds); public void removeDataSeries(DataSeries ds)dataSeries.remove(ds); public void d

19、raw(Graphics g) for( int i=0;i<dataSeries.size();i )draw(g,(DataSeries)dataSeries.get(i),i); public abstract void draw(Graphics g,DataSeries ds,int index); Plot類也被設(shè)計(jì)成了抽象類,具體的繪制方法由子類為實(shí)現(xiàn)。而DataSeries類的過于簡(jiǎn)單,在此我們就不列出代碼了。圖表(Chart)最后就是將上面的元素合成一個(gè)完整的圖表,即Chart類。一個(gè)Chart有一個(gè)橫軸和一個(gè)縱軸以及至少一個(gè)Plot,并且可以為它添加多個(gè)Plot。我們

20、最后來看一下整個(gè)Chart及其相關(guān)類的UML關(guān)系圖:圖2-7由于篇幅有限,在此就不列出Chart類的代碼了。 完成一個(gè)折線圖由于前面介紹的只是一些接口或抽象類,要完成一個(gè)圖表組件,還必須實(shí)現(xiàn)它們,下面我們以一個(gè)折線圖為例,來完成一個(gè)完整的折線圖。 實(shí)現(xiàn)x軸和y軸其實(shí)前面的Axis抽象類已經(jīng)完成一個(gè)大部分的操作,余下的就是分別完成x軸和y軸的繪制了。在這里我們就不打算列出完整的類代碼,只列出關(guān)鍵的實(shí)現(xiàn)部分。Public class XAxis extends Axis public void draw(Graphics g) if ( ! (scale instanceof XScale) )

21、return; int ticks = getTickCount(); int tickDist = (int) (double)(scale.getScreenMax()-scale.getScreenMin()/(double)(ticks 1); int tickX = scale.getScreenMin(); int tickY = peerAxis.getScale().getScreenMin(); int gridLength = peerAxis.getScale().getScreenMax(); int axisLength = scale.getScreenMax()-

22、scale.getScreenMin(); /*設(shè)置軸線顏色*/ g.setColor(axisColor); /*繪制橫軸*/ g.drawLine(tickX, tickY, tickX axisLength,tickY); for ( int i = 0 ; i < ticks; i ) 軟件開發(fā)網(wǎng) tickX = scale.getScreenMin() tickDist*(i 1); if ( isDrawGrid() ) /*如果drawGrid屬性為true,用gridColor繪制網(wǎng)格線*/ g.setColor(gridColor); g.drawLine(tickX,

23、 tickY , tickX, gridLength );/*繪制刻度線*/ g.setColor(axisColor); g.drawLine(tickX, tickY , tickX, tickY tickLength); int tickLabelWidth = g.getFontMetrics().stringWidth(String.valueOf(i 1); int tickLabelHeight = g.getFontMetrics().getHeight(); g.drawString(String.valueOf(i 1), tickX-(tickLabelWidth/2),

24、 tickY tickLabelHeight); public class YAxis extends Axis public void draw(Graphics g) if ( ! (scale instanceof YScale) ) return; int ticks = getTickCount(); int tickDist = (int) Math.abs(double)(scale.getScreenMax() - scale.getScreenMin()/(double)(ticks 1); int tickY = scale.getScreenMin(); int tick

25、X = peerAxis.getScale().getScreenMin(); int gridLength = peerAxis.getScale().getScreenMax(); int axisLength = scale.getScreenMax(); /*繪制縱坐標(biāo)軸*/ g.setColor(axisColor); g.drawLine(tickX, tickY, tickX, axisLength); for ( int i = 0 ; i < ticks; i ) tickY = scale.getScreenMin()-tickDist*(i 1); if ( isD

26、rawGrid() ) /*如果drawGrid屬性為true,用gridColor繪制網(wǎng)格線*/ g.setColor(gridColor); g.drawLine(tickX, tickY , gridLength, tickY );/*繪制刻度線*/ g.setColor(axisColor); g.drawLine(tickX, tickY , tickX-tickLength, tickY); int tickLabelWidth = g.getFontMetrics().stringWidth(String.valueOf(i 1); g.drawString(String.val

27、ueOf(i 1), tickX-tickLength-tickLabelWidth, tickY); 實(shí)現(xiàn)畫折線的LinePlot由于Plot是由DataSeries為它提供繪圖數(shù)據(jù)的,在實(shí)現(xiàn)LinePlot之前,先來實(shí)現(xiàn)一個(gè)DefaultDataSeries類: public class DefaultDataSeries extends DataSeries public DefaultDataSeries(Object yData) throws InvalidDataException super(); if ( yData = null | !(yData0 instanceof

28、Double) ) throw new InvalidDataException(); for ( int i = 0;i<yData.length;i ) /*將y值添加到序列中*/this.yData.add(yDatai);/*根據(jù)y值的個(gè)數(shù),從1開始自動(dòng)添加相應(yīng)數(shù)量的x值*/ this.xData.add(new Double(i 1); 這個(gè)DefaultDataSeries提供了一個(gè)構(gòu)造方法,使用者只需提供一組y坐標(biāo)值,即可構(gòu)造一個(gè)DataSeries了。下面是很重要的部分了。我們來看看實(shí)現(xiàn)一個(gè)畫折線的LinePlot是多么的簡(jiǎn)單:Public class LinePlot

29、extends Plot public void draw(Graphics g, DataSeries ds, int index) if ( ds = null ) return; g.setColor(lineColor); double x = new doubleds.size(); double y = new doubleds.size(); int xPoints = new intds.size(); int yPoints = new intds.size(); for ( int i = 0; i< ds.size(); i ) xi = (Double)ds.ge

30、tXData(i).doubleValue();yi = (Double)ds.getYData(i).doubleValue();/*將ds中的實(shí)際值轉(zhuǎn)換成屏幕坐標(biāo)值*/ xPointsi = xAxis.getScale().getScreenCoordinate(xi); yPointsi = yAxis.getScale().getScreenCoordinate(yi); /*繪制折線*/ g.drawPolyline(xPoints, yPoints, xPoints.length); 上面可出了LinePlot中繪制折線的代碼,我們看到,繪制一個(gè)折線是多么的輕松和簡(jiǎn)單。 完成折線

31、圖通過前面的實(shí)現(xiàn)代碼,我們來看一個(gè)完整的折線圖示例:double y = new double 12.5,14.1,13.2,11.4,13.25,12.32 ; try DataSeries ds = new DefaultDataSeries(Primary2ObjectUtil.Doulbe2Object(y); XAxis xaxis = new XAxis(new XScale(0,y.length 1),ds.size(); YAxis yaxis = new YAxis(new YScale(10,15),4); xaxis.setDrawGrid(true); yaxis.se

32、tDrawGrid(true); LinePlot plot = new LinePlot(ds,xaxis,yaxis); Chart chart = new Chart(xaxis,yaxis,plot); JFrame frame = new JFrame("Line Plot Demo"); frame.setSize(400,300); frame.getContentPane().add(chart); frame.setVisible(true); catch (InvalidDataException e) e.printStackTrace(); 下面是這

33、個(gè)程序運(yùn)行起來的屏幕截圖: (單組數(shù)據(jù)的折線圖) (有多組數(shù)據(jù)的折線圖) 擴(kuò)展其它類型的圖表通過前面的例子,我們知道要實(shí)現(xiàn)特定類型的圖表,只要實(shí)現(xiàn)特定的Plot類就可以了。如果數(shù)據(jù)有特殊格式,只需再擴(kuò)展一個(gè)DataSeries就可以了。為使大家加深理解,我們?cè)僖砸粋€(gè)柱狀圖為例子作講解。在第一節(jié)的圖1-2和圖1-3中,我們知道,一組數(shù)據(jù)除了用折線圖表示之外,還可以表示成柱狀圖的形式。在這里我們就借用折線圖的數(shù)據(jù),來實(shí)現(xiàn)一個(gè)BarPlot。下面列出了BarPlot的關(guān)鍵代碼:public class BarPlot extends Plot public void draw(Graphics g,

34、 DataSeries ds, int index) if ( ds = null ) return; /*每組柱子的個(gè)數(shù)*/ int bars = this.dataSeries.size(); /*出每個(gè)柱子應(yīng)有的寬度*/ int barWidth = (int) (double)xAxis.width/(double)ds.size() 1)/bars-barSpace); if ( barWidth <=0 ) barWidth = 1; int barx,bary,barw,barh; int barGroupWidth = barWidth*bars; double ymin

35、 = yAxis.getScale().getMin(); for ( int i = 0;i<ds.size(); i ) barx = (int)(xAxis.getScale().getScreenCoordinate(i 1) - barGroupWidth/2.0d) index*barWidth; double val = (Double)ds.getYData(i).doubleValue(); bary = yAxis.getScale().getScreenCoordinate(val); if ( ymin<0) if ( val<0 ) barh = b

36、ary-yAxis.getScale().getScreenCoordinate(0); bary = bary-barh; else barh = yAxis.getScale().getScreenCoordinate(0)-bary; else barh = yAxis.getScale().getScreenCoordinate(ymin)-bary; barw = barWidth; g.setColor(barColor); g.fillRect(barx,bary,barw, barh); g.setColor(Color.BLACK); g.drawRect(barx,bary

37、, barw, barh); BarPlot的實(shí)現(xiàn)比LinePlot稍微復(fù)雜一點(diǎn)。主要是要計(jì)算每個(gè)柱子的位置,寬度和高度。由于考慮到多組柱子以及柱子的值為負(fù)數(shù)時(shí)坐標(biāo)不同,所以計(jì)算要繁索一點(diǎn)。但總體來說,實(shí)現(xiàn)BarPlot也是相當(dāng)簡(jiǎn)單的。由于柱狀圖運(yùn)行代碼與折線圖類似,這里就不列出演示代碼。下面來看看程序在幾種情況下的運(yùn)行畫面:(單組數(shù)據(jù)的柱狀圖)(多組數(shù)據(jù)的柱狀圖)(有負(fù)值的柱狀圖)現(xiàn)在我們有了畫折線圖的類LinePlot和畫柱狀圖的類BarPlot。我們要生成一個(gè)折線圖與柱狀圖組合起來的例子。還是來看看代碼是如何實(shí)現(xiàn)的: DataSeries ds = new DefaultDataSeri

38、es(Primary2ObjectUtil.Doulbe2Object(y1); XAxis xaxis = new XAxis(new XScale(0,y1.length 1),ds.size(); YAxis yaxis = new YAxis(new YScale(10,15),4); xaxis.setDrawGrid(true); yaxis.setDrawGrid(true); LinePlot linePlot = new LinePlot(ds,xaxis,yaxis);BarPlot barPlot = new BarPlot(ds,xaxis,yaxis);/*先生成Ba

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論