为什么 JTextArea 和 TextLayout 的换行方式不同?
我们有一个应用程序,可以绘制文本,然后显示一个 JTextArea,供用户在单击文本时编辑文本。但是,这两个文本处理组件之间的换行有所不同。它们使用相同的宽度、文本字符串和字体。
对于文本绘制,我使用的是 Java 教程中的 ,我也看到其他人在此处和其他论坛的相关问题中使用了该教程。这是代码的一部分:
FontRenderContext frc = g2d.getFontRenderContext();
TextLayout layout;
AttributedString attrString = new AttributedString(myText);
AttributedCharacterIterator charIterator;
int paragraphStart;
int paragraphEnd;
LineBreakMeasurer lineMeasurer;
float breakWidth;
float drawPosX;
float drawPosY;
attrString.addAttribute(TextAttribute.FONT, myFont);
charIterator = attrString.getIterator();
paragraphStart = charIterator.getBeginIndex();
paragraphEnd = charIterator.getEndIndex();
lineMeasurer = new LineBreakMeasurer(charIterator, frc);
// Set break width to width of Component.
breakWidth = myTextWidth;
drawPosY = startY
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
textBounds = new Rectangle(startX, startY(), 0, 0);
// Get lines from until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
drawPosX = layout.isLeftToRight()
? startX() : breakWidth - layout.getAdvance();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2d, drawPosX, drawPosY);
lineBounds = new Rectangle2D.Float(drawPosX, drawPosY - layout.getAscent(), layout.getAdvance(), (layout.getAscent() + layout.getDescent() + layout.getLeading()));
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getAscent() + layout.getDescent() + layout.getLeading();
}
JTextArea 更简单:
JTextArea textArea = new JTextArea(myText);
textArea.setSize(myTextWidth, myTextThing.getHeight());
textArea.setOpaque(true);
textArea.setVisible(true);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setFont(myFont);
textArea.setBorder(null);
我将边框设置为空,因为我在文本区域的边界之外绘制了另一个矩形,并用虚线区域显示了它所在的位置。现在看起来可能很愚蠢,但当用户第一次选择他们想要编辑的文本时,我们用它来显示文本区域的边界。此时,JTextArea 尚未创建。他们必须再次单击它才能开始编辑。原因是,一旦选择了文本区域,他们还可以拖动文本区域并调整其大小,如果在开始拖动和调整大小时有一个活动的 JTextArea,那么情况会变得混乱且更加混乱。
单独地,绘制的 TextLayouts 和 JTextArea 似乎都可以很好地换行。但当一起使用时,您可以看到差异。这样做的问题是,当用户编辑文本时,JTextArea 正在做它的事情来换行文本。但是当用户 JTextArea 失去焦点时,它会转换为绘制的文本,然后单词可能会以不同的方式换行。
We have an app that draws text, but then displays a JTextArea for the user to edit the text when they click on the text. However, the wrapping between these two text-handling components differs. They use the same width, text String, and Font.
For the text-drawing, I'm using the from the Java tutorial, which I've also seen used by others in related questions here and other forums. Here's that part of the code:
FontRenderContext frc = g2d.getFontRenderContext();
TextLayout layout;
AttributedString attrString = new AttributedString(myText);
AttributedCharacterIterator charIterator;
int paragraphStart;
int paragraphEnd;
LineBreakMeasurer lineMeasurer;
float breakWidth;
float drawPosX;
float drawPosY;
attrString.addAttribute(TextAttribute.FONT, myFont);
charIterator = attrString.getIterator();
paragraphStart = charIterator.getBeginIndex();
paragraphEnd = charIterator.getEndIndex();
lineMeasurer = new LineBreakMeasurer(charIterator, frc);
// Set break width to width of Component.
breakWidth = myTextWidth;
drawPosY = startY
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
textBounds = new Rectangle(startX, startY(), 0, 0);
// Get lines from until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
drawPosX = layout.isLeftToRight()
? startX() : breakWidth - layout.getAdvance();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2d, drawPosX, drawPosY);
lineBounds = new Rectangle2D.Float(drawPosX, drawPosY - layout.getAscent(), layout.getAdvance(), (layout.getAscent() + layout.getDescent() + layout.getLeading()));
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getAscent() + layout.getDescent() + layout.getLeading();
}
The JTextArea is much simpler:
JTextArea textArea = new JTextArea(myText);
textArea.setSize(myTextWidth, myTextThing.getHeight());
textArea.setOpaque(true);
textArea.setVisible(true);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setFont(myFont);
textArea.setBorder(null);
I set the border to null because I have another rectangle drawn outside the bounds of the text area with a dashed area to show where it is. Might seem silly now, but we use it to show the bounds of the text area when the user first selects the text they want to edit. At that point, the JTextArea isn't yet created. They have to click on it again to begin editing. The reason for this is that once a text area is selected, they may also drag and resize the text area, and that gets messy and more confusing if they had a live JTextArea when they started dragging and resizing.
Separately, both the drawn TextLayouts and the JTextArea appear to wrap words just fine. but when used together you can see the difference. The problem with this is that while the user is editing the text, the JTextArea is doing its thing to wrap the text. But when the user JTextArea loses focus, it is converted to the drawn text, and then the words may be wrapped differently.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
使用
i
或l
字符填充文本区域。拿起 UI 标尺或放大镜,从最长行的最左边字符到最右边计算文本区域的大小以像素为单位。对n
、m
和其他一些字符执行相同的操作,以获得更多数据点。我怀疑文本区域有一个不可见的边框,即使设置为无边框,它也会使用几个像素。如果是这种情况,请在 TextLayout 组件周围添加相同的边框,它们应该看起来相同。(除了计算像素之外,您还可以为文本或组件设置背景颜色,但我不一定相信它。)
Fill the text area with
i
orl
characters. Grab a UI ruler or magnifying glass and count the size of your text area in pixels from the leftmost character of the longest line to the rightmost. Do the same withn
,m
, and a few other characters for a few more data points. I suspect that the text area has an invisible border of a few pixels it uses even when set to no border. If this is the case, add the same border around the TextLayout component and they should appear identical.(Alternatively to counting pixels, you could set a background color for the text or the components, but I wouldn't necessarily trust it.)