Blackberry Tablemodel gets messed up when scrolling - blackberry

I want to implement a scrollable List which is sorted alphabetically.
As a reference I am using the Tree Screen sample which comes delivered with the Eclipse IDE.
I changed the datatemplate to fit my needs and it works like a charm until you want to scroll. The whole UI gets messed up and I don't know what to do. I'm using JRE 7.1 and the Blackberry Simulator 9860 7.0 (I've also tested it on a real device).
Does anybody know if this is a known issue or do I miss something?
package lifeApp;
import net.rim.device.api.command.Command;
import net.rim.device.api.command.CommandHandler;
import net.rim.device.api.command.ReadOnlyCommandMetadata;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.component.table.DataTemplate;
import net.rim.device.api.ui.component.table.RegionStyles;
import net.rim.device.api.ui.component.table.SortedTableModel;
import net.rim.device.api.ui.component.table.TableController;
import net.rim.device.api.ui.component.table.TableModel;
import net.rim.device.api.ui.component.table.TableView;
import net.rim.device.api.ui.component.table.TemplateColumnProperties;
import net.rim.device.api.ui.component.table.TemplateRowProperties;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.decor.Border;
import net.rim.device.api.ui.decor.BorderFactory;
import net.rim.device.api.util.StringComparator;
public class ProductsScreen extends MainScreen
{
private SortedTableModel _tableModel;
private static final int ROW_HEIGHT = 40;
public ProductsScreen()
{
super(Manager.NO_VERTICAL_SCROLL | Manager.HORIZONTAL_SCROLL);
setTitle("Alle Produkte A-Z");
add(new LabelField("BlackBerry Devices", LabelField.FIELD_HCENTER));
add(new SeparatorField());
_tableModel = new SortedTableModel(StringComparator.getInstance(true), 0);
_tableModel.addRow(new Object[] {"A", "Produkt1"});
_tableModel.addRow(new Object[] {"b", "Produkt2"});
_tableModel.addRow(new Object[] {"c", "Produkt3"});
_tableModel.addRow(new Object[] {"c", "Produkt4"});
_tableModel.addRow(new Object[] {"b", "Produkt5"});
_tableModel.addRow(new Object[] {"c", "Produkt6"});
_tableModel.addRow(new Object[] {"c", "Produkt7"});
_tableModel.addRow(new Object[] {"r", "Produkt8"});
_tableModel.addRow(new Object[] {"t", "Produkt9"});
_tableModel.addRow(new Object[] {"c", "Produkt10"});
_tableModel.addRow(new Object[] {"b", "Produkt11"});
_tableModel.addRow(new Object[] {"u", "Produkt12"});
_tableModel.addRow(new Object[] {"v", "Produkt13"});
_tableModel.addRow(new Object[] {"t", "Produkt14"});
_tableModel.addRow(new Object[] {"c", "Produkt15"});
_tableModel.addRow(new Object[] {"b", "Produkt16"});
_tableModel.addRow(new Object[] {"u", "Produkt17"});
_tableModel.addRow(new Object[] {"v", "Produkt18"});
RegionStyles style = new RegionStyles(BorderFactory.createSimpleBorder(new XYEdges(1, 1, 1, 1), Border.STYLE_SOLID), null, null,
null, RegionStyles.ALIGN_LEFT, RegionStyles.ALIGN_TOP);
TableView tableView = new TableView(_tableModel);
TableController tableController = new TableController(_tableModel, tableView);
tableController.setFocusPolicy(TableController.ROW_FOCUS);
tableController.setCommand(new Command(new CommandHandler()
{
public void execute(ReadOnlyCommandMetadata metadata, Object context)
{
Dialog.alert("Command Executed");
}
}));
tableView.setController(tableController);
DataTemplate dataTemplate = new DataTemplate(tableView, 1, 1)
{
/**
* #see DataTemplate#getDataFields(int)
*/
public Field[] getDataFields(int modelRowIndex)
{
final Object[] data = (Object[]) ((TableModel) getView().getModel()).getRow(modelRowIndex);
Field[] fields = new Field[1];
fields[0] = new LabelField((String)data[1], Field.USE_ALL_WIDTH | Field.FOCUSABLE | DrawStyle.HCENTER);
return fields;
}
};
dataTemplate.createRegion(new XYRect(0, 0, 1, 1), style);
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(100, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setRowProperties(0, new TemplateRowProperties(ROW_HEIGHT));
tableView.setDataTemplate(dataTemplate);
dataTemplate.useFixedHeight(true);
add(tableView);
}
}

Well, I loaded up the JDE 7.1 UI/TableAndListDemo sample app, and ran it on the JDE 9900.
That sample (unmodified by me) exhibits the exact same screwy behaviour as the code you posted.
So, unfortunately, I would say that either there is a bug, or a valid example of how to use the relatively new SortedTableModel has not been produced (I couldn't find any better ones).
Of note: if you remove the sorting, and simply replace SortedTableModel with TableModel, the visual corruption goes away for me. Of course, you lose the important feature of the sorting, and the grouping (column 0 in your table model).
Another option would be to implement the sorting behaviour yourself. Sorting the data outside of the TableModel is undesirable, but also not too difficult. Then, you could add an extra row to the repeating pattern, as a placeholder for the separator row that you currently have showing the single character (the sort criterion). You would also change the data template to not be fixed height.
Also, in the data template, you would define a region that can hold the separator row. Whether or not that region will show anything or not depends on whether the data row to follow has the same single character as the last row, or not. So, here's what the code might look like
DataTemplate dataTemplate = new DataTemplate(tableView, 2, 1) // 2 "rows", not 1
{
public Field[] getDataFields(int modelRowIndex)
{
final Object[] data = (Object[]) _tableModel.getRow(modelRowIndex);
Field[] fields = new Field[2];
String rowGroup = (String)data[0];
// we're in a new group if this is the very first row, or if this row's
// data[0] value is different from the last row's data[0] value
boolean isNewGroup = (modelRowIndex == 0) ||
(rowGroup.compareTo((String) ((Object[])_tableModel.getRow(modelRowIndex - 1))[0]) != 0);
if (isNewGroup) {
// make a separator row
fields[0] = new LabelField((String)data[0],
Field.USE_ALL_WIDTH | Field.NON_FOCUSABLE);
} else {
// this is in the same group as the last product, so don't add anything here
fields[0] = new NullField();
}
// now, add the actual product information
fields[1] = new LabelField((String)data[1],
Field.USE_ALL_WIDTH | Field.FOCUSABLE | DrawStyle.HCENTER);
return fields;
}
};
dataTemplate.createRegion(new XYRect(0, 0, 1, 1), style); // group separator (maybe a null field)
dataTemplate.createRegion(new XYRect(0, 1, 1, 1), style); // actual rows with product information
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(100, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setRowProperties(0, new TemplateRowProperties(ROW_HEIGHT)); // separator
dataTemplate.setRowProperties(1, new TemplateRowProperties(ROW_HEIGHT)); // product data
dataTemplate.useFixedHeight(false);
In the above code, I chose to make the separator rows the same height and style as the product data rows. Of course, you could change that if you like.
One problem that this still doesn't solve is that of focus drawing. When you highlight the first row under the separator row, it draws focus on the product row, and the separator row above it.
You may need to implement some custom focus drawing. I'll leave that for the next question ... not sure if you even want to go this route. Hopefully I'm wrong, but it kind of looks like a bug in the RIM libraries to me :(

Related

How to get JIRA like change history using hibernate envers audit log?

I am trying to show JIRA like change history on UI. I am using Spring Data JPA and I have configured audit trail with Envers (v5.3.7). I can get list of all revisions using AuditQuery, for a particular entity by its primary key value.
Is there an easy way to calculate "delta" across revisions and identify properties that were changed? (With old and new value)
I have added #Audited(withModifiedFlag = true) annotation to my entity class. It adds one more column in the <entity>_aud table for each property indicating if the property was changed or not. I am trying to figure out, how to make use of these additional columns.
If you need something like JIRA you have to build it by your own.
I would suggest that you use Hibernate Interceptors:
http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#events
As you can see in the following example you get the current and the previous state and can then create the delta and store it in your own changelog table:
public static class LoggingInterceptor extends EmptyInterceptor {
#Override
public boolean onFlushDirty(
Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
LOGGER.debugv( "Entity {0}#{1} changed from {2} to {3}",
entity.getClass().getSimpleName(),
id,
Arrays.toString( previousState ),
Arrays.toString( currentState )
);
return super.onFlushDirty( entity, id, currentState,
previousState, propertyNames, types
);
}
}
Here is my code
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreUpdateEvent;
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class AuditListener implements PreUpdateEventListener {
#Autowired
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
// You can also add listener for a specific entity-type instead of event-group
// In my case I needed global event listener
registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(this);
}
#Override
public boolean onPreUpdate(PreUpdateEvent event) {
String[] propertyNames = event.getPersister().getPropertyNames();
Object[] oldValues = event.getOldState();
Object[] newValues = event.getState();
for (int index = 0; index < propertyNames.length; index++) {
String propertyName = propertyNames[index];
Object oldValue = oldValues[index];
Object newValue = newValues[index];
// This is just sample code
boolean changed = oldValue != newValue;
if (changed) {
System.out.println("Audit log -> Property: " + propertyName + ", Old value: " + oldValue + ", New value: " + newValue);
// Actual code that persists audit log
...
...
}
}
return false;
}
}

Vaadin table | How to set colspan

I'm using vaadin tree table, and I want to set 1st column colspan (equal to the total number of column in table) for some of the rows satisfying some business criteria. For the rest of table rows, individual columns will appear normally.
I've tried using generated columns, and by setting explicit column width, and also by having composite columns; but doing so changes the layout for all the row/columns. Kindly suggest how will we achieve this.
Thanks!
You can set the width of a column by calling TreeTable#setColumnExpandRatio(String columnName, float value).
In the example below, I've set the width of column "Name" to 75%. If you don't specify anything else, the rest of the columns will fit in the rest of the space.
ttable.setColumnExpandRatio("Name", 0.75f);
ttable.setColumnExpandRatio("Number", 0.25f); //not necessary
Try the example below that I modified from Vaadin book:
#Theme("mytheme")
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
TreeTable ttable = new TreeTable();
ttable.addContainerProperty("Name", String.class, null);
ttable.addContainerProperty("Number", Integer.class, null);
//Add some sample data
ttable.addItem(new Object[]{"Menu", null}, 0);
ttable.addItem(new Object[]{"Beverages", null}, 1);
ttable.setParent(1, 0);
ttable.addItem(new Object[]{"Foods", null}, 2);
ttable.setParent(2, 0);
ttable.addItem(new Object[]{"Coffee", 23}, 3);
ttable.addItem(new Object[]{"Tea", 42}, 4);
ttable.setParent(3, 1);
ttable.setParent(4, 1);
ttable.addItem(new Object[]{"Bread", 13}, 5);
ttable.addItem(new Object[]{"Cake", 11}, 6);
ttable.setParent(5, 2);
ttable.setParent(6, 2);
ttable.setColumnExpandRatio("Name", 0.75f);
ttable.setColumnExpandRatio("Number", 0.25f);
ttable.setSizeFull();
layout.addComponents(ttable);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
#WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
You can use com.vaadin.ui.Table.setRowGenerator:
Example with Java 8 + Vaadin 7.6.1
setRowGenerator((Table table, Object itemId) -> {
if (itemId instanceof MyClassThatIdentifiesARowToMerge) {
Table.GeneratedRow generatedRow = new Table.GeneratedRow("text-of-merged-cell");
generatedRow.setSpanColumns(true);
return generatedRow; // merge
}
return null; // doesn't merge
} );

Vaadin, TreeTable. setParent

I have a problem with creating parent-child link in my TreeTable.
Setting of DataSource
table.setContainerDataSource(new TempPeopleContainer(((MyUI) UI.getCurrent()).peopleService.getItemList()));
table.setParent(1,0);
How can I set id of Object in this kind of DataSource setting? I've no explicit Id for elements of TreeTable.
Here is example from vaadin , where you can see "clearly" definition of Id's for each element (code not from my app):
TreeTable ttable = new TreeTable("My TreeTable");
ttable.addContainerProperty("Name", String.class, null);
ttable.addContainerProperty("Number", Integer.class, null);
ttable.setWidth("20em");
// Create the tree nodes and set the hierarchy
ttable.addItem(new Object[]{"Menu", null}, 0);
ttable.addItem(new Object[]{"Beverages", null}, 1);
ttable.setParent(1, 0);
ttable.addItem(new Object[]{"Foods", null}, 2);
ttable.setParent(2, 0);
it's my TempPeopleContainer class definition:
private class TempPeopleContainer extends FilterableListContainer<People> {
public TempPeopleContainer(final Collection<People> collection) {
super(collection);
}
// This is only temporarily overridden until issues with
// BeanComparator get resolved.
#Override
public void sort(final Object[] propertyId, final boolean[] ascending) {
final boolean sortAscending = ascending[0];
final Object sortContainerPropertyId = propertyId[0];
Collections.sort(getBackingList(), (o1, o2) -> {
int result = 0;
if ("lastname".equals(sortContainerPropertyId)) {
result = o1.getLastname().compareTo(o2.getLastname());
}
if (!sortAscending) {
result *= -1;
}
return result;
});
}
}
I hope my question is clear. Thanks.
It depends on your ItemContainer. The common BeanItemContainer from vaadin uses the item as item id itself as documented here https://vaadin.com/api/com/vaadin/data/util/BeanItemContainer.html
You are using vitrin's org.vaadin.viritin.ListContainer acting like the BeanItemContainer
So you could use the items from your list as itemId/newParentId if you want to stick at your ItemContainer implementations.
Or you could go the long way and get the item ids by iterating over com.vaadin.data.Container.getItemIds() and check manually if this item id is the parent id you want to use.
edit:
Try something like this:
List myList = ((MyUI) UI.getCurrent()).peopleService.getItemList();
TempPeopleContainer container = new TempPeopleContainer(myList);
table.setContainerDataSource(container);
table.setParent(myList.get(1), myList.get(0));

Insert data in TableModel.addRow into Blackberry TableView

I need to Create a table where 3 columns are needed and can have multiple rows. I am using BlackBerry API version 6. I have debugged my code and it's giving IllegalArgumentException. I am not able to sort this error out.
My code is as follows:
public class designTableLayout extends MainScreen{
TableModel theModel = new TableModel();
theView = new TableView(theModel);
TableController theController = new TableController(theModel, theView,
TableController.FIELD_FOCUS);
theView.setController(theController);
HeaderTemplate theTemplate = new HeaderTemplate(theView, 1, 3);
theTemplate.createRegion(new XYRect(0,0,1,1));
theTemplate.createRegion(new XYRect(1,0,1,1));
theTemplate.createRegion(new XYRect(2,0,1,1));
theTemplate.setRowProperties(0, new TemplateRowProperties(60));
theTemplate.setColumnProperties(0, new TemplateColumnProperties(40));
theTemplate.setColumnProperties(1, new TemplateColumnProperties(40));
theTemplate.setColumnProperties(2, new TemplateColumnProperties(40));
theTemplate.useFixedHeight(true);
theView.setDataTemplate(theTemplate);
theModel.addRow(new String[]{"the","quick","brown"});// problem arises here
theModel.addRow(new String[]{"jumps","over","the"});
theModel.addRow(new String[]{"dog","the","quick"});
add(theView);
}
class HeaderTemplate extends DataTemplate {
LabelField field1 = new LabelField("field1");
LabelField field2 = new LabelField("field2");
LabelField field3 = new LabelField("field3");
public HeaderTemplate(DataView view,int rows,int columns){
super(view, rows, columns);
}
public Field[] getDataFields(int modelRowIndex) {
TableModel theModel = (TableModel) getView().getModel();
//Get the data for the row.
Object[] data = {field1, field2, field3};
data = (Object[]) theModel.getRow(modelRowIndex);
//Create a array to hold all fields.
Field[] theDataFields = new Field[data.length];
theDataFields[0] = new LabelField(field1/*, DrawStyle.ELLIPSIS*/);
theDataFields[1] = new LabelField(field2/*, DrawStyle.ELLIPSIS*/);
theDataFields[2] = new LabelField(field3/*, DrawStyle.ELLIPSIS*/);
return theDataFields;
}
}
I know you probably are using some of this code just to test your table model, but I think your template should look more like this:
class HeaderTemplate extends DataTemplate {
public HeaderTemplate(DataView view,int rows,int columns){
super(view, rows, columns);
}
public Field[] getDataFields(int modelRowIndex) {
TableModel theModel = (TableModel) getView().getModel();
//Get the data for the row.
Object[] data = (Object[]) theModel.getRow(modelRowIndex);
//Create a array to hold all fields.
Field[] theDataFields = new Field[data.length];
theDataFields[0] = new LabelField((String)data[0], DrawStyle.ELLIPSIS);
theDataFields[1] = new LabelField((String)data[1], DrawStyle.ELLIPSIS);
theDataFields[2] = new LabelField((String)data[2], DrawStyle.ELLIPSIS);
return theDataFields;
}
}
And then add your data as an Object[]:
theModel.addRow(new Object[]{"the","quick","brown"});
Here is the BlackBerry example on this

Blackberry: Drawing TableModel focus properly

I need help with drawing the focus of the selected row properly.
Currently if I select the first item of a category the separatorrow gets highlighted too. So how can I implement my custom focus drawing so that only the selected row gets focused/highlighted?
I am using the posted source code from here: Blackberry Tablemodel gets messed up when scrolling
I am using the Eclipse IDE from RIM and JRE 7.0.0
public class ProductsScreen extends MainScreen
{
private TableModel _tableModel;
private static final int ROW_HEIGHT = 40;
public ProductsScreen(MainCategory mc)
{
super(Manager.NO_VERTICAL_SCROLL | Manager.HORIZONTAL_SCROLL);
DBManager dbman = DBManager.getInstance();
AllProductByCategory[] products = null;
try {
products = dbman.getProducts(mc.getID().intValue());
} catch (DatabaseException e) {
System.out.println(e.getMessage());
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
setTitle(mc.getName());
_tableModel = new TableModel();//(StringComparator.getInstance(true), 0);
if(products != null)
{
for(int i = 0; i < products.length; i++)
{
ViewableData[] data = products[i].getData().getViewableData();
for(int j = 0; j < data.length; j++)
{
_tableModel.addRow(new Object[] {products[i].getCategoryName(), data[j].getTitle2()});
}
}
}
RegionStyles style = new RegionStyles(BorderFactory.createSimpleBorder(new XYEdges(1, 1, 1, 1), Border.STYLE_SOLID), null, null,
null, RegionStyles.ALIGN_LEFT, RegionStyles.ALIGN_TOP);
TableView tableView = new TableView(_tableModel);
final TableController tableController = new TableController(_tableModel, tableView);
tableController.setFocusPolicy(TableController.ROW_FOCUS);
tableController.setCommand(new Command(new CommandHandler()
{
public void execute(ReadOnlyCommandMetadata metadata, Object context)
{
}
}));
tableView.setController(tableController);
DataTemplate dataTemplate = new DataTemplate(tableView, 2, 2)
{
public Field[] getDataFields(int modelRowIndex)
{
final Object[] data = (Object[]) _tableModel.getRow(modelRowIndex);
Field[] fields = new Field[3];
String rowGroup = (String)data[0];
// we're in a new group if this is the very first row, or if this row's
// data[0] value is different from the last row's data[0] value
boolean isNewGroup = (modelRowIndex == 0) ||
(rowGroup.compareTo((String) ((Object[])_tableModel.getRow(modelRowIndex - 1))[0]) != 0);
if (isNewGroup) {
// make a separator row
fields[0] = new HeaderField((String)data[0],
Field.USE_ALL_WIDTH | Field.NON_FOCUSABLE);
} else {
// this is in the same group as the last product, so don't add anything here
fields[0] = new NullField();
}
// now, add the actual product information
fields[1] = new LabelField((String)data[1],
Field.USE_ALL_WIDTH | Field.FOCUSABLE | Field.USE_ALL_HEIGHT | DrawStyle.ELLIPSIS);
fields[2] = new BitmapField(Bitmap.getBitmapResource("img/bullet_arrow_right.png"));
return fields;
}
};
dataTemplate.createRegion(new XYRect(0, 0, 2, 1)); // group separator (maybe a null field)
dataTemplate.createRegion(new XYRect(0, 1, 1, 1)); // actual rows with product information
dataTemplate.createRegion(new XYRect(1, 1, 1, 1));
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(95, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setColumnProperties(1, new TemplateColumnProperties(5, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setRowProperties(0, new TemplateRowProperties(ROW_HEIGHT)); // separator
dataTemplate.setRowProperties(1, new TemplateRowProperties(ROW_HEIGHT)); // product data
dataTemplate.useFixedHeight(false);
tableView.setDataTemplate(dataTemplate);
add(tableView);
}
}
SOLUTION:
I was able to solve the problem on my own with the following approach.
I just added a overridden LabelField as headerfield and didn't implement its focus drawing. So only the "subfields" get the focus drawn.
Maybe some people would implement it in another way (take a look at the answer from Nate) but it worked for me.
So, I didn't have time to fully integrate your new code sample, which has data model code that I don't have, and which appears to have added a DataTemplate column for a BitmapField. Hopefully, you can adapt what I have to reintegrate those changes.
I'm sure there's more than one way to do this, and I'm not claiming this method to be the highest performance. However, it seems to draw the focus as you would expect, without the separator row getting highlighted when the row directly under it is focused.
What I did was abandon the concept of using multiple regions, and just made my data template 1 row by 1 column. If you want, you can probably make it 1 row by 2 columns, where the column I don't show is the BitmapField.
But, what I did was to place a VerticalFieldManager in the first row in each new group/category. That VerticalFieldManager then contained a separator/header row, a separator field (just a horizontal line), and then the actual product row. If the row was not the first in the group/category, I would just return a simple Field, not a VerticalFieldManager with three Field objects inside it.
Then, I changed the TableController focus policy to FIELD_FOCUS, not ROW_FOCUS. This allows focus to be taken by the VerticalFieldManager, when we're on the first row in a new group/category. However, inside that manager, only the actual product row is focusable. The separator row is not focusable, and will therefore not be drawn with focus.
Here's the code that changed. The rest is the same as in the previous sample I gave you:
_tableController.setFocusPolicy(TableController.FIELD_FOCUS);
_tableView.setController(_tableController);
DataTemplate dataTemplate = new DataTemplate(_tableView, 1, 1) // 1 row now!
{
public Field[] getDataFields(int modelRowIndex)
{
final Object[] data = (Object[]) _tableModel.getRow(modelRowIndex);
String rowGroup = (String)data[0];
// we're in a new group if this is the very first row, or if this row's data[0] value is
// different from the last row's data[0] value
boolean isNewGroup = (modelRowIndex == 0) ||
(rowGroup.compareTo((String) ((Object[])_tableModel.getRow(modelRowIndex - 1))[0]) != 0);
if (isNewGroup) {
LabelField header = new LabelField((String)data[0], Field.USE_ALL_WIDTH | Field.NON_FOCUSABLE);
SeparatorField line = new SeparatorField(Field.USE_ALL_WIDTH) {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
super.paint(g);
}
};
LabelField productRow = new LabelField((String)data[1],
Field.USE_ALL_WIDTH | Field.FOCUSABLE | DrawStyle.HCENTER);
VerticalFieldManager manager = new VerticalFieldManager(Field.USE_ALL_WIDTH | Field.FOCUSABLE |
Manager.NO_VERTICAL_SCROLL | Manager.NO_VERTICAL_SCROLLBAR);
manager.add(header);
manager.add(line);
manager.add(productRow);
return new Field[] { manager };
} else {
return new Field[] { new LabelField((String)data[1],
Field.USE_ALL_WIDTH | Field.FOCUSABLE | DrawStyle.HCENTER) };
}
}
};
// create just one region, with one row and one full-width column
dataTemplate.createRegion(new XYRect(0, 0, 1, 1), _style); // may be a product row, or a product row + separator
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(100, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setRowProperties(0, new TemplateRowProperties(2 * ROW_HEIGHT)); // max height if row + separator
_tableView.setDataTemplate(dataTemplate);
dataTemplate.useFixedHeight(false);
The scrolling is a little funny when you get down to the bottom of the page, but I'm pretty sure I've built VerticalFieldManager subclasses before that acted like lists, that needed some custom scroll handling ... if I get some time tomorrow, I'll try to add that in.
One step at a time, though ...

Resources