利用层实现ToolTip,利用图形组合实现不规则的ToolTip。这里源码来自五斗米
先看效果:
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Point; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Area; import java.awt.geom.RoundRectangle2D; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.text.View; import sun.swing.SwingUtilities2; /** * 用层来模拟ToolTip的部分功能,可以做出非规则形状 */ public class Test { private JFrame frame = null; private Box box = null; // 为了将MyButton放到一个合适的位置,采用这个比较麻烦的Box来管理 private MyButton button = null; public Test() { frame = new JFrame("MyToolTip"); box = Box.createVerticalBox(); button = new MyButton("Button"); button.setMyToolTip(new MyToolTip(frame.getLayeredPane(), "向Java战友问好!")); box.add(Box.createVerticalGlue()); Box box_ = Box.createHorizontalBox(); box_.add(Box.createHorizontalGlue()); box_.add(button); box_.add(Box.createHorizontalGlue()); box.add(box_); box.add(Box.createVerticalGlue()); frame.getContentPane().add(box, BorderLayout.CENTER); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(360, 220); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String args[]) { new Test(); } class MyButton extends JButton implements MouseListener, ActionListener { private static final long serialVersionUID = -6373246716080645309L; private Timer timer = null; private MyToolTip tip = null; // JButton的构造很多,都可以适用 public MyButton(String text) { super(text); timer = new Timer(500, this); // 构造定时器,这里使用的是javax.swing.Timer this.addMouseListener(this); } // 设置MyToolTip public void setMyToolTip(MyToolTip tip) { this.tip = tip; } public MyToolTip getMyToolTip() { return tip; } @Override public void mouseClicked(MouseEvent e) { } // 当鼠标进入时显示,也可以加一个定时器来延时显示 @Override public void mouseEntered(MouseEvent e) { timer.start(); // 启动定时器 } // 鼠标离开即消失 @Override public void mouseExited(MouseEvent e) { tip.setVisible(false); timer.stop(); } @Override public void mousePressed(MouseEvent e) { } @Override public void mouseReleased(MouseEvent e) { } @Override public void actionPerformed(ActionEvent e) { timer.stop(); Point p = getLocationOnScreen(); /**设置显示的位置*/ SwingUtilities.convertPointFromScreen(p, frame.getContentPane()); tip.setLocation(new Point(p.x - tip.getWidth() + 10, p.y - tip.getHeight() + 10)); tip.setVisible(true); } } /** * 继承JPanel,因为可以设置透明,所以我们很容易就可以做出不规则形状来。 * <br> 但是很遗憾,它的缺点是位置不能超出JFrame,否则超出部分无法显示 */ class MyToolTip extends JPanel { private static final long serialVersionUID = -1405474493135741335L; private String text = null; // 将要显示的字符串 private JLayeredPane lp = null; // 我们要用到的层 public MyToolTip(JLayeredPane lp, String text) { this.lp = lp; this.text = text; this.setOpaque(false); // 将组件放在弹出层中,这样就可以浮现在其它组件之上 lp.add(this, new Integer(JLayeredPane.POPUP_LAYER)); this.setSize(this.getPreferredSize()); this.setVisible(false); // 设置组件不可视 } public void setText(String text) { this.text = text; this.setSize(this.getPreferredSize()); // 改变文字后需要重新计算Size } public String getText() { return text; } public JLayeredPane getLp() { return lp; } public void setLp(JLayeredPane lp) { this.lp = lp; } /** * 画背景和文字 */ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(new Color(205, 235, 235)); // 背景颜色 g2d.fill(this.getArea(this.getSize())); g2d.setColor(new Color(40, 130, 180)); // 文字颜色 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g2d.drawString(text, 25 / 2, (getHeight() - 10) / 2 + 5); } // 画边框 @Override protected void paintBorder(Graphics g) { super.paintBorder(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(new Color(95, 145, 145)); // 边线颜色 g2d.draw(this.getArea(this.getSize())); } /** * 返回适合的Size */ @Override public Dimension getPreferredSize() { Font font = getFont(); FontMetrics fm = getFontMetrics(font); Insets insets = getInsets(); Dimension prefSize = new Dimension(insets.left + insets.right, insets.top + insets.bottom); if ((text == null) || text.equals("")) { text = ""; } else { View v = (this != null) ? (View) getClientProperty("html") : null; if (v != null) { prefSize.width += (int) v.getPreferredSpan(View.X_AXIS); prefSize.height += (int) v.getPreferredSpan(View.Y_AXIS); } else { prefSize.width += SwingUtilities2.stringWidth(this, fm, text) + 25; // 25为多加的部分 prefSize.height += fm.getHeight() + 10; // 10为多加的部分 } } return prefSize; } /** * 返回画图所需要的区域<br> * 这里主要用到了图形合并共能。通过图形合并我们可以实现各种自定义的图形 * @param dim * @return */ private Area getArea(Dimension dim) { Shape r = new RoundRectangle2D.Float(0, 0, dim.width - 1, dim.height - 10, 5, 5); // 圆角矩形 Area area = new Area(r); Polygon polygon = new Polygon(); // 多边形 polygon.addPoint(dim.width - 15, dim.height - 10); polygon.addPoint(dim.width - 5, dim.height - 10); polygon.addPoint(dim.width, dim.height); area.add(new Area(polygon)); // 合并图形 return area; } } }