Painting the slider icon of JSlider

There are three ways:

  1. change Java Look and Feel, Preferred of Ways
  2. OverRide XxxSliderUI, but that would be Look and Feel sensitive, and not easy way
  3. learning The Synth Look and Feel

Example using Synth

Synth slider

SynthSliderTest.java

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.synth.*;

public class SynthSliderTest {

    private JFrame f = new JFrame();

    public SynthSliderTest() {
        try {
            SynthLookAndFeel laf = new SynthLookAndFeel();
            laf.load(SynthSliderTest.class.getResourceAsStream("yourPathTo/demo.xml"), SynthSliderTest.class);
            UIManager.setLookAndFeel(laf);
        } catch (Exception e) {
            e.printStackTrace();
        }
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.getContentPane().add(makeUI());
        f.setSize(320, 240);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public JComponent makeUI() {
        JSlider slider = new JSlider(0, 100);
        JPanel p = new JPanel();
        p.add(slider);
        return p;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                SynthSliderTest synthSliderTest = new SynthSliderTest();
            }
        });
    }
}

demo.xml file

<?xml version="1.0" encoding="UTF-8"?>

<synth>
    <style id="backingStyle">
        <opaque value="TRUE"/>
        <font name="Dialog" size="12"/>
        <state>
            <color value="WHITE" type="BACKGROUND"/>
            <color value="BLACK" type="FOREGROUND"/>
        </state>
    </style>
    <bind style="backingStyle" type="region" key=".*"/>

    <style id="SliderTrackStyle">
        <opaque value="TRUE"/>
        <state>
            <color type="BACKGROUND" value="ORANGE"/>
        </state>
    </style>
    <bind style="SliderTrackStyle" type="region" key="SliderTrack" />

    <style id="SliderThumbStyle">
        <opaque value="TRUE"/>
        <state>
            <color type="BACKGROUND" value="RED"/>
        </state>
        <state value="PRESSED">
            <color type="BACKGROUND" value="GREEN"/>
        </state>
    <!-- state value="MOUSE_OVER">
      <color type="BACKGROUND" value="BLUE"/>
    </state -->
    </style>
    <bind style="SliderThumbStyle" type="region" key="SliderThumb" />
</synth>

Extending the BasicSliderUI delegate is not without peril, but it does allow arbitrary control over the rendering, as suggested in the example below.

BasicSliderUI subclass image

slider.setUI(new MySliderUI(slider));
...
private static class MySliderUI extends BasicSliderUI {

    private static float[] fracs = {0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
    private LinearGradientPaint p;

    public MySliderUI(JSlider slider) {
        super(slider);
    }

    @Override
    public void paintTrack(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        Rectangle t = trackRect;
        Point2D start = new Point2D.Float(t.x, t.y);
        Point2D end = new Point2D.Float(t.width, t.height);
        Color[] colors = {Color.magenta, Color.blue, Color.cyan,
            Color.green, Color.yellow, Color.red};
        p = new LinearGradientPaint(start, end, fracs, colors);
        g2d.setPaint(p);
        g2d.fillRect(t.x, t.y, t.width, t.height);
    }

    @Override
    public void paintThumb(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        Rectangle t = thumbRect;
        g2d.setColor(Color.black);
        int tw2 = t.width / 2;
        g2d.drawLine(t.x, t.y, t.x + t.width - 1, t.y);
        g2d.drawLine(t.x, t.y, t.x + tw2, t.y + t.height);
        g2d.drawLine(t.x + t.width - 1, t.y, t.x + tw2, t.y + t.height);
    }
}

Expanding on TrashGod's very helpful answer, I added in a few lines of code to allow the bar to be filled up to the point in which you have moved it. The rest of the bar will be blank:

enter image description here

The code to do this is:

@Override
public void paintTrack(Graphics g) {

    // ... TrashGod's code ...

    // calculate how much of the progress bar to fill
    double percentage = (double)slider.getValue()/(double)slider.getMaximum();

    // fill the progress bar with a rectange of that size, (with curved corners of 4px diameter)
    g2d.fillRoundRect(t.x, t.y, (int)(t.width*percentage), t.height, 4, 4);

    // ...

}

And if you want a background color as well, paint a second rectange before you paint the first:

enter image description here

    // ... TrashGod's code ...

    // calculate how much of the progress bar to fill
    double percentage = (double)slider.getValue()/(double)slider.getMaximum();

    // PAINT THE BACKGROUND
    // create a gradient paint for the background
    p = new LinearGradientPaint(start, end, new float[] {0.4f,0.8f}, new Color[] {Color.gray.brighter(), Color.gray.brighter().brighter()});
    g2d.setPaint(p);
    g2d.fillRoundRect((int)(t.width*percentage), t.y, t.width - (int)(t.width*percentage), t.height, 4, 4);

    // PAINT THE FOREGROUND
    // create the gradient paint
    p = new LinearGradientPaint(start, end, fracs, colors);
    g2d.setPaint(p);

    // fill the progress bar with a rectange of that size, (with curved corners of 4px diameter)
    g2d.fillRoundRect(t.x, t.y, (int)(t.width*percentage), t.height, 4, 4);