JFXStudio: sketch, hack, share

A custom virtual list

Posted by: Josh Marinacci on: June 20, 2009

JavaFX 1.2 has a new list control called ListView. ListView was designed to be virtual. This means it doesn’t actually create a text node for every item in the list. It only creates the text for the items which are visible on screen. As you scroll through the list it will destroy the old text items and create new ones as needed. This means you can have very long lists with tons of data without making the scene slow, something which is very important on mobile devices with limited resources.

The ListView has a problem though. You can’t easily customize it. The objects you put into the list are turned into a single line of text. That’s great for basic text data, but not great if you wanted to create a Twitter client where each list item has a few lines of a text, an image, and some buttons. I’ve been assured that custom item rendering will come in a future release, but there’s no reason to wait. :)

This sketch shows a reusable customizable virtual list component. It generates a sequence of 20,000 Item objects, then puts them into the CustomListView. It also assigns a function which draws each Item as two lines of text on top of a gradient. Perfect for a simple Twitter client. And because the CustomListView class is generic (it doesn’t know about the Item class), you can reuse it in other projects without having to subclass.


Click to run
com.sun.javafx.runtime.MainScreenSnapz013.png

The code below shows how to use it.


package customlisttest;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.layout.*;
import javafx.scene.*;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import javafx.scene.control.ScrollBar;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;

/*
make a list of items
each item has an image, two lines of text w/ diff fonts, and a gradient background
*/

class Item {
    public var name = "foo";
    public var status = "bar";
}

function customView(obj:Object, bounds:Bounds):Node {
    var item = obj as Item;
    return Group {
        content: [
            Rectangle { width: bounds.width height: bounds.height
                fill: LinearGradient { startX : 0.0 startY : 0.0 endX : 0.0 endY : 1.0
                        stops: [
                            Stop { color : Color.rgb(230,230,230) offset: 0.0 },
                            Stop { color : Color.rgb(210,210,210) offset: 1.0 },
                        ]
                    }
                }
            Text { content: item.name   x: 10  y: 15 font: Font.font("Helvetica", 17) }
            Text { content: item.status x: 10  y: 26 font: Font.font("Helvetica", 11) }
        ]
    }
}

var items = for(i in [0..20000]) {
    Item { name: "Bill Smitho {i}" status: "headin' down to el-rancho!" }
}

var scrollValue = 0.0;

Stage {
    title: "Application title"
    width: 500
    scene: Scene {
        height: 500
        content: [
            Rectangle { width: 400 height: 500 fill: Color.BLACK x: 20 }

            ScrollBar{
                min: 0 max: items.size() * 30 - 500 vertical: true
                height: 500
                value: bind scrollValue with inverse
                unitIncrement: 15
            }

            CustomListView {
                translateX: 20
                scrollValue: bind -scrollValue
                data: items
                width: 400 height: 500
                itemHeight: 30
                createItemNode: customView
            }
        ]

    }
}

Future work

This list works pretty well but it’s missing a few features. It doesn’t properly handle changes like resizing, changing data in the list, or scrolling horizontally. It also doesn’t extend Control and implement the proper Skinning behavior.

The CustomListView also isn’t as efficient as it could be. Currently it will regenerate the entire set of visible items when you scroll to a new item rather than just adding the one new item. It also doesn’t attempt to do any object recycling. I think this could all be added quite easily by an enterprising developer. Perhaps it will be you! :)

Source code

7 Responses to "A custom virtual list"

Its like you read my mind. I’ve been noodling around with a twitter client, and that is exactly the direction I want to take with it.

Will definitely look at it to see whats under the hood.

Thanks

Why is the scrollValue bind as negative in Main.fx and then used again with negative sign in adjust method. I’ve tried with a positive signs on both places and it works fine. Is there any reason I can’t figure out?

there’s no reason. I was just lazy. :)

Good work but I wanna understand two things:
1- could you make this CustomListView as a separate calss witouh stage.
2- how to handle selcetion and inputs.

@AMIRALe The CustomListView is already a separate class. Download the code to use it. The code does not currently handle selection and inputs, but it wouldn’t be hard to add it yourself.

Josh, I’m working this over a bit, to support changing data after-the fact, and to fix some little bugs… when I’m through with it, would you like to see it?

Absolutely. Please post it to jfxstudio.

Leave a Reply

Challenge: Small is the New Big

This month's challenge: code something cool in only 30 lines, using the theme of Five

Tags

Archives