I had been using the traditional Java TableCellRenderer approach for providing the renderers in a scala.swing.Table where I declare my renderers on the table's TableColumnModel. The code for this looked like:
val myTable =newTable{ lazy val tcm = initColumnModel peer.setColumnModel(tcm)overrideprotecteddef rendererComponent(sel:Boolean, foc:Boolean, row:Int, col:Int)={//GET THE VALUE FROM THE TableModel val value = model.getValueAt( peer.convertRowIndexToModel(row), peer.convertColumnIndexToModel(col))//GET THE RENDERER FROM THE ColumnModel val renderer = tcm.getColumn(col).getCellRenderer //WRAP IN A COMPONENTComponent.wrap(renderer.getTableCellRendererComponent( peer, value, sel, foc, row, col).asInstanceOf[JComponent])}}
Unfortunately this appears to have a memory leak - presumably because I am creating a new Component instance for every cell in the table (for ~30k rows). Certainly when I replace my scala table with a JTable (using exactly the same column and data models) my memory leak goes away.
My question is therefore, what sort of code do people use when overriding the rendererComponent method assuming one has ones own cell renderers?
回答1:
The idiomatic way of using Scala table cell renderers is to use Table.AbstractRenderer (if implementing your own) or one of its subclasses:
val tcr =newTable.AbstractRenderer[MyObj,MyRenderer](newMyRenderer){def configure(t:Table, sel:Boolean, foc:Boolean, o:MyObj, row:Int, col:Int)={//component variable is bound to your renderer component.prepare(o)}}
In this case prepare is a method you would define on your own renderer class:
classMyRendererextendsLabel{def prepare(o:MyObj){ text = o.toString //or whatever}}
Then this is used by overriding the rendererComponent method on Table:
val t =newTable{overridedef rendererComponent(sel:Boolean, foc:Boolean, row:Int, col:Int)={//FIND VALUE val v = model.getValueAt( peer.convertRowIndexToModel(row), peer.convertColumnIndexToModel(row)) col match {case0=> tcr.componentFor(this, sel, foc, v, row, col)}}}
Scala comes with its own implementations of AbstractRenderer, namely LabelRenderer which takes a function as an argument, converting an instance of MyObj to a Tuple2 consisting of a String and an Icon, for that label to display:
val ltcr =newLabelRenderer[MyObj]((o:MyObj)=>(null, o.toString))
回答2:
Thanks a ton for your example oxbow_lakes!
IMHO this scala-thing has become as ugly as table-rendering can possibly get. Trying to hide it as much as possible...
classTableRenderer[A](comp:TableRendererComp[A])extendsTable.AbstractRenderer[A,TableRendererComp[A]](comp){def configure(t:Table, sel:Boolean, foc:Boolean, a: A, row:Int, col:Int):Unit= component.render(a, sel, foc)} trait TableRendererComp[A]extendsComponent{def render(a: A, sel:Boolean, foc:Boolean):Unit}
Using like (at least the "configure" is gone...)
val tcr =newTableRenderer[MyObj](newMyRenderer)classMyRendererextendsLabelwithTableRendererComp[MyObj]{def render(o:MyObj, sel:Boolean, foc:Boolean){ text = o.toString //or whatever}}