如何使用包装文本制作自动调整大小的Swing Popup?

时间:2014-08-12 14:25:04

标签: java swing popup

我正在尝试制作一个只包含一个标签的弹出窗口。理想情况下,我会指定弹出窗口的最大宽度,以及要显示的字符串,弹出窗口会自动调整大小,以便文本在超过指定的最大宽度时会换行,并且它将是正确的高度。如果不通过计算复杂地使用FontMetrics来蛮力,是否可以在Swing中做这样的事情?以下是我尝试做的一个例子:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SpringLayout;


public class PopupTest extends JFrame {
    private static final String FOX_MESSAGE = "<html>The lithe, quick, brown fox jumps " +
                                              "happily and steadily over the fat, slobbery, " +
                                              "lazy dogs</html>";
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    PopupTest frame = new PopupTest();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PopupTest() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        final JPanel contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        final JButton btnLaunch = new JButton("Launch popup");
        btnLaunch.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Point btnLoc = btnLaunch.getLocationOnScreen();
                FoxPopup popup = new FoxPopup(FOX_MESSAGE, 200);
                popup.launch(contentPane, btnLoc.x, btnLoc.y + btnLaunch.getHeight());              
            }
        });
        contentPane.add(btnLaunch);
    }

    public class FoxPopup extends JPanel {
        private static final int ARBITRARILY_TALL = 9999;
        private Popup popup;

        public FoxPopup(String message, int width) {
            setBackground(Color.pink);
//          SpringLayout springLayout = new SpringLayout();
//          setLayout(springLayout);

            JLabel lblDescription = new JLabel(message);
//          springLayout.putConstraint(SpringLayout.NORTH, lblDescription, 5, SpringLayout.NORTH, this);
//          springLayout.putConstraint(SpringLayout.WEST, lblDescription, 5, SpringLayout.WEST, this);
//          springLayout.putConstraint(SpringLayout.EAST, lblDescription, -5, SpringLayout.EAST, this);
            add(lblDescription);
//          setPreferredSize(new Dimension(width, 65));
            setMaximumSize(new Dimension(width, ARBITRARILY_TALL));
        }

        public void launch(Component owner, int x, int y) {
            popup = PopupFactory.getSharedInstance().getPopup(owner, this, x, y);
            popup.show();
        }
    }
}

如果我编译并运行上面的代码,那么我会得到一个广泛的弹出窗口(比我要求的最大200像素宽),所有文本都在一行上。如果我取消注释所有注释行,那么我会得到所需的行为,但我必须setPreferredSize()调用中同时指定宽度和高度,因此处理不同的行为并不健全输入消息。


使用基于Andrew Thompson的解决方案进行编辑

public class PopupTest extends JFrame {
    private static final String FOX_MESSAGE = "The lithe, quick, brown fox jumps " +
                                              "happily and steadily over the fat, slobbery, " +
                                              "lazy dogs";
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    PopupTest frame = new PopupTest();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PopupTest() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        final JPanel contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        final JButton btnLaunch = new JButton("Launch popup");
        btnLaunch.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Point btnLoc = btnLaunch.getLocationOnScreen();
                FoxPopup popup = new FoxPopup(FOX_MESSAGE, 200);
                popup.launch(contentPane, btnLoc.x, btnLoc.y + btnLaunch.getHeight());              
            }
        });
        contentPane.add(btnLaunch);
    }

    public static class FoxPopup extends JPanel {
        private Popup popup;

        public FoxPopup(String message, int width) {
            setBackground(Color.pink);

            JLabel lblDescription = new JLabel(formatMessage(message, width));
            add(lblDescription);
        }

        private static String formatMessage(String message, int width) {
            return String.format("<html><body style='width: %d'>%s</body></html>", width, message);
        }

        public void launch(Component owner, int x, int y) {
            popup = PopupFactory.getSharedInstance().getPopup(owner, this, x, y);
            popup.show();
        }
    }
}

2 个答案:

答案 0 :(得分:4)

请参阅使用CSS修复宽度的FixedWidthText

答案 1 :(得分:0)

我可能会使用包装JTextArea而不是带有html的JLabel的单词,请参阅此处的JMultiLineLabel答案Java swing: Multiline labels?

至于弹出窗口的大小,我不知道除了使用FontMetrics之外的任何其他方法,尽管它并不复杂。

public class FoxPopup extends JPanel {

    private Popup popup;

    public FoxPopup(String message, int width) {
        setBackground(Color.pink);
        setLayout(new BorderLayout());

        JTextArea textArea = createMultiLineLabel(message);

        add(textArea);

        setSize(textArea, width);
    }

    private JTextArea createMultiLineLabel(String message) {
        JTextArea textArea = new JTextArea(message);
        textArea.setEditable(false);
        textArea.setCursor(null);
        textArea.setOpaque(false);
        textArea.setFocusable(false);
        textArea.setFont(UIManager.getFont("Label.font"));
        textArea.setWrapStyleWord(true);
        textArea.setLineWrap(true);
        return textArea;
    }

    private void setSize(JTextArea textArea, int width) {
        FontMetrics fm = textArea.getFontMetrics(textArea.getFont());

        int textWidth = SwingUtilities.computeStringWidth(textArea.getText());

        Dimension d = new Dimension();
        d.setSize(width, fm.getHeight() * Math.ceil((double) textWidth / (double) width));

        setPreferredSize(d);
    }

    public void launch(Component owner, int x, int y) {
        popup = PopupFactory.getSharedInstance().getPopup(owner, this, x, y);
        popup.show();
    }
}