如何消除大尺寸java swing标签的差距

在我的应用程序中,我有一个字体大小超过200的标签。这个标签包含大的上下(不规则)间隙。 我该如何删除它?

这是我的代码:

package Core; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.UIManager; public class LabelDemo extends JPanel { public LabelDemo() { super(new GridBagLayout()); JLabel label2; GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; // Create the other labels. label2 = new JLabel("Text-Only Label"); label2.setBorder(BorderFactory.createTitledBorder("aaaaaaaa")); label2.setFont(new Font("Verdana", Font.PLAIN, (int) 220)); // label2.setBorder(new EmptyBorder(-50, 0, 0, 0)); // Add the labels. add(label2, c); } /** * Create the GUI and show it. For thread safety, this method should be invoked from the event dispatch thread. */ private static void createAndShowGUI() { // Create and set up the window. JFrame frame = new JFrame("LabelDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add content to the window. frame.add(new LabelDemo()); // Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { // Schedule a job for the event dispatch thread: // creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { // Turn off metal's use of bold fonts UIManager.put("swing.boldMetal", Boolean.FALSE); createAndShowGUI(); } }); } } 

我也尝试了我的上一篇文章: 如何改变swing标签的差距和使用insets进行实验,但这在linux和windows中看起来不同

有没有更好的方法来消除这种差距?

JDigit可能会给你一些想法:

  • 它覆盖了paintComponent()以对高分辨率的BufferedImage进行下采样并控制几何。

  • 它使用setBorderPainted(false)来设置borderPainted属性。

  • 它使用FocusHandler进行自定义突出显示。

图片

附录:如此处所述,根本问题是字体的前导 ,在FontMetrics定义为包含在字体的高度中。 正如@Guillaume Polet的评论中所建议的那样,您可以在自己的JComponent任何位置呈现文本。 这里讨论的TextLayout可用于计算边界,如下所示。

优点:

  • 绝对控制放置。

  • 基于FontMetricsTexteLayout边界的几何。

缺点:

  • 没有Icon支持。

  • 没有HTML支持。

请注意, JComponent作者“建议您将组件放在JPanel并在JPanel设置边框。”

无铅图像

 import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import javax.swing.BorderFactory; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; /** * @see https://stackoverflow.com/a/16014525/230513 */ public class UnleadedTest { private static class Unleaded extends JComponent { private Font font = new Font("Verdana", Font.PLAIN, 144); private FontRenderContext frc = new FontRenderContext(null, true, true); private String text; private TextLayout layout; private Rectangle r; public Unleaded(String text) { this.text = text; calcBounds(); } @Override public Dimension getPreferredSize() { return new Dimension(r.width, r.height); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; calcBounds(); layout.draw(g2d, -rx, -ry); } private void calcBounds() { layout = new TextLayout(text, font, frc); r = layout.getPixelBounds(null, 0, 0); } } private void display() { JFrame f = new JFrame("Unleaded"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Unleaded label = new Unleaded("Unleaded"); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createTitledBorder("Title")); panel.add(label); f.add(panel); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new UnleadedTest().display(); } }); } } 

执行此操作的“正确方法”是扩展“BasicLabelUI”并覆盖“protected String layoutCL()”方法。 这是负责在标签内布置所有内容的方法,并在调用JLabel的“getPreferredSize()”时调用。 因此,此方法确定组件的高度。

如果向下钻取足够深,则会看到高度由SwingUtilities:1021类中的以下行确定(由layoutCL使用):

 textR.height = fm.getHeight(); 

所以标签不会导致空格,字体是。 该标签符合FontMetrics对象所说的是该大小的字体的最大高度。

