Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
New Developer
rboatright
Posts: 19
Registered: ‎04-09-2009
My Device: Not Specified

Grid / table / spreadsheet layout manager

One of the first challenges we faced when attempting to port our data-collection software to the Blackberry was the need to do table style layout.  In other languages we use, this is refered to as Grid layout.  Some folks call it spreadsheet like layout.

 

We noticed over and over requests for help with such in these and other forums, most of the replies to which suggested embedding a Horizontal Field manager for each row, into an overall Vertical field manager.  


This solution sucks because navigation fails utterly,  If you're on the seventh field in, and you attempt to move up or down, you end up on the first or last field of the next or previous row instead of on the field just above where you were. 

 

Eventually, searching turned up GridFieldManager by AnthonyRizk at http://www.thinkingblackberry.com.   Anthony's solution is an elegant and simple grid field manager which makes setting up tables VERY VERY SIMPLE. 

 

We needed to modify it a bit, because on his version, the top row  did not wrap to the bottom line if you scroll up, neither did the bottom line wrap to the top. 

 

Additionally, we modified the manager so that if you scroll right from the rightmost column, you end up on column one of the row YOU WERE ON rather than on column one of the next row.   Anthony's version "spirals down." 

 

So, attached is our version of Anthony's GridFieldManager.  I hope it helps someone.  If you want to see the original, you can find it at http://www.thinkingblackberry.com. 

 

Rick B. 

TBC Software


