如何获得具有多行功能的 JLabel? HTML真的是唯一的方法吗?

How do I get a JLabel with multiline capability? Is HTML really the only way?

我认为答案是 "you can't",但是关于 Swing 我还有很多不了解的地方。所以:

我需要显示只读的多行文本,一个字符一个字符,没有神奇的解释,在一个刚好能容纳它的矩形中。能够 select 一个区域(并从中复制)是一个优势。

到目前为止失败的方法:

JLabel("a\nb"); 根本行不通,我无法想象为什么,因为这应该是唯一正确的答案。 JLabel 记录为显示交给它的文本。但显然不是换行符。

JLabel 有另一个可怕的问题:如果文本恰好以 <html> 开头,它又不会显示它收到的文本 - 它会将其重新解释为 HTML。文档不仅没有提到这一点,而且现在我遇到了一个问题,如果允许用户输入显示在 JLabel 中的文本(他们是),他们可以坚持格式化标签并导致各种欺骗(和他们会的)。

我找不到关闭此功能的方法;所以为了准确显示给定的文本,我必须预先处理文本以进行完整的 HTML 引用,并将 \n 更改为 <br>,然后将其全部包装在 <html>...</html>.我在核心 Java 中找不到引用的任何内容 - 显然你必须自己编写或依赖其他库,如 apache? Recommended method for escaping HTML in Java 给出了很多不依赖第三方库的理由。现在我的 java 代码将包含一个 HTML 布局引擎,以便我可以可靠地显示文本?我非常想避免这种未记录且脆弱的黑客行为。

JTextArea 可以处理多行文本,可以设置为只读。但我看不出如何告诉它缩小到只容纳文本所需的大小。 (JLabel 至少做对了。[编辑:或不 - 我恢复到 JLabel,现在标签在他们的行中居中或扩展到全尺寸 - 我显然在某处缺少设置])我认为必须有一种告诉它停止扩展以填充行的方法,但我该怎么做? setSize(1,1) 不这样做。我觉得正确的答案就在这条路上,但我需要一个指针来让它自动调整到最小尺寸。

或者有更好的组件吗?有没有办法将文本字符串呈现为最小尺寸的图像?我可以在 JLabel 中显示它...

我还试图避免扫描所有文本并组成一个左对齐 JLabel 的垂直框,每行一个 - 可能是可能的,但当有(也许?)一个更简单的解决方案时,它肯定比我想要的更复杂.

请注意,我无法对任何组件的大小进行硬编码。组件描述由服务器生成并传送到应用程序,应用程序根据描述动态生成小部件,并将它们添加到 Box 容器中。服务器无法以像素为单位进行计算;我需要依靠 Swing 来简单地根据 Boxes 中事物的顺序进行布局。在大多数情况下,这意味着小部件必须占据它们的最小可能大小。

我该怎么办?

按照指示我尝试了这个。只出现了一个窗格,而且没有边框,所以我看不出它会占用多少 space。

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.BorderFactory; 
import javax.swing.border.Border;

public class MainTextPane {
    public static void main(final String[] args) {
        Border loweredbevel = BorderFactory.createLoweredBevelBorder();

        final JTextPane pane = new JTextPane();
        pane.setText("Hello, this is a\ntwo lined string!");
        final JTextPane pane2 = new JTextPane();
        pane2.setText("Next!");
        pane.setBorder(loweredbevel); //no effect
        pane2.setBorder(loweredbevel); //no effect


        final JFrame frame = new JFrame("TextPane with new lines.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(pane);
        frame.getContentPane().add(pane2); //clobbered previous pane
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

随时使用 JTextPane which is a JEditorPane. If you want the preferred size, just call getPreferredSize()(设置文本后)。您可以将其设置为可编辑或不可编辑,并轻松地从中复制粘贴,它会处理新行。简单的使用就不需要复杂:

import javax.swing.JFrame;
import javax.swing.JTextPane;

public class MainTextPane {
    public static void main(final String[] args) {
        final JTextPane pane = new JTextPane();
        pane.setText("Hello, this is a\nnew lined string! Hope JTextPane is what we are looking for here.");

        final JFrame frame = new JFrame("TextPane with new lines.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

如果你要用任意文本加载它,我建议在 JScrollPane 中使用它,并根据你的需要设置 JScrollPane 的首选大小。

也测试了JEditorPane,不会像JTextPane那么简单。

如果您有更多要求,请在评论中告诉我们或更新您的问题。

编辑 1

关于您提供的代码,您看到第一个窗格 clobbers 第二个窗格,因为您将它们都添加到了内容窗格,而没有设置 LayoutManager. Try setting the LayoutManager to FlowLayout例如(这会将两个窗格并排放置)。

按照斜角边框,我看得很清楚,但确实有点薄,所以你没看到的原因可能是这个。我 运行 你提供的代码也是,它是可见的,但不容易。无论如何,试试这个代码:

import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.BorderFactory; 
import javax.swing.border.Border;

public class MainTextPane {
    public static void main(final String[] args) {
        Border loweredbevel = BorderFactory.createLoweredBevelBorder();

        final JTextPane pane = new JTextPane();
        pane.setText("Hello, this is a\ntwo lined string!");
        final JTextPane pane2 = new JTextPane();
        pane2.setText("Next!");
        pane.setBorder(loweredbevel);
        pane2.setBorder(loweredbevel);


        final JFrame frame = new JFrame("TextPane with new lines.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new FlowLayout()); //Added this line of code...
        frame.getContentPane().add(pane);
        frame.getContentPane().add(pane2);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}