如何在包含HTML的JLabel中获取省略号?

时间:2015-06-30 14:58:57

标签: java swing jlabel

当我将HTML标记组合到JLabel文本中时,我忽略了当空间太小而无法显示完整文本时显示的省略号行为。在我的特定情况下,它是一个TableCellRenderer,它扩展了JLabel(swing的默认值或其他)。现在,当列宽太小而文本无法完全显示时,它不会显示省略号。

例如,见下图: 对于左列,我使用HTML将文本包装在渲染器中:setText("<html>" + "<strong>" + value.toString() + "</strong>" + "</html>");。正如您所看到的,当列宽太小而无法包含文本时,它只是被剪切。但是,显示日期和时间并使用DefaultTableCellRenderer的右列在未能包含完整文本时显示省略号。

enter image description here

所以我的问题是,我可以同时拥有两个吗?意思是,用HTML包装文本并仍然得到省略号?

更新

我发现在使用HTML时没有获得省略号的原因。我按照JComponent#paintComponent(Graphics g)的代码一直到BasicLabelUI#layoutCL(...)。请参阅从上一个中获取的以下代码段。如果它没有html属性,它只剪裁字符串(当标签文本用html包装时为true)。但我不知道如何解决它:

    v = (c != null) ? (View) c.getClientProperty("html") : null;
    if (v != null) {
        textR.width = Math.min(availTextWidth,
                               (int) v.getPreferredSpan(View.X_AXIS));
        textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
    } else {
        textR.width = SwingUtilities2.stringWidth(c, fm, text);
        lsb = SwingUtilities2.getLeftSideBearing(c, fm, text);
        if (lsb < 0) {
            // If lsb is negative, add it to the width and later
            // adjust the x location. This gives more space than is
            // actually needed.
            // This is done like this for two reasons:
            // 1. If we set the width to the actual bounds all
            //    callers would have to account for negative lsb
            //    (pref size calculations ONLY look at width of
            //    textR)
            // 2. You can do a drawString at the returned location
            //    and the text won't be clipped.
            textR.width -= lsb;
        }
        if (textR.width > availTextWidth) {
            text = SwingUtilities2.clipString(c, fm, text,
                                              availTextWidth);
            textR.width = SwingUtilities2.stringWidth(c, fm, text);
        }
        textR.height = fm.getHeight();
    }

3 个答案:

答案 0 :(得分:1)

只要您的问题中的HTML内容简单,使用定制的JLabel即可完成省略号显示。这是一个工作示例。 只需调整窗口的大小,您会看到省略号出现和消失,并且随着标签随窗口调整大小而适当地剪切了文本。

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JLabel;
import javax.swing.border.Border;

public class SimpleHTMLJLabel extends JLabel
{
    private static final long serialVersionUID = -1799635451172963826L;

    private String textproper;
    private String ellipsis = "...";
    private int textproperwidth;
    private FontMetrics fontMetrics;
    private int ellipsisWidth;
    private int insetsHorizontal;
    private int borderHorizontal;

    public SimpleHTMLJLabel(String textstart, String textproper, String textend)
    {
        super(textstart + textproper + textend);
        this.textproper = textproper;
        insetsHorizontal = getInsets().left + getInsets().right;
        fontMetrics = getFontMetrics(getFont());
        calculateWidths();
        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e)
            {
                int availablewidth = getWidth();
                if (textproperwidth > availablewidth - (insetsHorizontal + borderHorizontal)) 
                {
                    String clippedtextproper = textproper;
                    while (clippedtextproper.length() > 0 
                           && fontMetrics.stringWidth(clippedtextproper) + ellipsisWidth > availablewidth - (insetsHorizontal + borderHorizontal)) 
                    {
                        clippedtextproper = clipText(clippedtextproper);
                    }
                    setText(textstart + clippedtextproper + ellipsis + textend);
                } else 
                {
                    setText(textstart + textproper + textend);
                }
            }
        });
    }

    private void calculateWidths()
    {
        if (textproper != null) 
        {
            textproperwidth = fontMetrics.stringWidth(textproper);
        }

        if (ellipsis != null)
        {
            ellipsisWidth = fontMetrics.stringWidth(ellipsis);
        }
    }

    @Override
    public void setFont(Font font)
    {
        super.setFont(font);
        fontMetrics = getFontMetrics(getFont());
        calculateWidths();
    }

    private String clipText(String clippedtextproper)
    {
        return clippedtextproper.substring(0, clippedtextproper.length() - 1);
    }

    @Override
    public void setBorder(Border border)
    {
        super.setBorder(border);
        borderHorizontal = border.getBorderInsets(this).left + border.getBorderInsets(this).right;
    }
}