/** * GridFieldManager.java * Version 1.1 * * Copyright 2008 by Anthony Rizk * http://www.thinkingblackberry.com * modified 2009 by TBC Software * http://www.vocshop.com * * Changes: * V1.1 - added constructor with column widths * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License (LGPL) as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package B4Data; import net.rim.device.api.ui.Field; import net.rim.device.api.ui.Manager; import net.rim.device.api.ui.component.*; import net.rim.device.api.math.*; import net.rim.device.api.ui.*; public class GridFieldManager extends Manager { private int[] columnWidths; private int columns; private int allRowHeight = -1; public static B4Data _app = ( B4Data ) UiApplication.getUiApplication(); /** * Constructs a new GridFieldManager with the specified number of columns. * Rows will be added as needed to display fields. * Fields will be added to the grid in the order they are added to this manager, * filling up each row left-to-right: * * For example a 2 column manager: * * [Field1][Field2] * [Field3][Field4] * [Field5] * * There are two constructors available: * In the first, * Column widths are all equal, and the manager will attempt to use all available width. * The height of each row will be equal to the height of the tallest Field in that row. * Field positional styles are respected, so fields that are smaller than the row/column * they are in can be positioned left, right, top bottom, or centered. They default to top left. * * @param columns Number of columns in the grid * @param style */ public GridFieldManager ( int columns, long style ) { super ( style ); this.columns = columns; } /* * In the second constructor, instead of passing a single int with the number of columns, * an array of ints is passed with the width (in pixels) of each column. The grid will be * constructed with as many columns as required to comply with the array, and the total width * of the grid __MAY__ be wider than the screen. If so, the grid needs to be contained within * a horizontally scrolling screen or manager. */ public GridFieldManager ( int[] columnWidths, long style ) { super ( style ); this.columnWidths = columnWidths; this.columns = columnWidths.length; } public GridFieldManager ( int[] columnWidths, int rowHeight, long style ) { this ( columnWidths, style ); this.allRowHeight = rowHeight; } //jk Override to eliminate menu choices //by default, we preffer to explicitly create menus rather than let the default // blackberry menus come through. Change this as you will. public void makeMenu ( Menu menu, int instance ) { } protected boolean navigationMovement ( int dx, int dy, int status, int time ) { int focusIndex = getFieldWithFocusIndex(); /* * in each while, after we have moved into the new field, update the display at top of screen * right side of a row wraps to that row's left. Top wraps to the bottom, bottom wraps to the * top. The default blackberry movement of "spiral down" using the right arrow or trackwheel * is overridden. while ( dy > 0 ) { focusIndex += columns; int lnFieldCount = getFieldCount(); if ( focusIndex >= lnFieldCount ) { if (lnFieldCount > 0) { //Wrap around to top row getField( (whichfield - 1) ).setFocus(); return true; } else { return false; // Focus moves out of this manager } } else { Field f = getField ( focusIndex ); if ( f.isFocusable() ) { // Only move the focus onto focusable fields f.setFocus(); dy--; //UpdateDisplay(); } } } while ( dy < 0 ) { focusIndex -= columns; if ( focusIndex < 0 ) { //Wrap around to bottom row int lnFieldCount = getFieldCount(); int lnIndex = ( lnFieldCount - B4Data.FIELDCOUNT ) + ( whichfield - 1 ); getField(lnIndex).setFocus(); return true; } else { Field f = getField ( focusIndex ); if ( f.isFocusable() ) { f.setFocus(); dy++; //UpdateDisplay(); } } } while ( dx > 0 ) { int lnCount = B4Data.FIELDCOUNT ; if ( whichfield == ( lnCount ) ) { //Wrap around to beginning of line focusIndex = focusIndex - 11; } else { focusIndex ++; } int lnFieldCount = getFieldCount(); if ( focusIndex >= lnFieldCount ) { if (lnFieldCount > 0) { getField(1).setFocus(); return true; } else { return false; // Focus moves out of this manager } } else { Field f = getField ( focusIndex ); if ( f.isFocusable() ) { f.setFocus(); dx--; // UpdateDisplay(); } } } while ( dx < 0 ) { if ( whichfield == 2 ) { //Wrap around to end of line focusIndex = focusIndex + 12; } else { focusIndex --; } if ( focusIndex < 0 ) { return false; } else { Field f = getField ( focusIndex ); if ( f.isFocusable() ) { f.setFocus(); dx++; // UpdateDisplay(); } } } return true; } protected void sublayout ( int width, int height ) { int y = 0; if ( columnWidths == null ) { columnWidths = new int[columns]; for ( int i = 0; i < columns; i++ ) { columnWidths[i] = width / columns; } } Field[] fields = new Field[columnWidths.length]; int currentColumn = 0; int rowHeight = 0; for ( int i = 0; i < getFieldCount(); i++ ) { fields[currentColumn] = getField ( i ); layoutChild ( fields[currentColumn], columnWidths[currentColumn], height - y ); if ( fields[currentColumn].getHeight() > rowHeight ) { rowHeight = fields[currentColumn].getHeight(); } currentColumn++; if ( currentColumn == columnWidths.length || i == getFieldCount() - 1 ) { int x = 0; if ( this.allRowHeight >= 0 ) { rowHeight = this.allRowHeight; } for ( int c = 0; c < currentColumn; c++ ) { long fieldStyle = fields[c].getStyle(); int fieldXOffset = 0; long fieldHalign = fieldStyle & Field.FIELD_HALIGN_MASK; if ( fieldHalign == Field.FIELD_RIGHT ) { fieldXOffset = columnWidths[c] - fields[c].getWidth(); } else if ( fieldHalign == Field.FIELD_HCENTER ) { fieldXOffset = ( columnWidths[c] - fields[c].getWidth() ) / 2; } int fieldYOffset = 0; long fieldValign = fieldStyle & Field.FIELD_VALIGN_MASK; if ( fieldValign == Field.FIELD_BOTTOM ) { fieldYOffset = rowHeight - fields[c].getHeight(); } else if ( fieldValign == Field.FIELD_VCENTER ) { fieldYOffset = ( rowHeight - fields[c].getHeight() ) / 2; } setPositionChild ( fields[c], x + fieldXOffset, y + fieldYOffset ); x += columnWidths[c]; } currentColumn = 0; y += rowHeight; } if ( y >= height ) { break; } } int totalWidth = 0; for ( int i = 0; i < columnWidths.length; i++ ) { totalWidth += columnWidths[i]; } setExtent ( totalWidth, Math.min ( y, height ) ); } }

 

 

 

 

New Developer
davidvthokie
Posts: 1
Registered: ‎06-28-2009
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager

Thanks for the tip.  I was looking at it, and I don't see a definition for B4Data or a declaration for 'whichfield'.  Am I missing something?
Developer
konic
Posts: 173
Registered: ‎06-11-2009
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager


