TWaver - 专注UI技术

http://twaver.servasoft.com/
posts - 171, comments - 191, trackbacks - 0, articles - 2
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

UI定制总结

Posted on 2012-08-01 09:52 TWaver 阅读(950) 评论(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&nbsp;att=new&nbsp;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的源代码见原文最下方

只有注册用户登录后才能发表评论。


网站导航: