Posted on 2012-08-01 09:52
TWaver 閱讀(956)
評論(0) 編輯 收藏
TWaver本身提供的豐富的設置選項,可以幫助我們快速實現各種絢麗的效果,但是在某些情況下,我們需要在網元上繪制一些圖形來表示某種狀態或業務信息,沒問題,只需要一點點2D知識可以很容易實現這樣的需求。
假設一種需求(僅僅是假設):監控交換機各個端口的傳輸速度,并用柱狀圖動態顯示。效果圖如下:
大家可能奇怪,怎么放了三個一樣的網元呢?答案是:我使用了三種方式來實現這個效果!可能還有別的方式可以實現,所謂條條大路通羅馬是也!這也是TWaver的強大之處, 為我們留下了一扇門,門后就是神奇的"納尼亞王國",本文的目的就是把這扇門的鑰匙交給你。
我們從最簡單的說起,TWaver提供了BarChart表示柱狀圖,我們可以把BarChart通過ComponentAttachment掛到Node上,這是最簡單的方式,之所以說它簡單,因為我們不需要使用2D繪制( 其實2D也不難),僅僅需要監聽Node的屬性變化然后更新BarChart即可。
ComponentAttachment上允許添加任何JComponent組件(JPanel,JButton,JLabel,JCheckbox,etc.),BarChart是JComponent的子類,自然也可以被添加。ComponentAttachment是個抽象類,需要定制一個PortRateAttachment繼承ComponentAttachment
注冊Attachment
1 TUIManager.registerAttachment("PortRateAttachment",PortRateAttachment.class);
1 node1.addAttachment("PortRateAttachment");
2 node1.putAttachmentPosition(TWaverConst.POSITION_RIGHT);
3 node1.putAttachmentXOffset(-20);
1 public class PortRateAttachment extends ComponentAttachment {
2 Node p1=new Node();
3 Node p2=new Node();
4 Node p3=new Node();
5 private BarChart barChart=new BarChart();
6 public PortRateAttachment(String name, ElementUI ui) {
7 super(name, ui);
8 TDataBox box=new TDataBox();
9 barChart.setDataBox(box);
10 barChart.setShadowOffset(0);//取消陰影
11 barChart.setUpperLimit(100);//刻度最大值
12 barChart.setLowerLimit(0);//刻度最小值
13 barChart.setYScaleLineVisible(false);//Y軸刻度線不可見
14 barChart.setXAxisVisible(false);//X軸不可見
15 barChart.setYAxisVisible(false);//Y軸不可見
16 barChart.setBundleSize(3);//將三個Node放在一塊顯示
17 barChart.setOpaque(false);//背景透明
18 barChart.setXGap(0);//X軸Margin為0
19 barChart.setYGap(0);//Y軸Margin為0
20 barChart.setValueTextVisible(false);//ChartValue不可見
21 barChart.getLegendPane().setVisible(false);//隱藏LegendPane
22
23 //初始化三個Node
24 p1.putChartValue(0);
25 p1.putChartColor(PortRateConst.COLORS[0]);
26 box.addElement(p1);
27
28 p2.putChartValue(0);
29 p2.putChartColor(PortRateConst.COLORS[1]);
30 box.addElement(p2);
31
32 p3.putChartValue(0);
33 p3.putChartColor(PortRateConst.COLORS[2]);
34 box.addElement(p3);
35
36 this.setComponent(barChart);//將BarChart掛載在Attachment上
37 this.setBorderVisible(true);//顯示Border
38 this.setBorderColor(Color.gray);//Border顏色
39 this.setSize(new Dimension(50,50));//設置Attachment大小
40 this.setPosition(this.element.getAttachmentPosition());//設置Attachment的位置,提前存入Node的ClientProperty
41 this.setXOffset(this.element.getAttachmentXOffset());//設置Attachment的X軸偏移量,提前存入Node的ClientProperty
42 }
43
44 /*
45 * 監控Node Property變化并更新到BarChart上
46 * @see twaver.network.ui.ComponentAttachment#elementPropertyChange(java.beans.PropertyChangeEvent)
47 */
48 @Override
49 public void elementPropertyChange(PropertyChangeEvent evt) {
50 super.elementPropertyChange(evt);
51 if(evt.getNewValue()!=null){
52 if("UP:p1".equals(evt.getPropertyName())){
53 p1.putChartValue(Double.parseDouble(evt.getNewValue().toString().substring(3))*100);
54 }else if("UP:p2".equals(evt.getPropertyName())){
55 p2.putChartValue(Double.parseDouble(evt.getNewValue().toString().substring(3))*100);
56 }else if("UP:p3".equals(evt.getPropertyName())){
57 p3.putChartValue(Double.parseDouble(evt.getNewValue().toString().substring(3))*100);
58 }
59 }
60
61 }
62 }
在Attachment中添加一個BarChart,并用三個Node表示chart的三個組,在elementPropertyChange中監控Node屬性變化并同步更新到這三個Node上即可。
所有的代碼已經加上注釋,就不過多解釋了。
TWaver還提供了一種IconAttachment,允許我們將一個Icon作為附件掛載在Node上,我們可以在Icon上畫任何內容,所以這也是一種思路(參考了TWaver官方demo,InstrumentDemo,為避免版權糾紛,特此說明 :-D )。我們定制一個PortRateIconAttachment從IconComponentAttachment繼承。
1 public class PortRateIconAttachment extends IconAttachment {
2 public final static double WIDTH = 40;
3 public final static double HEIGHT = 50;
4
5 public PortRateIconAttachment(String name, ElementUI elementUI) {
6 super(name, elementUI,new PortRateIcon(elementUI.getElement()));
7 }
8
9 }
10 class PortRateIcon implements javax.swing.Icon{
11 private Element element;
12 public PortRateIcon(Element element){
13 this.element=element;
14 }
15 @Override
16 public void paintIcon(Component c, Graphics g, int x, int y) {
17 Graphics2D g2d = (Graphics2D)g;
18 //計算出柱狀圖中組數和組寬
19 final int count = PortRateConst.KEYS.length;
20 final double width = PortRateIconAttachment.WIDTH / count;
21 //計算坐標和尺寸,繪制三個矩形代表三個組
22 for(int i=0; i<count; i++){
23 double proportion=0;
24 if(element.getUserProperty(PortRateConst.KEYS[i])!=null)
25 proportion =Double.parseDouble(element.getUserProperty(PortRateConst.KEYS[i]).toString().substring(3));
26 g2d.setColor(PortRateConst.COLORS[i]);
27 g2d.fillRect((int)(x + i * width),
28 (int)(y + PortRateIconAttachment.HEIGHT * (1- proportion)),
29 (int)width,(int)(PortRateIconAttachment.HEIGHT * proportion));
30 }
31 //繪制邊框
32 g2d.setColor(Color.GRAY);
33 g2d.setStroke(TWaverConst.BASIC_STROKE);
34 g2d.drawRect(x, y-1, (int)PortRateIconAttachment.WIDTH, (int)PortRateIconAttachment.HEIGHT);
35 }
36
37 @Override
38 public int getIconWidth() {
39 return (int) PortRateIconAttachment.WIDTH;
40 }
41
42 @Override
43 public int getIconHeight() {
44 return (int) PortRateIconAttachment.HEIGHT;
45 }
46
47 }
在PortRateIconAttachment中沒有任何繪制,僅僅與一個PortRateIcon綁定,繪制工作交給PortRateIcon執行,每當PortRateIconAttachment監聽到Node屬性變化時就會重繪Icon
在paintIcon方法中,我們最終繪制出了我們需要的動態柱狀圖。
除了Attachment,重寫NodeUI也是一個不錯的選擇。看我們的第三種方法
我們寫了一個SwitchNode繼承自Node,重寫getUIClassID方法,實際上就是為此Node指定了UI類
PortNodeUI
1 public class PortNodeUI extends NodeUI {
2 private Rectangle2D rect=null;
3 public PortNodeUI(TNetwork network, Node node) {
4 super(network, node);
5 }
6 @Override
7 public void paintBody(Graphics2D g2d){
8 super.paintBody(g2d);
9 final int count = PortRateConst.KEYS.length;
10 //計算出柱狀圖中組數和組寬
11 final double width = 40 / count;
12 double x=element.getX()+element.getWidth()-20;
13 double y=element.getY()+20;
14 //繪制組
15 for(int i=0; i<count; i++){
16 double proportion=0;
17 if(element.getUserProperty(PortRateConst.KEYS[i])!=null)
18 proportion =Double.parseDouble(element.getUserProperty(PortRateConst.KEYS[i]).toString().substring(3));
19 g2d.setColor(PortRateConst.COLORS[i]);
20 g2d.fillRect((int)(x + i * width),
21 (int)(y + 50 * (1- proportion)),
22 (int)width, (int)(PortRateIconAttachment.HEIGHT * proportion));
23 }
24 //繪制邊框
25 g2d.setColor(Color.GRAY);
26 g2d.setStroke(TWaverConst.BASIC_STROKE);
27 g2d.drawRect((int)x, (int)y, (int)PortRateIconAttachment.WIDTH, (int)PortRateIconAttachment.HEIGHT);
28 }
29 }
注意paintBody方法,幾乎跟前面的paintIcon是一致的。
注意右側的PropertySheet,我們也為其實現了一個Renderer繪制進度條,實際上,通過Renderer,我們可以在PropertySheet繪制任何圖形或組件。
通過 ElementAttribute指定Renderer
1 ElementAttribute att=new ElementAttribute();
2 att.setRendererClass(PortRateRenderer.class.getName());
Renderer類
1 public class PortRateRenderer extends JComponent implements TableCellRenderer {
2 private Color foreColor;
3 private double proportion;
4 @Override
5 public Component getTableCellRendererComponent(JTable table, Object value,
6 boolean isSelected, boolean hasFocus, int row, int column) {
7 if(value!=null){
8 String strValue=(String)value;
9 for(int i=0; i<PortRateConst.KEYS.length; i++){
10 String key = PortRateConst.KEYS[i];
11 if(strValue.startsWith(key)){
12 proportion = Double.parseDouble(strValue.substring(key.length() + 1));
13 foreColor = PortRateConst.COLORS[i];
14 }
15 }
16 }
17 return this;
18 }
19 public void paintComponent(Graphics g){
20 Graphics2D g2d=(Graphics2D)g;
21 g2d.setColor(new Color(127,92,128));
22 g2d.drawRect(1, 1, this.getWidth()-2, this.getHeight()-2);
23 g2d.setColor(new Color(240,240,240));
24 g2d.fillRect(2, 2, this.getWidth()-3, this.getHeight()-3);
25 g2d.setColor(foreColor);
26 g2d.fillRect(2, 2, (int)(this.getWidth() * proportion), this.getHeight()-3);
27 }
28
29 }
在getTableCellRendererComponent中,我們根據傳進Renderer的Value,設置進度條的值和前景色,在paintComponent中就可以使用了。在paintComponent里,首先畫一個矩形當作進度條背景,然后覆蓋一個矩形當作進度,繪制好邊框以后一個進度條就完成了!
最近論壇上有人提出要實現帶圓角Title的Group,貼子地址:
http://twaver.servasoft.com/forum/viewtopic.php?f=4&t=3091。
帖子里的實現過程是這樣的:Group的Label其實是一個LabelAttachment,為Group重新定制了一個LabelAttachment,在LabelAttachment繪制出圓角效果,然后將Group的LabelPosition設置為左上角即可。
圓角繪制的原理是將一個圓角矩形和普通矩形通過Area組合,然后用漸變色填充,具體的代碼大家可以去帖子里下載。
這個專題終于可以告一段落了,熟悉TWaver架構以后,再加上一些2d知識,我們可以發揮自己的想象力繪制各種令人驚嘆的效果。希望本文能起到拋磚引玉的作用。最后附上文中demo的源代碼
見原文最下方。