Make a custom JScrollBar using an image
Asked Answered
H

2

6

So I use Java Swing to build the UI for my app and use custom images to replace the ugly Java ones, the custom images have a style and are very easy to integrate into Java Swing.

Right now my problem is I need to use a JScrollBar with a JScrollPane in an app and I really don't want to use either the default Java Scroll Bar or even the native OS Scroll Bar.

I just want to be able to have a custom image be the Background of the scroll bar and an image to be the Thumb of the scroll bar.

How to make a custom JScrollBar using an image?

Hung answered 4/9, 2012 at 14:33 Comment(4)
#9630159 could help you.Spook
What about the Nimbus scroll bar? Or some third-party look-and-feel?Pedant
@Pedant Look and Feels won't work since none of them conform to the UI style already implemented in the UIHung
Some of the Look and Feels are "skinnable", see this: geeknizer.com/…Pedant
S
10

I wrote an example that shows how to set a custom image for the thumb and background (called track) of a scrollbar. You need the two files thumb.png and track.png for the custom images in the same place where your class file is. I also do some scaling of the images to fit the scrollbar. Just experiment a little bit with this code. I changed the width of the scrollbar (setPreferredSize) to better see the images too.

The essential points are that you have to create your own class MyUi that extends BasicScrollBarUI and overrides paintThumb and paintTrack, and personalize your scrollbar with setUI(new MyUI):

import java.awt.*;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.*;
import javax.swing.plaf.metal.MetalScrollBarUI;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.geom.AffineTransform;


public class CustomScrollbarUIExample {
  public static void main(String[] args) {
    JScrollPane before = makeExamplePane();
    JScrollPane after = makeExamplePane();  

    JScrollBar sb=after.getVerticalScrollBar();
    sb.setPreferredSize(new Dimension(50, Integer.MAX_VALUE));
    sb.setUI(new MyScrollbarUI());

    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Container c = f.getContentPane();
    c.setLayout(new GridLayout(2, 1, 0, 1));
    c.add(before);
    c.add(after);
    f.setSize(450, 400);
    f.setVisible(true);
  }

  private static JScrollPane makeExamplePane() {
    String exampleText= "Lorem ipsum dolor sit amet,\n consetetur sadipscing elitr,\n sed diam nonumy eirmod \ntempor invidunt ut labore et dolore \nmagna aliquyam erat,\n sed diam voluptua. At vero eos et accusam et \njusto duo dolores et ea rebum. Stet clita\n kasd gubergren, no sea\n takimata sanctus est Lorem ipsum dolor sit amet.\n Lorem ipsum dolor sit amet,\n consetetur sadipscing elitr, sed diam\n nonumy eirmod tempor invidunt \nut labore et dolore\n magna aliquyam erat, sed diam voluptua.\n At vero eos et accusam et justo \nduo\n dolores et ea rebum. Stet clita kasd gubergren, no sea\n takimata sanctus est Lorem\n ipsum dolor sit amet. Lorem ipsum dolor\n sit amet, consetetur sadipscing elitr,\n sed diam nonumy\n eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos\n et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est \nLorem ipsum dolor sit amet.Duis\n autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel\n illum dolore eu feugiat nulla facilisis at vero eros et \naccumsan et iusto odio \ndignissim qui blandit praesent luptatum zzril delenit augue\n duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer\n adipiscing elit, sed diam nonummy nibh euismod \ntincidunt ut laoreet\n dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam,\n quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea\n commodo consequat. Duis autem vel eum iriure \ndolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla \nfacilisis at vero eros et accumsan et iusto odio dignissim qui blandit\n praesent luptatum zzril delenit augue duis dolore \nte feugait nulla facilisi.";
    JTextArea text = new JTextArea(exampleText);
    JScrollPane scroll = new JScrollPane(text);
    return scroll;
  }

  static class MyScrollbarUI extends MetalScrollBarUI {
    private Image imageThumb, imageTrack;
    MyScrollbarUI() {
        try {
            imageThumb = ImageIO.read(new File("thumb.png"));
            imageTrack = ImageIO.read(new File("track.png"));
        } catch (IOException e){}
    }

    @Override
    protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {        
        g.translate(thumbBounds.x, thumbBounds.y);
        g.setColor( Color.red );
        g.drawRect( 0, 0, thumbBounds.width - 2, thumbBounds.height - 1 );
        AffineTransform transform = AffineTransform.getScaleInstance((double)thumbBounds.width/imageThumb.getWidth(null),(double)thumbBounds.height/imageThumb.getHeight(null));
        ((Graphics2D)g).drawImage(imageThumb, transform, null);
        g.translate( -thumbBounds.x, -thumbBounds.y );
    }

