Idiomatische Tabellenzelle Renderer in Scala
-
19-09-2019 - |
Frage
Ich hatte den traditionellen Java TableCellRenderer
Ansatz wurde unter Verwendung der Renderer in einem scala.swing.Table
für die Bereitstellung, wo ich meine Renderer TableColumnModel
steht auf dem Tisch erklären. Der Code für das sah aus wie:
val myTable = new Table {
lazy val tcm = initColumnModel
peer.setColumnModel(tcm)
override
protected def 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 COMPONENT
Component.wrap(renderer.getTableCellRendererComponent(
peer,
value,
sel,
foc,
row,
col).asInstanceOf[JComponent])
}
}
Leider scheint dies ein Speicherleck zu haben - vermutlich, weil ich eine neue Komponenteninstanz für jede Zelle in der Tabelle erschaffe (für ~ 30k Zeilen). Sicherlich, wenn ich meinen scala Tisch mit einem JTable
ersetzen (mit genau die gleichen Spalte und Daten Modelle) mein Speicherverlust geht weg.
Meine Frage ist also, was für Code tun Menschen verwenden, wenn die rendererComponent
Methode überschreibt unter der Annahme einer die eigene Zelle Renderer hat?
Lösung
Die idiomatische Weg Scala Tabellenzelle Renderer zu verwenden, ist Table.AbstractRenderer
zu verwenden (wenn Sie Ihre eigene Implementierung) oder einer ihrer Unterklassen:
val tcr = new Table.AbstractRenderer[MyObj, MyRenderer](new MyRenderer) {
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 diesem Fall prepare
ist eine Methode, die Sie auf Ihrer eigenen Renderer Klasse definieren würden:
class MyRenderer extends Label {
def prepare(o: MyObj) {
text = o.toString //or whatever
}
}
Dann wird diese durch Überschreiben der rendererComponent
Methode auf Table
verwendet:
val t = new Table {
override def rendererComponent(sel: Boolean, foc: Boolean, row: Int, col: Int) = {
//FIND VALUE
val v = model.getValueAt(
peer.convertRowIndexToModel(row),
peer.convertColumnIndexToModel(row))
col match {
case 0 => tcr.componentFor(this, sel, foc, v, row, col)
}
}
}
Scala kommt mit seinen eigenen Implementierungen von AbstractRenderer
, nämlich LabelRenderer
, die eine Funktion als ein Argument nimmt, eine Instanz von Umwandeln MyObj zu einem Tuple2
bestehend aus einem String
und einem Icon
, für das Etikett zu Anzeige :
val ltcr = new LabelRenderer[MyObj] ( (o: MyObj) => (null, o.toString) )
Andere Tipps
Dank einer Tonne für Ihr Beispiel oxbow_lakes!
IMHO dieses scala-Ding hat so hässlich wie Tisch-Rendering werden kann möglicherweise bekommen. Der Versuch, es so weit wie möglich zu verbergen ...
class TableRenderer[A](comp: TableRendererComp[A]) extends Table.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] extends Component {
def render(a: A, sel: Boolean, foc: Boolean): Unit
}
Mit wie (zumindest die "konfiguriert" ist weg ...)
val tcr = new TableRenderer[MyObj](new MyRenderer)
class MyRenderer extends Label with TableRendererComp[MyObj] {
def render(o: MyObj, sel: Boolean, foc: Boolean) {
text = o.toString //or whatever
}
}