Having different tooltips for each tick on a JSlider

This example shows you how to create your own custom (Metal) UI which overrides some paintLabel methods to save the x, y location of each label. It also overrides the JSlider component to add an extra method setToolTipTextAt that allows you to set a tooltip for a particular label. It overrides the getToolTipText method to determine if the mouse is located on top of one of the labels.

Main.java:

import javax.swing.plaf.metal.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
   
public class Main extends JFrame {
   public Main() {
      getContentPane().setLayout(new FlowLayout());
 
      final TTJSlider slider = new TTJSlider(JSlider.HORIZONTAL, 0, 30, 15);
      slider.setToolTipTextAt(0, "zero");
      slider.setToolTipTextAt(10, "ten");
      slider.setToolTipTextAt(20, "twenty");
      slider.setToolTipTextAt(30, "thirty");
 
      // draw the major tick marks (one for every tick label)
      slider.setMajorTickSpacing(10);
      // draw the minor tick marks (between the tick labels)
      slider.setMinorTickSpacing(1);
      // draw the tick marks
      slider.setPaintTicks(true);
      // draw the tick mark labels
      slider.setPaintLabels(true);
  
      slider.setToolTipText("rest of JSlider tooltip");
 
      getContentPane().add(slider);
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent event) {
            System.exit(0);   
         }      
      });
 
      pack();
   }
 
   public static void main(String[] args) {
      (new Main()).show();
   }
}
 
class TTJSlider extends JSlider
{
   private Hashtable htToolTips = new Hashtable();
   private int lastX = 0;
   private int lastY = 0;
   // for faster access to our own UI
   TTMetalSliderUI ttUI;
 
   public TTJSlider(int orientation, int min, int max, int value) {
      super(orientation, min, max, value);
      addMouseMotionListener(new MouseMotionAdapter() {
         public void mouseMoved(MouseEvent event) {
            if (event != null)  {
               lastX = event.getX();
               lastY = event.getY();
            }
         }
      });
      ttUI = new TTMetalSliderUI();
      setUI(ttUI);
   }
 
   public void setToolTipTextAt(int index, String toolTipText) {
      htToolTips.put(new Integer(index), toolTipText);
   }
 
   public String getToolTipText() {
      int value = ttUI.getLabelForLocation(lastX, lastY);
 
      String toolTipText = (String) htToolTips.get(new Integer(value));
      if (toolTipText == null) 
         return super.getToolTipText();
      else
         return toolTipText;
   }
   
}
 
class TTMetalSliderUI extends MetalSliderUI
{
   // contains label - rectangle pair
   Hashtable labelRects = null;
  
   public void paintLabels(Graphics g) {
      labelRects = new Hashtable();
 
      super.paintLabels(g);
   }
 
   protected void paintHorizontalLabel(Graphics g, int value, Component label) { 
      int labelCenter = xPositionForValue( value );
      int labelLeft = labelCenter - (label.getPreferredSize().width / 2);
 
      Rectangle r = new Rectangle(
         labelLeft, 
         labelRect.y, 
         label.getPreferredSize().width,
         label.getPreferredSize().height);
 
      labelRects.put(r, new Integer(value));
 
      super.paintHorizontalLabel(g, value, label);
   }
 
   protected void paintVerticalLabel(Graphics g, int value, Component label) {
      int labelCenter = yPositionForValue(value);
      int labelTop = labelCenter - (label.getPreferredSize().height / 2);
 
      Rectangle r = new Rectangle(
         labelRect.x, 
         labelTop, 
         label.getPreferredSize().width,
         label.getPreferredSize().height);
 
      labelRects.put(r, new Integer(value));
 
      super.paintVerticalLabel(g, value, label);
   }
  
   public int getLabelForLocation(int x, int y) {
      Enumeration enum = labelRects.keys();
      while (enum.hasMoreElements()) {
         Rectangle r = (Rectangle) enum.nextElement(); 
         if (r.contains(x, y)) return ((Integer) labelRects.get(r)).intValue();
      }
 
      return -1;
   }
}