renderer di celle tavolo idiomatiche a Scala
-
19-09-2019 - |
Domanda
avevo usato l'approccio tradizionale TableCellRenderer
Java per fornire i renderer in un scala.swing.Table
in cui dichiaro la mia renderer sul TableColumnModel
della tabella. Il codice per questo sembrava:
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])
}
}
Purtroppo questo sembra avere una perdita di memoria - presumibilmente perché sto creando una nuova istanza componente per ogni cella della tabella (per ~ le righe 30k). Certo, quando sostituisco il mio tavolo scala con un JTable
(utilizzando esattamente gli stessi Colonna e Dati modelli) la mia perdita di memoria se ne va.
La mia domanda è quindi, che tipo di codice di fare la gente usa quando l'override del metodo rendererComponent
assumendo uno ha quelli proprio renderer di celle?
Soluzione
Il modo idiomatico di usare Scala renderer di celle da tavolo è quello di utilizzare Table.AbstractRenderer
(se attuare il proprio) o una delle sue sottoclassi:
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 questo caso prepare
è un metodo che si definirebbe da soli classe renderer:
class MyRenderer extends Label {
def prepare(o: MyObj) {
text = o.toString //or whatever
}
}
Quindi questo è usato sovrascrivendo il metodo rendererComponent
su Table
:
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 dotato di proprie implementazioni di AbstractRenderer
, cioè LabelRenderer
che prende una funzione come argomento, convertire un'istanza di MyObj per un Tuple2
costituito da un String
e un Icon
, per tale etichetta da visualizzare :
val ltcr = new LabelRenderer[MyObj] ( (o: MyObj) => (null, o.toString) )
Altri suggerimenti
Grazie una tonnellata per il tuo esempio oxbow_lakes!
IMHO questa scala-cosa è diventata brutta come tavolo-rendering può eventualmente ottenere. Cercando di nascondere il più possibile ...
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
}
Utilizzando come (almeno il "configure" non c'è più ...)
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
}
}