天天看点

在自定义 JToolTip 里面显示组件

在自定义 JToolTip 里面显示组件

在自定义 JToolTip 里面显示组件

我们经常可以看到很多 Windows 程序显示出各种各样好看的工具栏提示, 不是默认的那种只有文字的提示... 但是 Java Swing 默认的那个, 似乎只有那个一行文字, well, how can we change it? 请参阅: JFC -- Chapter 24 - Building a Custom Component http://mail.phys-iasi.ro/Library/Computing/jfc_unleashed/ch24.htm

笔者基于文中的介绍写出了自己的解决方案. First, 写一个自定义的 ToolTipUI 来显示 JToolTip, 实现下列逻辑:

1. 如果没有在 JToolTip 中包含子组件(还记得嘛, 所有 JComponent 都是容器), 那么按照普通的模式显示 JToolTip, 稍微改进了一点, 就是可以显示多行的文字, 例如 "a\nb" 这样的文字就被显示为两行, 而不是默认的 JToolTip 显示的单行文本.

2. 如果包含了子组件, 就忽略设置的提示文本, 转而显示里面包含的组件, 这样就实现了在 JToolTip 中显示组件的功能. 源代码如下:

import java.awt.*;

import java.util.StringTokenizer;

import javax.swing.*;

import javax.swing.plaf.ComponentUI;

import javax.swing.plaf.basic.BasicToolTipUI;

public class ComponentToolTipUI extends BasicToolTipUI

{

    static ComponentToolTipUI sharedInstance = new ComponentToolTipUI();

    static final int MARGIN = 2;

    static final String lineSeparator = "\n";

    protected ComponentToolTipUI()

    {

        super();

    }

    public static ComponentUI createUI(JComponent c)

    {

        return sharedInstance;

    }

    public void paint(Graphics g, JComponent c)

    {

  // Paints each of the components in this container.

  if(c.getComponentCount() > 0) {

          c.paintComponents(g);

          return;

  }

  // If no components, then paint a muiltiple line tooltip

  //

        // Determine the size for each row.

        //

        Font font = c.getFont();

        FontMetrics fontMetrics = c.getFontMetrics(font);

        int fontHeight = fontMetrics.getHeight();

        int fontAscent = fontMetrics.getAscent();

        //

        // Paint the background in the tip color.

        //

        g.setColor(c.getBackground());

        Dimension size = c.getSize();

        g.fillRect(0, 0, size.width, size.height);

        //

        // Paint each line in the tip using the

        // foreground color. Use a StringTokenizer

        // to parse the ToolTip. Each line is left

        // justified, and the y coordinate is updated

        // through the loop.

        //

        g.setColor(c.getForeground());

        int y = 2+fontAscent;

        String tipText = ((JToolTip)c).getTipText();

        StringTokenizer tokenizer = new StringTokenizer(tipText, lineSeparator);

        int numberOfLines = tokenizer.countTokens();

        for (int i = 0; i < numberOfLines; i++)

        {

            g.drawString(tokenizer.nextToken(), MARGIN, y);

            y += fontHeight;

        }

    }

    // paint

    public Dimension getPreferredSize(JComponent c)

    {

        // If has children components

  if(c.getComponentCount() > 0) {

         return c.getLayout().preferredLayoutSize(c);

  }

  //

        // Determine the size for each row.

        //

        Font font = c.getFont();

        FontMetrics fontMetrics = c.getFontMetrics(font);

        int fontHeight = fontMetrics.getHeight();

        //

        // Get the tip text string.

        //

        String tipText = ((JToolTip)c).getTipText();

        //

        // Empty tip, use a default size.

        //

        if (tipText == null)

            return new Dimension(2 * MARGIN, 2 * MARGIN);

        //

        // Create a StringTokenizer to parse the ToolTip.

        //

        StringTokenizer tokenizer = new StringTokenizer(tipText, lineSeparator);

        int numberOfLines = tokenizer.countTokens();

        //

        // Height is number of lines times height of a single line.

        //

        int height = numberOfLines * fontHeight;

        //

        // Width is width of longest single line.

        //

        int width = 0;

        for (int i = 0; i < numberOfLines; i++)

        {

            int thisWidth = fontMetrics.stringWidth(tokenizer.nextToken());

            width = Math.max(width, thisWidth);

        }

        //

        // Add the margin to the size, and return.

        //

        return (new Dimension(width + 2 * MARGIN, height + 2 * MARGIN));

    }

    // getPreferredSize

}

// ComponentToolTipUI

接着, 我们定义一个包含了其它组件的 JToolTip:

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class MyTooltip extends javax.swing.JToolTip

{

  JButton jButton1 = new JButton();

  FlowLayout flowLayout1 = new FlowLayout();

  JTextField jTextField1 = new JTextField();

 public MyTooltip()

    {

    try  {

      jbInit();

    }

    catch(Exception e) {

      e.printStackTrace();

    }

 }

 public void setTipText(String tipText) {

    super.setTipText(tipText);

//    button.setText(tipText);

 }

  private void jbInit() throws Exception {

//    jLabel1.setMaximumSize(new Dimension(400, 300));

//    jLabel1.setMinimumSize(new Dimension(400, 300));

//    jLabel1.setPreferredSize(new Dimension(400, 300));

    jButton1.setText("This is a button included in the tool tip.");

    this.setLayout(flowLayout1);

    this.setOpaque(true);

    jTextField1.setText("jTextField1");

    this.add(jButton1, null);

    this.add(jTextField1, null);

  }

}

最后, 我们编写一个测试类来测试这个 JToolTip:

import java.awt.event.*;

import javax.swing.*;

public class MyButton extends javax.swing.JButton

{

 public MyButton()

    {

 }

 public JToolTip createToolTip() {

  JToolTip tip = new MyTooltip();

  tip.setComponent(this);

  tip.setTipText(getToolTipText());

  return tip;

 }

 public static void main(String[] args)

    {

  try {

      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

  } catch (Exception ex) {

    ex.printStackTrace();

  }

        try

        {

            String multiLineToolTipUIClassName =

            "ComponentToolTipUI";

//            System.out.println(multiLineToolTipUIClassName);

            UIManager.put( "ToolTipUI", multiLineToolTipUIClassName );

            UIManager.put( multiLineToolTipUIClassName,

            Class.forName( multiLineToolTipUIClassName ) );

        }

        catch( ClassNotFoundException cnfe )

        {

            System.err.println( "MultiLine ToolTip UI class not found" );

            System.err.println( cnfe );

        }

  final JFrame frame = new JFrame("Test JToolTip");

  frame.addWindowListener(new java.awt.event.WindowAdapter() {

         public void windowClosing(WindowEvent e) {

    frame.dispose();

    System.exit(0);

   }

  });

  MyButton button = new MyButton();

  button.setText("这个按钮显示一个包含组件的工具提示");

  button.setToolTipText("提示");

  frame.getContentPane().add(button);

  JButton buttonPlain = new JButton("这是一个包含多行文本提示的普通按钮");

  buttonPlain.setToolTipText("Line 1\nLine 2\n");

  frame.getContentPane().add(java.awt.BorderLayout.SOUTH, buttonPlain);

//  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  frame.pack();

  frame.show();

 }

}

编译并运行最后一个类: MyButton, 就可以看到包含了可以交互的按钮和文本框, 以及一个显示两行文本的工具栏提示。