davidvthokie wrote:
Thanks for the tip.  I was looking at it, and I don't see a definition for B4Data or a declaration for 'whichfield'.  Am I missing something?

Here is the source site: http://www.thinkingblackberry.com/page/2

New Developer
eborisow
Posts: 8
Registered: ‎05-12-2009
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager

Rick,

 

Do you have the actual source code available?  The code that is pasted here does not compile because of a broken comment.  Then, once you fix that, you receive additional compile errors.  The source code mentioned by konic is a link back to the original author's source code which is ok.  But, this modified version will not compile.

 

     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:39: cannot find symbol
     [rapc] symbol  : class B4Data
     [rapc] location: class B4Data.GridFieldManager
     [rapc]     public static B4Data _app = ( B4Data ) UiApplication.getUiApplication();
     [rapc]                   ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:39: cannot find symbol
     [rapc] symbol  : class B4Data
     [rapc] location: class B4Data.GridFieldManager
     [rapc]     public static B4Data _app = ( B4Data ) UiApplication.getUiApplication();
     [rapc]                                   ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:111: cannot find symbol
     [rapc] symbol  : variable whichfield
     [rapc] location: class B4Data.GridFieldManager
     [rapc]                 getField( (whichfield - 1) ).setFocus();
     [rapc]                            ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:133: cannot find symbol
     [rapc] symbol  : variable B4Data
     [rapc] location: class B4Data.GridFieldManager
     [rapc]               int lnIndex = ( lnFieldCount -  B4Data.FIELDCOUNT ) +  ( whichfield - 1 );

     [rapc]                                               ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:133: cannot find symbol
     [rapc] symbol  : variable whichfield
     [rapc] location: class B4Data.GridFieldManager
     [rapc]               int lnIndex = ( lnFieldCount -  B4Data.FIELDCOUNT ) +  ( whichfield - 1 );

     [rapc]                                                                        ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:148: cannot find symbol
     [rapc] symbol  : variable B4Data
     [rapc] location: class B4Data.GridFieldManager
     [rapc]             int lnCount = B4Data.FIELDCOUNT ;
     [rapc]                           ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:149: cannot find symbol
     [rapc] symbol  : variable whichfield
     [rapc] location: class B4Data.GridFieldManager
     [rapc]             if ( whichfield == ( lnCount ) ) {
     [rapc]                  ^
     [rapc] C:\project\src\B4Data\GridFieldManager.jav
a:177: cannot find symbol
     [rapc] symbol  : variable whichfield
     [rapc] location: class B4Data.GridFieldManager
     [rapc]             if ( whichfield == 2 ) {
     [rapc]                  ^
     [rapc] Note: Some input files use or override a deprecated API.
     [rapc] Note: Recompile with -Xlint:deprecation for details.
     [rapc] 8 errors

 

Thanks,

Eric

Contributor
ArjunDhar
Posts: 11
Registered: ‎07-13-2010
My Device: 9000 Bold

Re: Grid / table / spreadsheet layout manager

[ Edited ]

Hi,

 I adapted the original code to suit my needs; and also fixed the compile time bugs in the post above.

Here is the code that I modified to:

package com.arjundhar.learning.bberry.managers;

import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.Menu;

public class GridFieldManager extends Manager {
    private int[] columnWidths;
    private int columns;
    private int allRowHeight = -1;

    /**
     * Constructs a new GridFieldManager with the specified number of columns.
     * Rows will be added as needed to display fields.
     * Fields will be added to the grid in the order they are added to this manager,
     * filling up each row left-to-right:
     *
     * For example a 2 column manager:
     *
     * [Field1][Field2]
     * [Field3][Field4]
     * [Field5]
     *
     * There are two constructors available: 
     * In the first, 
     * Column widths are all equal, and the manager will attempt to use all available width.
     * The height of each row will be equal to the height of the tallest Field in that row.
     * Field positional styles are respected, so fields that are smaller than the row/column
     * they are in can be positioned left, right, top bottom, or centered.  They default to top left.
     *
     * @param columns Number of columns in the grid
     * @param style
     * 
     * 
     * @author modified 2010 by Arjun Dhar<br />
     * @author modified 2009 by TBC Software http://www.vocshop.com
     * @author Copyright 2008 by Anthony Rizk - http://www.thinkingblackberry.com
     */
    public GridFieldManager ( int columns, long style ) {
        super ( style );
        this.columns = columns;
    }
	/*
	* In the second constructor, instead of passing a single int with the number  of columns, 
	* an array of ints is passed with the width (in pixels) of each column.  The grid will be 
	* constructed with as many columns as required to comply with the array, and the total width 
	* of the grid __MAY__ be wider than the screen.   If so, the grid needs to be contained within 
	* a horizontally scrolling screen or manager.
	*/

    public GridFieldManager ( int[] columnWidths, long style ) {
        super ( style );
        this.columnWidths = columnWidths;
        this.columns = columnWidths.length;
    }

    public GridFieldManager ( int[] columnWidths, int rowHeight, long style ) {
        this ( columnWidths, style );
        this.allRowHeight  = rowHeight;
    }


    //jk Override to eliminate menu choices
    //by default, we preffer to explicitly create menus rather than let the default 
    // blackberry menus come through.  Change this as you will.
    public void makeMenu ( Menu menu, int instance ) {
    }

    protected boolean navigationMovement ( int dx, int dy, int status, int time ) {
        
        int focusIndex = getFieldWithFocusIndex();

        /*
        * in each while, after we have moved into the new field, update the display at top of screen
        * right side of a row wraps to that row's left.  Top wraps to the bottom,  bottom wraps to the 
        * top.  The default blackberry movement of "spiral down" using the right arrow or trackwheel 
        * is overridden. */

        int lnFieldCount = getFieldCount();
        while ( dy > 0 ) {
            focusIndex += columns;
                      
            if ( focusIndex >= lnFieldCount ) {
              if (lnFieldCount > 0) {
                //Wrap around to top row
            	  getField(lnFieldCount-1).setFocus();
                return true;
              } else {
                return false; // Focus moves out of this manager
              }
            } else {
                Field f = getField ( focusIndex );

                if ( f.isFocusable() ) { // Only move the focus onto focusable fields
                    f.setFocus();
                    dy--;
                    //UpdateDisplay();
                }
            }
        }

        while ( dy < 0 ) {
            focusIndex -= columns;

            if ( focusIndex < 0 ) {
              //Wrap around to bottom row
              getField(0).setFocus();
              return true;
            } else {
                Field f = getField ( focusIndex );

                if ( f.isFocusable() ) {
                    f.setFocus();
                    dy++;
                    //UpdateDisplay();
                }
            }
        }

        while ( dx > 0 ) {
        	if (focusIndex == lnFieldCount-1) {
        		return false;
        	}
        	if ((focusIndex+1)%columns == 0) {
              //Wrap around to beginning of line or boundary
               //focusIndex = (focusIndex-columns) + 1;
               return false;
            } else {
               focusIndex ++;
            }
            
            if ( focusIndex >= lnFieldCount ) {
              if (lnFieldCount > 0) {
                getField(1).setFocus();
                return true;
              } else {
                return false; // Focus moves out of this manager
              }
            } else {
                Field f = getField ( focusIndex );

                if ( f.isFocusable() ) {
                    f.setFocus();
                    dx--;
                    // UpdateDisplay();
                }
            }
        }

        while ( dx < 0 ) {
            if (focusIndex%columns == 0) {
              //Wrap around to end of line Or Boundary
               //focusIndex = (focusIndex+columns) -1;
               return false;
            } else {
               focusIndex --;
            }


            if ( focusIndex < 0 ) {
                return false;
            } else {
                Field f = getField ( focusIndex );

                if ( f.isFocusable() ) {
                    f.setFocus();
                    dx++;
                    //   UpdateDisplay();
                }
            }
        }

        return true;
    }


    protected void sublayout ( int width, int height ) {
        int y = 0;

        if ( columnWidths == null ) {
            columnWidths = new int[columns];

            for ( int i = 0; i < columns; i++ ) {
                columnWidths[i] = width / columns;
            }
        }

        Field[] fields = new Field[columnWidths.length];
        int currentColumn = 0;
        int rowHeight = 0;

        for ( int i = 0; i < getFieldCount(); i++ ) {
            fields[currentColumn] = getField ( i );
            layoutChild ( fields[currentColumn], columnWidths[currentColumn], height - y );

            if ( fields[currentColumn].getHeight() > rowHeight ) {
                rowHeight = fields[currentColumn].getHeight();
            }

            currentColumn++;

            if ( currentColumn == columnWidths.length || i == getFieldCount() - 1 ) {
                int x = 0;

                if ( this.allRowHeight >= 0 ) {
                    rowHeight = this.allRowHeight;
                }

                for ( int c = 0; c < currentColumn; c++ ) {
                    long fieldStyle = fields[c].getStyle();
                    int fieldXOffset = 0;
                    long fieldHalign = fieldStyle & Field.FIELD_HALIGN_MASK;

                    if ( fieldHalign == Field.FIELD_RIGHT ) {
                        fieldXOffset = columnWidths[c] - fields[c].getWidth();
                    } else if ( fieldHalign == Field.FIELD_HCENTER ) {
                        fieldXOffset = ( columnWidths[c] - fields[c].getWidth() ) / 2;
                    }

                    int fieldYOffset = 0;
                    long fieldValign = fieldStyle & Field.FIELD_VALIGN_MASK;

                    if ( fieldValign == Field.FIELD_BOTTOM ) {
                        fieldYOffset = rowHeight - fields[c].getHeight();
                    } else if ( fieldValign == Field.FIELD_VCENTER ) {
                        fieldYOffset = ( rowHeight - fields[c].getHeight() ) / 2;
                    }

                    setPositionChild ( fields[c], x + fieldXOffset, y + fieldYOffset );
                    x += columnWidths[c];
                }

                currentColumn = 0;
                y += rowHeight;
            }

            if ( y >= height ) {
                break;
            }
        }

        int totalWidth = 0;

        for ( int i = 0; i < columnWidths.length; i++ ) {
            totalWidth += columnWidths[i];
        }

        setExtent ( totalWidth, Math.min ( y, height ) );
    }

}

 

 

Developer
peter_strange
Posts: 19,610
Registered: ‎07-14-2008
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager

[ Edited ]

Thanks for this.

 

Just wondering if you could quickly summarise where someone would use this and what advantages it has over TableLayoutManager?

 

Also, welcome to the forum!  And I have to say that if this is any indication, I hope that you will contribute frequently!  :smileyhappy:

Contributor
sujayarun8
Posts: 48
Registered: ‎07-20-2010
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager

[ Edited ]

hi.. i tried to execute this code. . but its not working... actually program is not showing in the emulator itself.. i think there is no main class found in this program.. then how this program vil work..any one please clear my doubt..

Developer
jdesmedt
Posts: 118
Registered: ‎06-24-2009
My Device: Not Specified

Re: Grid / table / spreadsheet layout manager

I need a Manager like this, but that also supports adding to cell spans.

E.g:a table with cell like this:

_____________

|            |

|____________|

|____|___|   |

|________|___|

 

Anyone knows of a Manager like this on BlackBerry, or will I have to write it myself?

New Contributor
adeldian
Posts: 7
Registered: ‎11-18-2010
My Device: Onyx 9700

Re: Grid / table / spreadsheet layout manager

it has a problem when use touch screen device on tilt action.

if u set the grid will draw equal width for all child, call constructor Manager gfm = new GridFieldManager(2,0);

so every child will have same width. but when u tilt the device, you notify that the width of all child, is same as the before tilt. it's because variable columnWidths already set in void sublayout(int width,int height);

 

if (columnWidths == null) {
	columnWidths = new int[columns];
	for(int i = 0; i < columns; i++) {
		columnWidths[i] = width/columns;
	}
}

 so it mean after set variable columnWidths the layout not calculate again because columnWidths already not null.

if you remove block "if(columnWidths==null)" the columnWidths will calculate again every tilt action. so you will get dynamic table.

 

thank you

 

 

Contributor
randolphvalencia
Posts: 11
Registered: ‎06-21-2011
My Device: Bold

Re: Grid / table / spreadsheet layout manager

The java files from thinkblackberry.com no longer exist. Sad