最简单的方法可能是作弊; 强制进行大小计算以执行不应该执行的操作。 下面是您可以试验的自定义LabelUI组件的示例。 例如,如果强制变量’dy’为’-40’,则文本将位于顶部。 如果你想要更耐用的东西,你可以检查标签字符串中的所有字母,测量它们的最大高度,并在layoutCL方法中使用它。 但这显然更有效。

 package Core; import sun.swing.SwingUtilities2; import javax.swing.*; import javax.swing.plaf.LabelUI; import javax.swing.plaf.basic.BasicLabelUI; import javax.swing.text.View; import java.awt.*; public class LabelDemo extends JPanel { public LabelDemo() { super(new GridBagLayout()); JLabel label2; GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; // Create the other labels. label2 = new JLabel("Text-Only Label"); label2.setVerticalAlignment(SwingUtilities.TOP); label2.setVerticalTextPosition(SwingUtilities.TOP); label2.setUI(SkinnyLabelUI.createUI(label2)); label2.setBorder(BorderFactory.createTitledBorder("aaaaaaaa")); label2.setFont(new Font("Verdana", Font.PLAIN, (int) 220)); // label2.setBorder(new EmptyBorder(-50, 0, 0, 0)); // Add the labels. add(label2, c); } /** * Create the GUI and show it. For thread safety, this method should be * invoked from the event dispatch thread. */ private static void createAndShowGUI() { // Create and set up the window. JFrame frame = new JFrame("LabelDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Add content to the window. frame.add(new LabelDemo()); // Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { // Schedule a job for the event dispatch thread: // creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { // Turn off metal's use of bold fonts UIManager.put("swing.boldMetal", Boolean.FALSE); createAndShowGUI(); } }); } private static class SkinnyLabelUI extends BasicLabelUI { private static final SkinnyLabelUI labelUI = new SkinnyLabelUI(); public static LabelUI createUI(JComponent c) { return labelUI; } protected String layoutCL( JLabel label, FontMetrics fm, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR) { int verticalAlignment = label.getVerticalAlignment(); int horizontalAlignment = label.getHorizontalAlignment(); int verticalTextPosition = label.getVerticalTextPosition(); int horizontalTextPosition = label.getHorizontalTextPosition(); if (icon != null) { iconR.width = icon.getIconWidth(); iconR.height = icon.getIconHeight(); } else { iconR.width = iconR.height = 0; } /* Initialize the text bounds rectangle textR. If a null * or and empty String was specified we substitute "" here * and use 0,0,0,0 for textR. */ boolean textIsEmpty = (text == null) || text.equals(""); int lsb = 0; int rsb = 0; /* Unless both text and icon are non-null, we effectively ignore * the value of textIconGap. */ int gap; View v; if (textIsEmpty) { textR.width = textR.height = 0; text = ""; gap = 0; } else { int availTextWidth; gap = (icon == null) ? 0 : label.getIconTextGap(); if (horizontalTextPosition == SwingUtilities.CENTER) { availTextWidth = viewR.width; } else { availTextWidth = viewR.width - (iconR.width + gap); } v = (label != null) ? (View) label.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(label, fm, text); lsb = SwingUtilities2.getLeftSideBearing(label, 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(label, fm, text, availTextWidth); textR.width = SwingUtilities2.stringWidth(label, fm, text); } textR.height = fm.getHeight(); System.out.println("font height: " + textR.height); } } /* Compute textR.x,y given the verticalTextPosition and * horizontalTextPosition properties */ if (verticalTextPosition == SwingUtilities.TOP) { if (horizontalTextPosition != SwingUtilities.CENTER) { textR.y = 0; } else { textR.y = -(textR.height + gap); } } else if (verticalTextPosition == SwingUtilities.CENTER) { textR.y = (iconR.height / 2) - (textR.height / 2); } else { // (verticalTextPosition == BOTTOM) if (horizontalTextPosition != SwingUtilities.CENTER) { textR.y = iconR.height - textR.height; } else { textR.y = (iconR.height + gap); } } if (horizontalTextPosition == SwingUtilities.LEFT) { textR.x = -(textR.width + gap); } else if (horizontalTextPosition == SwingUtilities.CENTER) { textR.x = (iconR.width / 2) - (textR.width / 2); } else { // (horizontalTextPosition == RIGHT) textR.x = (iconR.width + gap); } // WARNING: DefaultTreeCellEditor uses a shortened version of // this algorithm to position it's Icon. If you change how this // is calculated, be sure and update DefaultTreeCellEditor too. /* labelR is the rectangle that contains iconR and textR. * Move it to its proper position given the labelAlignment * properties. * * To avoid actually allocating a Rectangle, Rectangle.union * has been inlined below. */ int labelR_x = Math.min(iconR.x, textR.x); int labelR_width = Math.max(iconR.x + iconR.width, textR.x + textR.width) - labelR_x; int labelR_y = Math.min(iconR.y, textR.y); int labelR_height = Math.max(iconR.y + iconR.height, textR.y + textR.height) - labelR_y; int dx, dy; if (verticalAlignment == SwingUtilities.TOP) { dy = viewR.y - labelR_y; } else if (verticalAlignment == SwingUtilities.CENTER) { dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2)); } else { // (verticalAlignment == BOTTOM) dy = (viewR.y + viewR.height) - (labelR_y + labelR_height); } if (horizontalAlignment == SwingUtilities.LEFT) { dx = viewR.x - labelR_x; } else if (horizontalAlignment == SwingUtilities.RIGHT) { dx = (viewR.x + viewR.width) - (labelR_x + labelR_width); } else { // (horizontalAlignment == CENTER) dx = (viewR.x + (viewR.width / 2)) - (labelR_x + (labelR_width / 2)); } /* Translate textR and glypyR by dx,dy. */ textR.x += dx; textR.y += dy; iconR.x += dx; iconR.y += dy; if (lsb < 0) { // lsb is negative. Shift the x location so that the text is // visually drawn at the right location. textR.x -= lsb; textR.width += lsb; } if (rsb > 0) { textR.width -= rsb; } return text; } } } 

更改边框偏移可能会有所帮助:

 int OFFSET_TOP=50,OFFSET_BOTTOM=50; label.setBorder(new TitledBorder(TITLE){ @Override public Insets getBorderInsets(Component c, Insets insets){ return new Insets(insets.top - OFFSET_TOP, insets.left, insets.bottom - OFFSET_BOTTOM, insets.right); } });