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カスタムの強調表示に を使用します。

画像

補遺: hereに記載されているように、根本的な問題は、フォントの高さに含まれていると定義されているフォントのLeadingです。FontMetrics@Guillaume Polet のコメントで示唆されているように、独自の .xml 内の好きな場所にテキストをレンダリングできますJComponentここTextLayoutで説明するは、以下に示すように境界を計算するために使用できます。

長所:

  • 配置を完全に制御。

  • TexteLayoutに基づく境界のジオメトリFontMetrics

短所:

  • 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, -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」を拡張し、「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;
        }
    }
}
于 2013-04-26T23:11:39.393 に答える