问题 如何使用Graphics在多行上输出String


我的程序覆盖 public void paint(Graphics g, int x, int y); 为了画一些使用的蜇伤 g.drawString(someString, x+10, y+30);

现在someString可能很长,因此它可能不适合一行。

在多行上写文本的最佳方法是什么。
 例如,在矩形(x1,y1,x2,y2)中?


4656
2017-10-27 10:42


起源



答案:


感谢Epaga的暗示和网上的几个例子(不是那么明显!我主要使用 为文本布局打破一行),我可以制作一个组件来显示包装文本。它不完整,但至少它显示了预期的效果。

class TextContainer extends JPanel
{
    private int m_width;
    private int m_height;
    private String m_text;
    private AttributedCharacterIterator m_iterator;
    private int m_start;
    private int m_end;

    public TextContainer(String text, int width, int height)
    {
        m_text = text;
        m_width = width;
        m_height = height;

        AttributedString styledText = new AttributedString(text);
        m_iterator = styledText.getIterator();
        m_start = m_iterator.getBeginIndex();
        m_end = m_iterator.getEndIndex();
    }

    public String getText()
    {
        return m_text;
    }

    public Dimension getPreferredSize()
    {
        return new Dimension(m_width, m_height);
    }

    public void paint(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        FontRenderContext frc = g2.getFontRenderContext();

        LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
        measurer.setPosition(m_start);

        float x = 0, y = 0;
        while (measurer.getPosition() < m_end)
        {
            TextLayout layout = measurer.nextLayout(m_width);

            y += layout.getAscent();
            float dx = layout.isLeftToRight() ?
                    0 : m_width - layout.getAdvance();

            layout.draw(g2, x + dx, y);
            y += layout.getDescent() + layout.getLeading();
        }
    }
}

只是为了好玩,我让它适合一个圆圈(唉,没有理由,似乎):

public void paint(Graphics g)
{
    super.paintComponent(g);

    Graphics2D g2 = (Graphics2D) g;
    FontRenderContext frc = g2.getFontRenderContext();

    LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
    measurer.setPosition(m_start);

    float y = 0;
    while (measurer.getPosition() < m_end)
    {
        double ix = Math.sqrt((m_width / 2 - y) * y);
        float x = m_width / 2.0F - (float) ix;
        int width = (int) ix * 2;

        TextLayout layout = measurer.nextLayout(width);

        y += layout.getAscent();
        float dx = layout.isLeftToRight() ?
                0 : width - layout.getAdvance();

        layout.draw(g2, x + dx, y);
        y += layout.getDescent() + layout.getLeading();
    }
}

不过,我对dx计算不太了解。


9
2017-10-27 12:44





java.awt.font.TextLayout中 可能会有所帮助。这是他们的示例代码的片段:

   Graphics2D g = ...;
   Point2D loc = ...;
   Font font = Font.getFont("Helvetica-bold-italic");
   FontRenderContext frc = g.getFontRenderContext();
   TextLayout layout = new TextLayout("This is a string", font, frc);
   layout.draw(g, (float)loc.getX(), (float)loc.getY());

   Rectangle2D bounds = layout.getBounds();
   bounds.setRect(bounds.getX()+loc.getX(),
                  bounds.getY()+loc.getY(),
                  bounds.getWidth(),
                  bounds.getHeight());
   g.draw(bounds);

否则,您总是可以使用Swing文本元素为您完成工作,只需传入您想要绘制的图形。


3
2017-10-27 10:44



我已经拿到了那个。我的问题是,我不希望文本周围有一个矩形,而是一个给定矩形内的文本。 - Burkhard
请注意,如果您传递的字符串为空,TextLayout的构造函数会无证地抛出异常。 - Ricky Clarkson
使用Shape labelShape = textLayout1.getOutline(null);然后是g2d.fill(labelShape);他们应该消除边界 - mk7


答案:


感谢Epaga的暗示和网上的几个例子(不是那么明显!我主要使用 为文本布局打破一行),我可以制作一个组件来显示包装文本。它不完整,但至少它显示了预期的效果。

class TextContainer extends JPanel
{
    private int m_width;
    private int m_height;
    private String m_text;
    private AttributedCharacterIterator m_iterator;
    private int m_start;
    private int m_end;

    public TextContainer(String text, int width, int height)
    {
        m_text = text;
        m_width = width;
        m_height = height;

        AttributedString styledText = new AttributedString(text);
        m_iterator = styledText.getIterator();
        m_start = m_iterator.getBeginIndex();
        m_end = m_iterator.getEndIndex();
    }

