6

在我的应用程序中,我有一个字体大小超过 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();
            }
        });
    }
}

我还尝试了我的上一篇文章:如何改变摇摆标签中的间隙并尝试插入,但这在 linux 和 windows 中看起来不同

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

4

3 回答 3

9

JDigit可能会给你一些想法:

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

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

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

图片

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

优点:

  • 绝对控制布局。

  • TexteLayout基于的边界几何FontMetrics

缺点:

  • 没有Icon支持。

  • 不支持 HTML。

请注意,JComponent作者“建议您将组件放在 a 中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, -r.x, -r.y);
        }

        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();
            }
        });
    }
}
于 2013-04-15T11:55:07.563 回答
3

这样做的“正确方法”是扩展“BasicLabelUI”并覆盖“受保护的字符串 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;
        }
    }
}
于 2013-04-26T23:11:39.393 回答
0

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

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);
    }
});
于 2013-05-01T21:36:38.080 回答