我敢肯定,我们所有人都在Facebook状态(或其他位置)上看到省略号,然后单击“显示更多”,并且只有另外两个字符左右。我猜这是由于懒惰的编程,因为肯定有一种理想的方法。
我的人把苗条的字符算作[iIl1]“半个字符”,但是当省略号几乎没有隐藏任何字符时,这并不能解决。
[iIl1]
有没有理想的方法?这是我的:
/** * Return a string with a maximum length of <code>length</code> characters. * If there are more than <code>length</code> characters, then string ends with an ellipsis ("..."). * * @param text * @param length * @return */ public static String ellipsis(final String text, int length) { // The letters [iIl1] are slim enough to only count as half a character. length += Math.ceil(text.replaceAll("[^iIl]", "").length() / 2.0d); if (text.length() > length) { return text.substring(0, length - 3) + "..."; } return text; }
语言并不重要,但可以标记为Java,因为这是我最感兴趣的内容。
我喜欢让“瘦”字符算作半个字符的想法。简单而良好的近似。
然而,大多数省略的主要问题是(imho)他们在中间砍了字。这是一个考虑单词边界的解决方案(但不涉及像素数学和Swing-API)。
private final static String NON_THIN = "[^iIl1\\.,']"; private static int textWidth(String str) { return (int) (str.length() - str.replaceAll(NON_THIN, "").length() / 2); } public static String ellipsize(String text, int max) { if (textWidth(text) <= max) return text; // Start by chopping off at the word before max // This is an over-approximation due to thin-characters... int end = text.lastIndexOf(' ', max - 3); // Just one long word. Chop it off. if (end == -1) return text.substring(0, max-3) + "..."; // Step forward as long as textWidth allows. int newEnd = end; do { end = newEnd; newEnd = text.indexOf(' ', end + 1); // No more spaces. if (newEnd == -1) newEnd = text.length(); } while (textWidth(text.substring(0, newEnd) + "...") < max); return text.substring(0, end) + "..."; }
似乎您可以从Java图形上下文的中获得更准确的几何图形FontMetrics。
附录:解决此问题时,可能有助于区分模型和视图。模型是StringUTF-16代码点的有限序列,而视图是一系列字形,以某种字体在某些设备上呈现。
在Java的特定情况下,可以使用JavaSwingUtilities.layoutCompoundLabel()进行翻译。下面的示例拦截了布局调用BasicLabelUI以演示效果。在其他情况下也可以使用实用程序方法,但是FontMetrics必须根据经验确定适当的方法。
JavaSwingUtilities.layoutCompoundLabel()
替代文字
import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.border.LineBorder; import javax.swing.plaf.basic.BasicLabelUI; /** @see http://stackoverflow.com/questions/3597550 */ public class LayoutTest extends JPanel { private static final String text = "A damsel with a dulcimer in a vision once I saw."; private final JLabel sizeLabel = new JLabel(); private final JLabel textLabel = new JLabel(text); private final MyLabelUI myUI = new MyLabelUI(); public LayoutTest() { super(new GridLayout(0, 1)); this.setBorder(BorderFactory.createCompoundBorder( new LineBorder(Color.blue), new EmptyBorder(5, 5, 5, 5))); textLabel.setUI(myUI); textLabel.setFont(new Font("Serif", Font.ITALIC, 24)); this.add(sizeLabel); this.add(textLabel); this.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { sizeLabel.setText( "Before: " + myUI.before + " after: " + myUI.after); } }); } private static class MyLabelUI extends BasicLabelUI { int before, after; @Override protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR) { before = text.length(); String s = super.layoutCL( label, fontMetrics, text, icon, viewR, iconR, textR); after = s.length(); System.out.println(s); return s; } } private void display() { JFrame f = new JFrame("LayoutTest"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(this); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new LayoutTest().display(); } }); } }