Implementing a standard style scrollbar on a Blackberry device

by Developer on ‎07-22-2010 11:47 AM - edited on ‎09-20-2010 04:59 PM by Retired (18,107 Views)

Problem: Standard BlackBerry scrollbar leaves much to be desired.

A standard BlackBerry scrollbar (shown, for example, by a VerticalFieldManager with VERTICAL_SCROLL | VERTICAL_SCROLLBAR style) does not give the BlackBerry® device user any idea of how big is the screen height nor what part of the whole screen is above and below the visible area.  It just displays a small triangle in the bottom right corner when there is more data to be viewed below the visible window and another one in the top right corner when there is more above the screen.  Dry and not very informative.


A standard style scrollbar with a slider height giving the user some rough idea about the relationship between the visible area and the whole document and a slider position indicating what percentage of the whole document is below (and above) the currently displayed part would be much nicer and "professional-looking".

Some useful math first: A standard VerticalFieldManager (as well as any net.rim.device.api.ui.Manager) provides access to some useful bits of information:

Total Manager's height: Manager.getVirtualHeight()
Manager's visible height: Manager.getVisibleHeight()
Current scroll position: Manager.getVerticalScroll()

This information is sufficient to calculate the size and the position of the scrollbar slider:

slider height / visible height = visible height / total height gives us slider height = visible height ^ 2 / total height
slider position = scroll position * visible height / total height (on the screen; don't forget that in the associated Graphics object all positions are relative to the top left corner of the whole Manager)

Making room for the scrollbar on the right: in order to lay out the managed fields properly, we are going to invoke super.sublayout().  However, we need to make some room for our own scrollbar to be displayed on top of that.  How?  Just steal a little of the whole width before passing it to the superclass, then restore it back so that we can later draw in that area.  The managed fields will not know about that little extra space we've created, so they will never paint there!

What class to extend? looks like the most natural candidate.  If we want finer control over the layout, we might want to extend Manager directly and then implement the whole sublayout() with layoutChild() and setPositionChild() calls.  Such an approach, however, is well beyond the scope of this article and is thus left as an exercise for the reader.
A note of caution: if you use a MainScreen (or extend it) and just add this VerticalScrollManager to it, you will not see any good-looking scrollbars, but rather the default Blackberry device one.  How come?  The default MainScreen comes with a VERTICAL_SCROLL | VERTICAL_SCROLLBAR VerticalFieldManager as its delegate.  Such a manager gives all the height you want (up to 0x3FFFFFFF) to the managed fields.  Thus, the underlying VerticalScrollManager will think it's got enough room to display all fields at once.  Use MainScreen(NO_VERTICAL_SCROLL | NO_VERTICAL_SCROLLBAR) as a container.  Alternatively, take a look at the various constructors of VerticalScrollManager below: some let you restrict the maximum dimensions of the Manager.  This way you can have several of such scrolling "windows" on the screen, share the display with other fields/Managers, etc.


A few more details: I learned the hard way that adding this Manager as its own ScrollChangeListener and calling invalidate() in its scrollChanged() method is necessary - touch-screen devices with gesture scrolling can behave weird otherwise.  Also, painting the scroll slider with lighter left and top borders and darker right and bottom ones makes it look 3-D so I added this.  Remove the code after setting the TOP_SHADE_COLOR and BOTTOM_SHADE_COLOR (four drawLine calls) to make it flat.


Let's use all of the above to implement a nice-looking scrollbar:
import net.rim.device.api.ui.container.VerticalFieldManag er;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.ScrollChangeListener;
* VerticalScrollManager - a VerticalFieldManager with a nicer-looking scrollbar
* and optional width and height limits
public class VerticalScrollManager extends VerticalFieldManager implements ScrollChangeListener {
* Some constants governing the exact look - colors and size - of the scrollbar.
* Our scrollbar is a light grey bar on the right of the manager's visible area
* with a darker grey scroll slider.
private static final int SCROLLBAR_COLOR = 0xCCCCCC;
private static final int SLIDER_COLOR = 0x66666666;
private static final int TOP_SHADE_COLOR = 0xDDDDDD;
private static final int BOTTOM_SHADE_COLOR = 0x333333;
private static final int SCROLLBAR_WIDTH = 8;
private static final int SCROLLBAR_RIGHT_MARGIN = 0;
private static final int SCROLLBAR_LEFT_MARGIN = 0;

// The eventual height of the slider in pixels
private int sliderHeight;
// The eventual horizontal slider position - in this Manager's coordinates
private int sliderXPosition;
// Height and width limits - useful for creating Managers which should not take
// the whole provided area
private int maxVisibleHeight;
private int maxVisibleWidth;
// Actual height and width - set in sublayout() below
private int visibleHeight;
private int visibleWidth;
// Total (a.k.a "virtual") height of the Manager
private int totalHeight;
// Do we need to display a scrollbar?
private boolean isScrolling;

// Constructors - please observe the use of width and height limits
// Also - you do want to use VERTICAL_SCROLL but definitely not the
// default "scrollbar", thus we made that a default
public VerticalScrollManager() {

public VerticalScrollManager(int w, int h) {

public VerticalScrollManager(long style) {
this(style, Integer.MAX_VALUE, Integer.MAX_VALUE);

public VerticalScrollManager(long style, int w, int h) {
maxVisibleHeight = h;
maxVisibleWidth = w;

// This is how we report our desided height and width to the parent manager
public int getPreferredHeight() {
return visibleHeight;

public int getPreferredWidth() {
return visibleWidth;

// This is called by the framework just before displaying this Manager. At this point we are
// given the biggest rectangle within the parent Manager that our Manager is allowed to occupy
// This is the natural place to make all necessary calculations
protected void sublayout(int w, int h) {
// Initial value - no scrollbar unless VERTICAL_SCROLL is requested
isScrolling = ((getStyle() & VERTICAL_SCROLL) == VERTICAL_SCROLL);
// How much room (horizontally) do we need for the scrollbar
// Further limit the given dimensions with the requested size
visibleHeight = Math.min(h, maxVisibleHeight);
visibleWidth = Math.min(w, maxVisibleWidth);
// Before asking the parent class to layout, reserve the necessary room for the scrollbar
int myWidth = visibleWidth - scrollbarWidth;
super.sublayout(myWidth, visibleHeight);
// After the VerticalFieldManager lays out its fields, let's ask it for dimensions and
// adjust width back to include the scrollbar
visibleHeight = getHeight();
totalHeight = getVirtualHeight();
visibleWidth = getWidth() + scrollbarWidth;
// Report our proper dimensions to the parent Manager
setExtent(visibleWidth, visibleHeight);
// This is necessary for the overall BlackBerry framework to know how far we can scroll
// Especially important for touch-screen devices
setVirtualExtent(visibleWidth, totalHeight);
// Now, let's double check whether any scrollbar is needed
// If the visible area is tall enough, let's not bother
isScrolling = (visibleHeight < totalHeight);

// Finally, determine how big is the slider and where to start painting it horizontally
if (isScrolling) {
sliderHeight = visibleHeight * visibleHeight / totalHeight;
sliderHeight = Math.max(sliderHeight, 1); // show at least one pixel!
// Please observe that we reserved the width based on both left and right margin,
// but are going to paint based on right margin only - that's how we create
// that left margin
sliderXPosition = visibleWidth - SCROLLBAR_WIDTH - SCROLLBAR_RIGHT_MARGIN;

// This is called each time our Manager needs repainting (invalidate(), scrolling, etc.)
protected void paint(Graphics g) {
// First, paint the fields "normally"

// Now, add the scrollbar if necessary
if (isScrolling) {
// Determine how far have we scrolled
int scrollPosition = getVerticalScroll();
// The slider vertical position on the screen is proportional to the scroll position.
// Please observe that we add the scroll position to the calculated result since
// everything on the screen starts there. All x and y coordinates for this Graphics
// object are within the Manager's FULL (virtual) rectangle.
int sliderYPosition = scrollPosition * visibleHeight / totalHeight + scrollPosition;
// draw the scrollbar
// Again, scrollbar starts at scroll position (top of the displayed part) and
// is visibleHeight high
g.fillRect(sliderXPosition, scrollPosition, SCROLLBAR_WIDTH, visibleHeight);
// draw the slider
g.fillRect(sliderXPosition, sliderYPosition, SCROLLBAR_WIDTH, sliderHeight);
// draw the shading - make it "3-D"
if (sliderHeight > 2) {
g.drawLine(sliderXPosition, sliderYPosition, sliderXPosition + SCROLLBAR_WIDTH - 1, sliderYPosition);
g.drawLine(sliderXPosition, sliderYPosition, sliderXPosition, sliderYPosition + sliderHeight - 1);

if (sliderHeight > 2) {
g.drawLine(sliderXPosition, sliderYPosition + sliderHeight - 1, sliderXPosition + SCROLLBAR_WIDTH - 1, sliderYPosition + sliderHeight - 1);
g.drawLine(sliderXPosition + SCROLLBAR_WIDTH - 1, sliderYPosition, sliderXPosition + SCROLLBAR_WIDTH - 1, sliderYPosition + sliderHeight - 1);

public void scrollChanged(Manager mgr, int newX, int newY) {
if (mgr == this) {
invalidate(newX + sliderXPosition, newY, SCROLLBAR_WIDTH + SCROLLBAR_RIGHT_MARGIN, getVisibleHeight());