Archive for the tag 'TableModel'

How to create a TableModel conveniently

January 1st, 2008

These days, guys start ask questions about TableModel, i know most guys use DefaultTableModel, but i never use it, so i think i should do something now.

generally, i never use DefaultTableModel, because it is not convenient for most case, here’s my solution:

I use a ListModel to store all the row infos, and then use a implemented TableModel to return certain values for specified columns. I implement a class named PropertyTableModel, now it is on svn you can get it, for read, i paste it here too:

PropertyTableModel.as

package org.aswing.table{

import org.aswing.event.TableModelListener;
import org.aswing.ListModel;
import org.aswing.event.ListDataListener;
import org.aswing.event.ListDataEvent;
public class PropertyTableModel extends AbstractTableModel implements ListDataListener{

protected var list:ListModel;
protected var names:Array;
protected var properties:Array;
protected var translators:Array;

/**
* Create a Property table model, column headers, properties names, and translators.
* @param listModel the list model that contains the row objects.
* @param names column header labels.
* @param properties property names for column values, "." means returns row data object directly.
* @param translators the translators for each column, a null translator for a columns means return the property
* of that name directly.
*/
public function PropertyTableModel(listModel:ListModel, names:Array, properties:Array, translators:Array){
super();
this.setList(listModel);
this.names = names.concat();
this.properties = properties.concat();
this.translators = translators.concat();
}

/**
* Sets the row data provider, a list model.
* @param listModel the row object datas.
*/
public function setList(listModel:ListModel):void{
if(list != null){
list.removeListDataListener(this);
}
list = listModel;
if(list != null){
list.addListDataListener(this);
}
fireTableDataChanged();
}

/**
* Returns the row data provider, a list model.
* @returns the row data provider.
*/
public function getList():ListModel{
return list;
}

override public function getRowCount():int{
if(list){
return list.getSize();
}else{
return 0;
}
}

override public function getColumnCount():int{
return names.length;
}

/**
* Returns the translated value for specified row and column.
* @return the translated value for specified row and column.
*/
override public function getValueAt(rowIndex:int, columnIndex:int):*{
var translator:PropertyTranslator = translators[columnIndex];
var info:* = list.getElementAt(rowIndex);
var key:String = properties[columnIndex];
if(translator != null){
return translator.translate(info, key);
}else{
if(key == "."){
return info;
}
return info[key];
}
}

/**
* Returns the column name for specified column.
*/
override public function getColumnName(column:int):String {
return names[column];
}

//__________Listeners for List Model, to keep table view updated when row objects changed__________

public function intervalAdded(e:ListDataEvent):void{
fireTableRowsInserted(e.getIndex0(), e.getIndex1());
}

public function intervalRemoved(e:ListDataEvent):void{
fireTableRowsDeleted(e.getIndex0(), e.getIndex1());
}

public function contentsChanged(e:ListDataEvent):void{
fireTableRowsUpdated(e.getIndex0(), e.getIndex1());
}
}
}

Here is PropertyTranslator.as:

package org.aswing.table{

public interface PropertyTranslator{

function translate(info:*, key:String):*;

}
}

here’s a example for a case,

var list:VectorListModel = new VectorListModel([{id:1, name:”Romain”, country:”France”},{id:2, name:”Bob”, country:”England”}, {id:3, name:”iiley”, country:”China”}]);

Then you can create your PropertyTableModel like this:
var tableModel:PropertyTableModel= new PropertyTableModel(list, [“Name”, “Country”], [“name”, “country”], [null, null]);

Then the table will view two column, name and country. It is very simple to use.

But how to get the “id” when a row selected? You can get it from list.get(table.getSelectedRow()).id.

And well, you may want to get the “id” from your CellRenderer, then you can use a PropertyTranslator, for example


public class TranslatorInfoSelf implements PropertyTranslator{

public function translate(info:*, key:String):*{
return info;
}

}

new PropertyTableModel(list, ["Name", "Country"], ["name", "country"], [new TranslatorInfoSelf (), null]);

Then the first column will return info object as value, so you need a Renderer, like

tableModel.setColumnClass(0, "Name");
table.setDefaultCellFactory("Name", new GeneralTableCellFactory(NameCell));

The NameCell setCellValue implememtation:

public function setCellValue(v:*):void{
var name:String = v.name;
var info:Object = v; //here you can get the id with info.id.
}

Also, use “.” for property name and null translator you can get info object too, like this: new PropertyTableModel(list, [“Name”, “Country”], [“.”, “country”], [null, null]);

Well, the important is the model is splited into two models, a ListModel and a PropertyTableModel, so it is very flexible. When you want to add a row, it is simple, just call list.append({id:xx, name:xxx, country:xxx}); , If one info changed, you can just call list.valueChangedAt(row).

Here’s a simple test case, PropertyTableTest.as:

package{

import flash.display.Sprite;
import flash.events.MouseEvent;

import org.aswing.AsWingManager;
import org.aswing.JFrame;
import org.aswing.JScrollPane;
import org.aswing.JTable;
import org.aswing.VectorListModel;
import org.aswing.table.PropertyTableModel;

public class PropertyTableTest extends Sprite{

private var listData:VectorListModel;
private var table:JTable;

public function PropertyTableTest(){
super();

AsWingManager.initAsStandard(this);
listData = new VectorListModel(
[{name:"iiley", sex:1, age:26, score:99},
{name:"Comeny", sex:0, age:24, score:100},
{name:"Tom", sex:1, age:30, score:98},
{name:"Lita", sex:0, age:16, score:36}]
);

var tableModel:PropertyTableModel = new PropertyTableModel(
listData,
["Student Name", "Sex", "Score"],
["name", "sex", "score"],
[null, new SexTranslator(), null]
);

table = new JTable(tableModel);

var frame:JFrame = new JFrame(null, "PropertyTableTest");
frame.setContentPane(new JScrollPane(table));

frame.setSizeWH(300, 240);
frame.show();

//just for test add row
addData({name:"Added name", sex:1, age:6, score:9});
}

public function addData(obj:Object):void{
listData.append(obj);
}
}
}

import org.aswing.table.PropertyTranslator;

class SexTranslator implements PropertyTranslator{

public function translate(info:*, key:String):*{
var sex:int = info[key];
if(sex == 0){
return "female";
}else{
return "male";
}
}
}

Running Screenshot:
property table model

Finally, my suggestion for JTable and JTree model usage is, some times, implement your own Model class maybe the best way, use DefaultXXXModel maybe not the best. It is not hard to implement a TreeModel or a TableModel since there are examples already. And, notice, if you dont want just view with texts, there’s TreeCell, TableCell you can implement.

And, don’t hesitate to share your way to us.