    public String getText()
    {
        return m_text;
    }

    public Dimension getPreferredSize()
    {
        return new Dimension(m_width, m_height);
    }

    public void paint(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        FontRenderContext frc = g2.getFontRenderContext();

        LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
        measurer.setPosition(m_start);

        float x = 0, y = 0;
        while (measurer.getPosition() < m_end)
        {
            TextLayout layout = measurer.nextLayout(m_width);

            y += layout.getAscent();
            float dx = layout.isLeftToRight() ?
                    0 : m_width - layout.getAdvance();

            layout.draw(g2, x + dx, y);
            y += layout.getDescent() + layout.getLeading();
        }
    }
}

只是为了好玩,我让它适合一个圆圈(唉,没有理由,似乎):

public void paint(Graphics g)
{
    super.paintComponent(g);

    Graphics2D g2 = (Graphics2D) g;
    FontRenderContext frc = g2.getFontRenderContext();

    LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
    measurer.setPosition(m_start);

    float y = 0;
    while (measurer.getPosition() < m_end)
    {
        double ix = Math.sqrt((m_width / 2 - y) * y);
        float x = m_width / 2.0F - (float) ix;
        int width = (int) ix * 2;

        TextLayout layout = measurer.nextLayout(width);

        y += layout.getAscent();
        float dx = layout.isLeftToRight() ?
                0 : width - layout.getAdvance();

        layout.draw(g2, x + dx, y);
        y += layout.getDescent() + layout.getLeading();
    }
}

不过,我对dx计算不太了解。


9
2017-10-27 12:44





java.awt.font.TextLayout中 可能会有所帮助。这是他们的示例代码的片段:

   Graphics2D g = ...;
   Point2D loc = ...;
   Font font = Font.getFont("Helvetica-bold-italic");
   FontRenderContext frc = g.getFontRenderContext();
   TextLayout layout = new TextLayout("This is a string", font, frc);
   layout.draw(g, (float)loc.getX(), (float)loc.getY());

   Rectangle2D bounds = layout.getBounds();
   bounds.setRect(bounds.getX()+loc.getX(),
                  bounds.getY()+loc.getY(),
                  bounds.getWidth(),
                  bounds.getHeight());
   g.draw(bounds);

否则,您总是可以使用Swing文本元素为您完成工作,只需传入您想要绘制的图形。


3
2017-10-27 10:44



我已经拿到了那个。我的问题是,我不希望文本周围有一个矩形,而是一个给定矩形内的文本。 - Burkhard
请注意,如果您传递的字符串为空,TextLayout的构造函数会无证地抛出异常。 - Ricky Clarkson
使用Shape labelShape = textLayout1.getOutline(null);然后是g2d.fill(labelShape);他们应该消除边界 - mk7


使用Epaga的方法逐步增加字符串,一次一个字,以查找字符串的长度。一旦长度超过矩形,删除最后一个单词并打印。重复,直到你用完了。

这听起来像一个糟糕的算法,但对于每一行,它实际上是O(screenWidth / averageCharacterWidth)=> O(1)。

仍然,使用StringBuffer来构建你的字符串!


1
2017-10-27 11:26



这不会有O(1)的时间复杂度; O(n),也许,因为你需要(至少)添加一个单词(即在StringBuilder.append中添加一些字符),直到所有的字符都用完为止。 - jamesh
我似乎很难正确地表达自己......我的意思是每一行都是O(1)。你可以抛出1000个单词,但是,对于每一行,它将停止在屏幕边缘添加单词。总的来说,是的,O(n)。 - Ryan Fox
它只有在你认为字符串最多为2行时才有效,当它更多时,它将不起作用。 - Cyril N.


有一点麻烦自己这是我的解决方案:

Graphics2D g=....
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
String[] outputs = text.split("\n");
for(int i=0; i<outputs.length; i++){
    g.drawString(outputs[i], 15,(int) (15+i*layout.getBounds().getHeight()+0.5));

希望有所帮助......简单但有效。


1
2018-05-31 00:16





您可以使用JLabel并使用html嵌入文本。

JLabel.setText("<html>"+line1+"<br>"+line2);

0
2017-10-27 12:53



你的HTML被剥离了。我假设第一个字符串文字是<html>,第二个字符串是<br>。 - Michael Myers♦
如果你必须添加自己的换行符,它是否会破坏目的? - Ryan Fox