Posted on 2013-07-19 15:01
TWaver 阅读(1894)
评论(2) 编辑 收藏
Network 上面的Node,默认情况是Label的折行只能通过HTML的<br>标签,这样做的弊端就是不能动态 根据文字的长度智能折行。 如果需要达到这个要求,需要定制Node的LabelAttachment。
最初的想法是 创建一个WrapLabelAttachment,继承自LabelAttachment,然后通过在一个JTextArea,设置文字,setLineWrap(true),指定JTextArea的宽度,然后把JTextArea 的
内容绘制这个Attachment;在实现的过程中,发现JTextArea 有一个bug,就是折行的计算不准确,总是在右边留下很大的空白; 如下图:
测试代码:
1 package demo.text;
2
3 import java.awt.*;
4 import java.awt.font.*;
5 import java.text.*;
6 import javax.swing.*;
7 import twaver.network.ui.*;
8
9 public class WrapLabelAttachment extends LabelAttachment {
10
11 // private final MyTextArea label = new MyTextArea();
12 private final JTextArea label = new JTextArea();
13 protected int maxLength;
14 private LabelBorder lbBorder = null;
15 public WrapLabelAttachment(ElementUI ui) {
16 super(ui);
17 this.updateLabel();
18 }
19
20 private void updateLabel() {
21 label.setOpaque(false);
22 label.setAutoscrolls(false);
23 label.setText(this.getLabelContent());
24 label.setFont(font);
25 label.setBorder(null);
26 label.setLineWrap(true);
27 if(this.border){
28 if(lbBorder == null){
29 lbBorder = new LabelBorder(1);
30 }
31 label.setBorder(lbBorder);
32 }
33 }
34
35 protected String getLabelContent() {
36 Object content = element.getName();
37 if (content == null) {
38 return null;
39 } else {
40 return content.toString();
41 }
42 }
43
44 protected void update() {
45 this.updateLabel();
46 }
47
48 public void paintName(Graphics2D g2d, String elementLabel) {
49 Rectangle bounds = getBounds();
50 // MyTextArea renderer = label;
51 JTextArea renderer = label;
52 String text = getLabelContent();
53 if (highlightable && element.isSelected() && network.isSelectedStatePaintable(element)) {
54 renderer.setOpaque(true);
55 renderer.setBackground(this.highlightBackground);
56 renderer.setForeground(this.highlightForeground);
57 } else {
58 if (this.background == null) {
59 renderer.setOpaque(false);
60 } else {
61 renderer.setOpaque(true);
62 renderer.setBackground(this.background);
63 }
64 renderer.setForeground(this.color);
65 }
66 if(this.border){
67 // draw border
68 lbBorder.setBorderVisible(this.border);
69 lbBorder.setBorderStroke(this.borderStroke);
70 lbBorder.setLineColor(this.borderColor);
71
72 // draw outline
73 lbBorder.setUnderlineVisible(this.underline);
74 lbBorder.setUnderlineStroke(this.underlineStroke);
75 lbBorder.setUnderlineColor(this.underlineColor);
76 }
77
78 if (text != null) {
79 renderer.setBounds(0, 0, bounds.width, bounds.height);
80 g2d.translate(bounds.x, bounds.y);
81 renderer.paint(g2d);
82 g2d.translate(-bounds.x, -bounds.y);
83 }
84 }
85
86 public class MyTextArea extends JComponent {
87 private String t = "";
88 public void setText(String t){
89 this.t = t;
90 }
91
92 public String getText(){
93 return t;
94 }
95
96 public void paint(Graphics g) {
97 super.paint(g);
98 if(this.isOpaque()){
99 g.setColor(this.getBackground());
100 g.fillRect(0, 0, this.getWidth(), this.getHeight());
101 g.setColor(highlightForeground);
102 }
103 Graphics2D g2d = (Graphics2D) g;
104 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
105 g2d.setFont(font);
106 AttributedString messageAS = new AttributedString(getText());
107 messageAS.addAttribute(TextAttribute.FONT,WrapLabelAttachment.this.getFont());
108 AttributedCharacterIterator messageIterator = messageAS.getIterator();
109 FontRenderContext messageFRC = g2d.getFontRenderContext();
110 LineBreakMeasurer messageLBM = new LineBreakMeasurer(messageIterator,
111 messageFRC);
112
113 Insets insets = getInsets();
114 float wrappingWidth = getSize().width - insets.left - insets.right;
115 float x = insets.left;
116 float y = insets.top;
117
118 while (messageLBM.getPosition() < messageIterator.getEndIndex()) {
119 TextLayout textLayout = messageLBM.nextLayout(wrappingWidth);
120 y += textLayout.getAscent();
121 textLayout.draw(g2d, x, y);
122 y += textLayout.getDescent() + textLayout.getLeading();
123 x = insets.left;
124 }
125 }
126 }
127
128 public Rectangle getBounds() {
129 updateLabel();
130 Rectangle rect = super.getBounds();
131 if (element.getClientProperty("Label_Max_Width") != null) {
132 this.maxLength = Integer.valueOf(element.getClientProperty("Label_Max_Width").toString());
133 if (this.maxLength > 0 && this.maxLength < rect.width) {
134 rect.x += (rect.width - this.maxLength) / 2;
135 rect.height = rect.height * rect.width / this.maxLength + 1;
136 rect.width = this.maxLength;
137 }
138 }
139 return rect;
140 }
141 }
最后通过自己定制一个MyTextArea来实现,在MyTextArea中,用LineBreakMeasurer来计算动态折行:
代码:
1 public class MyTextArea extends JComponent {
2 private String t = "";
3 public void setText(String t){
4 this.t = t;
5 }
6
7 public String getText(){
8 return t;
9 }
10
11 public void paint(Graphics g) {
12 super.paint(g);
13 if(this.isOpaque()){
14 g.setColor(this.getBackground());
15 g.fillRect(0, 0, this.getWidth(), this.getHeight());
16 g.setColor(highlightForeground);
17 }
18 Graphics2D g2d = (Graphics2D) g;
19 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
20 g2d.setFont(font);
21 AttributedString messageAS = new AttributedString(getText());
22 messageAS.addAttribute(TextAttribute.FONT,WrapLabelAttachment.this.getFont());
23 AttributedCharacterIterator messageIterator = messageAS.getIterator();
24 FontRenderContext messageFRC = g2d.getFontRenderContext();
25 LineBreakMeasurer messageLBM = new LineBreakMeasurer(messageIterator,
26 messageFRC);
27
28 Insets insets = getInsets();
29 float wrappingWidth = getSize().width - insets.left - insets.right;
30 float x = insets.left;
31 float y = insets.top;
32
33 while (messageLBM.getPosition() < messageIterator.getEndIndex()) {
34 TextLayout textLayout = messageLBM.nextLayout(wrappingWidth);
35 y += textLayout.getAscent();
36 textLayout.draw(g2d, x, y);
37 y += textLayout.getDescent() + textLayout.getLeading();
38 x = insets.left;
39 }
40 }
41 }
然后重写WrapLabelAttachment的 paintName 方法 和getBounds 方法:
代码:
1 public void paintName(Graphics2D g2d, String elementLabel) {
2 Rectangle bounds = getBounds();
3 MyTextArea renderer = label;
4 // JTextArea renderer = label;
5 String text = getLabelContent();
6 if (highlightable && element.isSelected() && network.isSelectedStatePaintable(element)) {
7 renderer.setOpaque(true);
8 renderer.setBackground(this.highlightBackground);
9 renderer.setForeground(this.highlightForeground);
10 } else {
11 if (this.background == null) {
12 renderer.setOpaque(false);
13 } else {
14 renderer.setOpaque(true);
15 renderer.setBackground(this.background);
16 }
17 renderer.setForeground(this.color);
18 }
19 if(this.border){
20 // draw border
21 lbBorder.setBorderVisible(this.border);
22 lbBorder.setBorderStroke(this.borderStroke);
23 lbBorder.setLineColor(this.borderColor);
24
25 // draw outline
26 lbBorder.setUnderlineVisible(this.underline);
27 lbBorder.setUnderlineStroke(this.underlineStroke);
28 lbBorder.setUnderlineColor(this.underlineColor);
29 }
30
31 if (text != null) {
32 renderer.setBounds(0, 0, bounds.width, bounds.height);
33 g2d.translate(bounds.x, bounds.y);
34 renderer.paint(g2d);
35 g2d.translate(-bounds.x, -bounds.y);
36 }
37 }
38
代码:
1 public Rectangle getBounds() {
2 updateLabel();
3 Rectangle rect = super.getBounds();
4 if (element.getClientProperty("Label_Max_Width") != null) {
5 this.maxLength = Integer.valueOf(element.getClientProperty("Label_Max_Width").toString());
6 if (this.maxLength > 0 && this.maxLength < rect.width) {
7 rect.x += (rect.width - this.maxLength) / 2;
8 rect.height = rect.height * rect.width / this.maxLength + 1;
9 rect.width = this.maxLength;
10 }
11 }
12 return rect;
13 }
14
getBounds方法需要动态计算rect 的宽高。
最终效果图:
全部代码见附件:
WrapLabelDemo