Implementing a Scroll View in JavaFX

August 30th, 2009 by Martin Leave a reply »

JavaFX does not seem to have support for scroll views. So, you have to
implement it on your own. Here is how I started implementing my own scroll view supporting
vertical scrolling:

package scrollviewdemo;

import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ScrollBar;
import javafx.util.Math;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Paint;

public class ScrollView extends CustomNode {
    public-init var node: Node;
    public var width: Float;
    public var height: Float;

    var group: Group;
    var gap = bind group.layoutBounds.height - height;

    def scrollBar: ScrollBar = ScrollBar {
        translateX: bind width - scrollBar.width
        min: 0
        max: bind Math.max(0, gap)
        visible: bind gap > 0
        vertical: true
        height: bind height
    }

    override function create() {
        Group {
            content: [
                Group {
                    content: [
                        group = Group {
                            translateY: bind - scrollBar.value
                            content: [node]
                        }
                    ]
                    clip: Rectangle {
                        width: bind width - {if (gap > 0) scrollBar.width else 0}
                        height: bind height
                    }
                },
                scrollBar
            ]
        }
    }
}

This is how it can be used in a simple application:

var stage: Stage = Stage {
    title: "Application title"
    width: 200
    height: 200
    scene: Scene {
        content: [
            ScrollView {
                width: bind stage.scene.width
                height: bind stage.scene.height
                node: VBox {
                    content: for (i in [0..20]) ImageView {
                        image: Image {
                            // some picture
                            url: "{__DIR__}pic.png"
                        }
                    }
                }
            }
        ]
    }
}

To support mouse scroll wheel I added the following to the ScrollView class:

    override var onMouseWheelMoved = function(event) {
        // multiplying by 4 makes the scrolling faster and still smooth
        scrollBar.value += event.wheelRotation * 4;
    }

However, as it turned out, transparent components don’t receive mouse events, so the mouse
wheel worked only when the mouse pointer was on top of a non-transparent object. So, to work
around this, I had to fill the scroll view background using an opaque rectangle. The resulting
ScrollView component looks like this:

package scrollviewdemo;

import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ScrollBar;
import javafx.util.Math;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Paint;

public class ScrollView extends CustomNode {
    public-init var node: Node;
    public var width: Float;
    public var height: Float;
    public var fill: Paint;

    var group: Group;
    var gap = bind group.layoutBounds.height - height;

    def scrollBar: ScrollBar = ScrollBar {
        translateX: bind width - scrollBar.width
        min: 0
        max: bind Math.max(0, gap)
        visible: bind gap > 0
        vertical: true
        height: bind height
    }

    override function create() {
        Group {
            content: [
                Rectangle {
                    fill: bind fill
                    width: bind width - {if (gap > 0) scrollBar.width else 0}
                    height: bind height
                },
                Group {
                    content: [
                        group = Group {
                            translateY: bind - scrollBar.value
                            content: [node]
                        }
                    ]
                    clip: Rectangle {
                        width: bind width - {if (gap > 0) scrollBar.width else 0}
                        height: bind height
                    }
                },
                scrollBar
            ]
        }
    }

    override var onMouseWheelMoved = function(event) {
        // multiplying by 4 makes the scrolling faster and still smooth
        scrollBar.value += event.wheelRotation * 4;
    }
}

And the following line should be added to the Main.fx to make it work:

package scrollviewdemo;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;

var stage: Stage = Stage {
    title: "Application title"
    width: 200
    height: 200
    scene: Scene {
        content: [
            ScrollView {
                width: bind stage.scene.width
                height: bind stage.scene.height
                fill: Color.WHITE
                node: VBox {
                    content: for (i in [0..20]) ImageView {
                        image: Image {
                            // some picture
                            url: "{__DIR__}pic.png"
                        }
                    }
                }
            }
        ]
    }
}

6 comments

  1. Jim Weaver says:

    Nice work, Martin. You might want to look at the javafx.scene.layout.ClipView class as well.

    Thanks,
    Jim Weaver

  2. Андрей Наумов says:

    add please coordinates X,Y

    public class ScrollView extends CustomNode {

    public var x:Float; //<— add
    public var y:Float; //<— add

    override function create() {
    Group {translateX: bind x translateY:bind y //<— add

    __________
    Thanks!

  3. mr.naasso says:

    i was wondering reusing the Swing ScrollPanes was no option?
    AFAIK should be possible to use Swing Components via SwingComponent.wrap(new JComponent) or so.

    But ofc kudos to your work. good to see some usefull programming on javaFX instead of some flashy nothing :-)

  4. public says:

    Thanks a lot!
    Its great

  5. Amazing, I’m exchanging my Sun’s blog interest by this one.

  6. Sonya Michel says:

    hi! thanks a lot !! its amazing!!
    actually , i’m implementing a mobile aplication .. i’m trying to use this code but i block because i can’t move the scrollBar from the keybord :(((
    would you pleaz help me .. it would be nice

Leave a Reply