主要

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Main
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run()
            {
                JFrame window = new JFrame();
                window.setResizable(true);
                window.setTitle("Label Test");
                window.getContentPane().add(getContent());
                window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                window.setSize(400, 200);
                window.setLocationRelativeTo(null);
                window.setVisible(true);
            }
        });
    }

    protected static Component getContent()
    {
        JPanel panel = new JPanel(new BorderLayout());
        SimpleHTMLJLabel label = new SimpleHTMLJLabel("<html><strong>", "TEST1test2TEST3test4TEST5test6TEST7test8TEST", "</strong></html>");
        label.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLUE, 5),
        BorderFactory.createEmptyBorder(10, 10, 10, 10)));
        label.setFont(label.getFont().deriveFont(20F));
        panel.add(label, BorderLayout.CENTER);
        return panel;
    }
}

答案 1 :(得分:0)

我要说:不,你不能同时拥有。

我认为如果你想要自定义样式和省略号,你必须自己不用HTML和使用自定义TableCellRenderer。

如果你想尝试吃蛋糕并吃掉它,你可以通过创建自己的View对象并使用c.putClientProperty(&#34; html&#34;,value)来设置它,但是我怀疑HTML呈现代码没有ellipsing的概念(文本溢出是HTML 5ish特性)所以你必须弄清楚如何教它做这件事。我怀疑这比编写自己的TableCellRenderer要困难得多。

答案 2 :(得分:0)

这里是SimpleHTMLJLabel的修改版,正在使用上面的代码

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.IOException;
import java.io.StringReader;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import javax.swing.JLabel;
import javax.swing.border.Border;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML.Tag;
import javax.swing.text.html.HTMLEditorKit.ParserCallback;

public class SimpleHTMLJLabel extends JLabel {

    private static final String ellipsis = "...";
    private static final String Set = null;
    private int textproperwidth;
    private FontMetrics fontMetrics;
    private int ellipsisWidth;
    private int insetsHorizontal;
    private int borderHorizontal;

    private List<Entry<String, String>> lines;
    static String HTML = "<HTML>";

    public SimpleHTMLJLabel() {
        insetsHorizontal = getInsets().left + getInsets().right;
        fontMetrics = getFontMetrics(getFont());
        calculateWidths();
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                int availablewidth = getWidth();
                renderHtml(availablewidth);
            }
        });
    }

    public SimpleHTMLJLabel(String text) {
        this();
        setText(text);
    }

    public SimpleHTMLJLabel(List<Entry<String, String>> lines) {
        this();
        this.lines = lines;
        calculateWidths();
        super.setText(HTML + toHtml(lines));
    }

    @Override
    public void setText(String text) {
        if (text.toUpperCase().startsWith(HTML)) {
            this.lines = parseHtml(text);
            calculateWidths();
            super.setText(HTML + toHtml(lines));
            return;
        }
        super.setText(text);
    }

    private List<Entry<String, String>> parseHtml(String text) {
        List<Entry<String, String>> ret = new ArrayList<>();
        java.util.Map<Tag, MutableAttributeSet> tags = new HashMap<>();
        try {
            (new javax.swing.text.html.parser.ParserDelegator()).parse(new StringReader(text), new ParserCallback() {
                @Override
                public void handleEndTag(Tag t, int pos) {
                    //TODO clean handle MutableAttributeSet a
                    tags.remove(t);
                }

                @Override
                public void handleStartTag(javax.swing.text.html.HTML.Tag t, MutableAttributeSet a, int pos) {
                    if (t == Tag.HTML) return;
                    if (t == Tag.P) return;
                    if (t == Tag.BR) return;
                    if (t == Tag.BODY) return;
                    tags.put(t,a);
                }

                @Override
                public void handleText(char[] data, int pos) {

                    String formats = tags.entrySet().stream().map(t -> "<" + t.getKey() + getAttrib(t.getValue) + ">").collect(Collectors.joining());
                    ret.add(new AbstractMap.SimpleEntry<>(formats, new String(data)));
                }

                private String getAttrib(MutableAttributeSet t) {
                    // TODO Auto-generated method stub
                    //return " style='color:red'";
                    return " " + t;
                }
            }, false);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return ret;
    }

    private static String toEndTag(String s) {
        return s.replace("<", "</");
    }

    private static String toHtml(List<Entry<String, String>> lines) {
        return lines.stream().map(s -> s.getKey() + s.getValue() + toEndTag(s.getKey())).collect(Collectors.joining());
    }

    private static String toPlain(List<Entry<String, String>> lines) {
        return lines.stream().map(s -> s.getValue()).collect(Collectors.joining("  "));
    }

    static private List<Entry<String, String>> clipText(List<Entry<String, String>> properList) {
        Entry<String, String> last = properList.get(properList.size() - 1);
        List<Entry<String, String>> ret = properList.subList(0, properList.size() - 1);
        String newlastValue = truncate(last.getValue());
        if (newlastValue.isEmpty()) {
            return ret;
        }
        List<Entry<String, String>> retNew = new ArrayList<>();
        retNew.addAll(ret);
        retNew.add(new AbstractMap.SimpleEntry<>(last.getKey(), newlastValue));
        return retNew;
    }

    static private String truncate(String newlastValue) {
        newlastValue = newlastValue.substring(0, newlastValue.length() - 1);
        while (newlastValue.endsWith(" ")) {
            newlastValue = newlastValue.substring(0, newlastValue.length() - 1);
        }
        return newlastValue;
    }

    private void calculateWidths() {
        if (lines != null) {
            textproperwidth = fontMetrics.stringWidth(toPlain(lines));
        }
        ellipsisWidth = fontMetrics.stringWidth(ellipsis);
    }

    @Override
    public void setFont(Font font) {
        super.setFont(font);
        fontMetrics = getFontMetrics(getFont());
        calculateWidths();
    }

    @Override
    public void setBorder(Border border) {
        super.setBorder(border);
        borderHorizontal = border.getBorderInsets(this).left + border.getBorderInsets(this).right;
    }
}

