Как автоматически настроить размер шрифта нескольких JLabel в зависимости от размера контейнера? - PullRequest
6 голосов
/ 22 марта 2012

Мне нужно изменить размер шрифта нескольких JLabel на основе коэффициента масштабирования, используемого для изменения размера контейнера. Чтобы сделать это, я устанавливаю шрифт каждого JLabel в нуль, чтобы они принимали шрифт контейнера. Это работает, но это также дает странные результаты.

Если быть точным, то текст, кажется, "отстает" от контейнера, а иногда даже усекается Я хотел бы избежать такого поведения. Есть идеи как?

Пример кода, имитирующего поведение:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.AffineTransform;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TextResize implements Runnable {

    public static void main(String[] args) {
        TextResize example = new TextResize();
        SwingUtilities.invokeLater(example);
    }

    public void run() {
        JFrame frame = new JFrame("JLabel Text Resize");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(800, 400));

        Container container = frame.getContentPane();
        container.setLayout(new BorderLayout());

        final JPanel labelContainer = new JPanel(new GridBagLayout());
        labelContainer.setBorder(BorderFactory.createLineBorder(Color.black));

        //initial font
        final Font textFont = new Font("Lucida Console", Font.PLAIN, 10).deriveFont(AffineTransform.getScaleInstance(1, 1));
        labelContainer.setFont(textFont);

        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.insets = new Insets(0, 10, 0, 10);
        c.weightx = 1;
        for (int i = 0; i < 5; i++) {
            JLabel f = new JLabel("Text here with possibly looooooooong words");
            f.setBorder(BorderFactory.createLineBorder(Color.green));
            f.setFont(null);//take the font from parent
            c.gridy = i;
            labelContainer.add(f, c);
        }

        JSlider slider = new JSlider(0,50000,10000);
        slider.addChangeListener(new ChangeListener() {     
            double containerWidth = labelContainer.getPreferredSize().getWidth();
            double containerHeight = labelContainer.getPreferredSize().getHeight();

            @Override
            public void stateChanged(ChangeEvent ev) {
                JSlider source = (JSlider) ev.getSource();
                double scale = (double) (source.getValue() / 10000d);

                //scaling the container
                labelContainer.setSize((int) (containerWidth * scale), (int) (containerHeight * scale));

                //adjusting the font: why does it 'lag' ? why the truncation at times?
                Font newFont = textFont.deriveFont(AffineTransform.getScaleInstance(scale, scale));
                labelContainer.setFont(newFont);

                //print (font.getSize() does not change?)
                System.out.println(scale + " " + newFont.getTransform() + newFont.getSize2D());
            }
        });

        container.add(slider, BorderLayout.NORTH);
        JPanel test = new JPanel();
        test.setLayout(null);
        labelContainer.setBounds(5, 5, labelContainer.getPreferredSize().width, labelContainer.getPreferredSize().height);
        test.add(labelContainer);
        container.add(test, BorderLayout.CENTER);

        frame.pack();
        frame.setVisible(true);
    }

}

Изображение: http://i.stack.imgur.com/tZLOO.png

Спасибо

-s

Ответы [ 3 ]

6 голосов
/ 22 марта 2012

Вы можете использовать любой из следующих методов:

2 голосов
/ 26 апреля 2012

Я вроде решил проблему, добавив:

@Override
protected void paintComponent(Graphics g) {
    final Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    super.paintComponent(g2d);
}

В любом случае, спасибо.

1 голос
/ 10 июня 2015

Если скорость работы является проблемой, то вам может пригодиться следующая информация о 3 методах, указанных MKorbel выше.

  1. Код Коберда имеет некоторыеограничения, если они используются для нескольких вызовов (например, в слушателе sizeChanged или LayoutManager)

  2. Метод Трашгод в 2–4 раза медленнее, чем метод Станислава (ноон также предназначен для заполнения области ОБА направления, как ОП задала в этом вопросе, поэтому не удивительно.)

  3. Приведенный ниже код улучшает метод прямоугольника Станислава (начиная с текущего размера шрифта каждый раз, а не возвращаясь обратно к MIN_FONT_SIZE каждый раз) и, таким образом, работает в 20-50 раз быстрее, чем этот код, особенно когда окно / шрифт велики.

В нем также рассматривается ограничение в этом коде, которое эффективно работает только для меток, расположенных в 0,0 (как в приведенном там примере).Приведенный ниже код работает для нескольких ярлыков на панели и в любом месте.

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

// Improved version of http://java-sl.com/tip_adapt_label_font_size.html

public class FontResizingLabel extends JLabel {
    public static final int MIN_FONT_SIZE=3;
    public static final int MAX_FONT_SIZE=240;
    Graphics g;
    int currFontSize = 0;

    public FontResizingLabel(String text) {
        super(text);
        currFontSize = this.getFont().getSize();
        init();
    }

    protected void init() {
        addComponentListener(new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                adaptLabelFont(FontResizingLabel.this);
            }
        });
    }

   protected void adaptLabelFont(JLabel l) {
        if (g==null) {
            return;
        }
        currFontSize = this.getFont().getSize();

        Rectangle r  = l.getBounds();
        r.x          = 0;    
        r.y          = 0;    
        int fontSize = Math.max(MIN_FONT_SIZE, currFontSize);
        Font f       = l.getFont();

        Rectangle r1 = new Rectangle(getTextSize(l, l.getFont()));
        while (!r.contains(r1)) {
               fontSize --;
            if (fontSize <= MIN_FONT_SIZE) 
                break;
            r1 = new Rectangle(getTextSize(l, f.deriveFont(f.getStyle(), fontSize)));
        }    

        Rectangle r2 = new Rectangle();
        while (fontSize < MAX_FONT_SIZE) {
            r2.setSize(getTextSize(l, f.deriveFont(f.getStyle(),fontSize+1)));
            if (!r.contains(r2)) {
                break;
            }
            fontSize++;
        }

        setFont(f.deriveFont(f.getStyle(),fontSize));
        repaint();
    }

    private Dimension getTextSize(JLabel l, Font f) {
        Dimension size  = new Dimension();
        //g.setFont(f);   // superfluous.
        FontMetrics fm  = g.getFontMetrics(f);
        size.width      = fm.stringWidth(l.getText());
        size.height     = fm.getHeight();
        return size;
    }
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        this.g=g;
    }

    public static void main(String[] args) throws Exception {
        FontResizingLabel label=new FontResizingLabel("Some text");
        JFrame frame=new JFrame("Resize label font");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(label);

        frame.setSize(300,300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
...