Search code examples
javauser-interfacejslider

JSlider image behind track


I want to put an image (visualization of an audio) behind the JSlider which represents the audioplayer, the process of playing. First I tried to overwrite the paint-method of the Slider

public void paintComponent(Graphics g) {
    // Draw the previously loaded image to Component
    g.drawImage(img, 0, -100, null);
    super.paintComponent(g);
}

this worked, but the image is higher than the slider, so my next try was a JLayeredPane, where I put the JSlider above a JLabel with the image. Looks good for the first moment. But I mentioned that I need the image behind the track of the slider, not the whole slider. There is space to the left and right. Can anybody tell me a way how to calculate this space? Or the width and offset of the track to the border of the slider? This should run under Windows and MacOs, so different LookAndFeels, so I think hard coded values will not work. Example Slider with background image

Thankyou.


Solution

  • My solution for this Problem is now to overwrite the SliderUI. So this is a very special component, so it's nonrelevant that it looks the same on all LookAndFeels. It supports also jumping directly to mouse position, which is different to BasicSliderUI.

    /**
     *
     * Custom SliderUI for AudioPlayer with audioimage in background
     */
    public class AudioSliderUI extends BasicSliderUI {
    
        private BasicStroke stroke = new BasicStroke(1f, BasicStroke.CAP_ROUND, 
                BasicStroke.JOIN_ROUND, 0f);
    
        public AudioSliderUI(AudioSlider b) {
            super(b);
        }
    
        @Override
        public void paint(Graphics g, JComponent c) {
            Graphics2D g2d = (Graphics2D) g;
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                    RenderingHints.VALUE_ANTIALIAS_ON);
            super.paint(g, c);
        }
    
        @Override
        protected Dimension getThumbSize() {
            return new Dimension(2, 200);
        }
    
        @Override
        public void paintTrack(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            Stroke old = g2d.getStroke();
            g2d.setStroke(stroke);
            g2d.setPaint(Color.WHITE);
            if (slider.getOrientation() == SwingConstants.HORIZONTAL) {
                g2d.drawLine(trackRect.x, trackRect.y + trackRect.height / 2, 
                        trackRect.x + trackRect.width, trackRect.y + trackRect.height / 2);
            } else {
                g2d.drawLine(trackRect.x + trackRect.width / 2, trackRect.y, 
                        trackRect.x + trackRect.width / 2, trackRect.y + trackRect.height);
            }
            g2d.setStroke(old);
            Image img = ((AudioSlider)slider).getImage();
            g2d.drawImage(img, trackRect.x, trackRect.y, trackRect.width, trackRect.height, slider);
    
        }
    
        @Override
        public void paintThumb(Graphics g) {
            Rectangle knobBounds = thumbRect;
            int w = knobBounds.width;
            int h = 100;
            int newStarty = knobBounds.height/2- h/2;
            g.translate(knobBounds.x, knobBounds.y);
    
            //     "plain" version
            g.setColor(Color.YELLOW);
            g.fillRect(0, newStarty, w, h);
    
        }
    
        @Override
        protected TrackListener createTrackListener(JSlider slider) {
            return new TrackListener() {
                @Override
                public void mousePressed(MouseEvent e) {
                    if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag")
                            && SwingUtilities.isLeftMouseButton(e)) {
                        JSlider slider = (JSlider) e.getComponent();
                        switch (slider.getOrientation()) {
                        case SwingConstants.VERTICAL:
                            slider.setValue(valueForYPosition(e.getY()));
                            break;
                        case SwingConstants.HORIZONTAL:
                            slider.setValue(valueForXPosition(e.getX()));
                            break;
                        default:
                            throw new IllegalArgumentException(
                                    "orientation must be one of: VERTICAL, HORIZONTAL");
                        }
                        super.mousePressed(e); // isDragging = true;
                        super.mouseDragged(e);
                    } else {
                        super.mousePressed(e);
                    }
                }
    
                @Override
                public boolean shouldScroll(int direction) {
                    return false;
                }
            };
        }
    }
    

    Matching Slider:

    public class AudioSlider extends JSlider {
    
        private Image img;
    
        public AudioSlider() {
            setOpaque(false);
        }
    
        /**
         * @return the img
         */
        public Image getImage() {
            return img;
        }
    
        public void setImage(Image img) {
            this.img = img;
        }
    }
    

    Works for me, maybe covers not all prospects.