在TableCellRenderer中使用我的代码时,您需要在构造函数中立即调整大小,但是在没有列大小的情况下:

    public SimpleHTMLJLabel() {
...
    addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            int availablewidth = getWidth();
            renderHtml(availablewidth);
        }
    });
}

protected void renderHtml(int availablewidth) {
    if (lines == null || availablewidth == 0) return;
    System.out.println("renderHtml " + textproperwidth + ">" + availablewidth);
    if (textproperwidth > availablewidth - (insetsHorizontal + borderHorizontal)) {
        List<Entry<String, String>> properList = clipText(lines);
        while (properList.size() > 0 && fontMetrics.stringWidth(toPlain(properList)) + ellipsisWidth > availablewidth - (insetsHorizontal + borderHorizontal)) {
            properList = clipText(properList);
        }
        SimpleHTMLJLabel.super.setText(HTML + toHtml(properList) + ellipsis);
    } else {
        SimpleHTMLJLabel.super.setText(HTML + toHtml(lines));
    }
}
@Override
public void reshape(int x, int y, int w, int h) {
    if (w > 0) renderHtml(w - 5);
    super.reshape(x, y, w, h);
}

和在JTable中

    table = new JTable(model) {
        @Override
        public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
            Component c = super.prepareRenderer(renderer, row, column);
            TableColumn col = table.getColumnModel().getColumn(column);
            javax.swing.table.DefaultTableCellRenderer.UIResource csss;
            SimpleHTMLJLabel lab = new SimpleHTMLJLabel(((JLabel) 
                            //lab.setXXX( c.getXXX)); for font bcolor, color, border, etc
                            lab.setText(c.getText());
            lab.renderHtml(col.getWidth() - 5);
            return lab;
        }
    };

一个人可以重用html-labell组件来保存GC