    @Override
    protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {        
        g.translate(trackBounds.x, trackBounds.y);
        ((Graphics2D)g).drawImage(imageTrack,AffineTransform.getScaleInstance(1,(double)trackBounds.height/imageTrack.getHeight(null)),null);
        g.translate( -trackBounds.x, -trackBounds.y );
    }

  }
}

For more information consult the sourcefile MetalScrollBarUI.java.

Spook answered 4/9, 2012 at 16:32 Comment(5)
hmm, I keep getting a Null Pointer Error: Exception in thread "Thread-22" java.lang.NullPointerException at javax.swing.plaf.metal.MetalScrollBarUI.installDefaults(Unknown Source) at javax.swing.plaf.basic.BasicScrollBarUI.installUI(Unknown Source) at javax.swing.JComponent.setUI(Unknown Source) at javax.swing.JScrollBar.setUI(Unknown Source) Using a BasicScrollBarUI works but I get blanks areas in the areas where arrows on the scroll bar where...Hung
Is there maybe a way to get of the arrows? I don't Really need themHung
@Epicmaster To get rid of the arrow buttons you have to override protected JButton createDecreaseButton( int orientation ) and protected JButton createIncreaseButton( int orientation ) with JButton but = new JButton(); but.setVisible(false); return but;. Where the buttons were remains a small grey area, but I don't know how to remove this.Spook
@halex: +1 for before & after; I've added a variation here.Polytonality
This is actually the solution I went with before coming back to see what you guys did to remove the arrows: #1787386. Thanks though this really helped!!Hung
P
9

Where the buttons were remains a small grey area, but I don't know how to remove this.

Here's a variation of @halex's example that removes the buttons and lets the graphics context do the scaling. I added a faux image to make the example self-contained.

enter image description here

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.plaf.metal.MetalScrollBarUI;

/** @see https://mcmap.net/q/1601058/-make-a-custom-jscrollbar-using-an-image */
public class CustomScrollbarUIExample {

    public static void main(String[] args) {
        JScrollPane before = makeExamplePane();
        JScrollPane after = makeExamplePane();
        JScrollBar sb = after.getVerticalScrollBar();
        sb.setUI(new MyScrollbarUI());
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridLayout(0,1));
        f.add(before);
        f.add(after);
        f.pack();
        f.setSize(320, 240);
        f.setVisible(true);
    }

    private static JScrollPane makeExamplePane() {
        JTextArea text = new JTextArea(16, 16);
        text.append("Lorem ipsum dolor sit amet…");
        JScrollPane scroll = new JScrollPane(text);
        return scroll;
    }

    static class MyScrollbarUI extends MetalScrollBarUI {

        private Image imageThumb, imageTrack;
        private JButton b = new JButton() {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(0, 0);
            }

        };

        MyScrollbarUI() {
            imageThumb = FauxImage.create(32, 32, Color.blue.darker());
            imageTrack = FauxImage.create(32, 32, Color.lightGray);
        }

        @Override
        protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
            g.setColor(Color.blue);
            ((Graphics2D) g).drawImage(imageThumb,
                r.x, r.y, r.width, r.height, null);
        }

        @Override
        protected void paintTrack(Graphics g, JComponent c, Rectangle r) {
            ((Graphics2D) g).drawImage(imageTrack,
                r.x, r.y, r.width, r.height, null);
        }

        @Override
        protected JButton createDecreaseButton(int orientation) {
            return b;
        }

        @Override
        protected JButton createIncreaseButton(int orientation) {
            return b;
        }
    }

    private static class FauxImage {

        static public Image create(int w, int h, Color c) {
            BufferedImage bi = new BufferedImage(
                w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = bi.createGraphics();
            g2d.setPaint(c);
            g2d.fillRect(0, 0, w, h);
            g2d.dispose();
            return bi;
        }
    }
}
Polytonality answered 4/9, 2012 at 19:26 Comment(3)
Thank you (and +1 :)) for the tip that overriding getPreferredSize this way makes the buttons disappear.Spook
This answer is brilliant! I have only one question. How can I get this "UIManager.put("ScrollBarUI", "MyScrollbarUI");" to work? PS: Do you think it would be possible to make the ScrollBar transparent?Lorenzoloresz
Transparent is possible, for example; use put() to create your own L&F, as outlined in the article cited here.Polytonality

© 2022 - 2024 — McMap. All rights reserved.