renderizadores de célula tabela idiomáticas em Scala
-
19-09-2019 - |
Pergunta
Eu estava usando a abordagem tradicional TableCellRenderer
Java para fornecer os representantes em uma scala.swing.Table
onde eu declaro meus prestadores de TableColumnModel
da tabela. O código para esta parecia:
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])
}
}
Infelizmente, isto parece ter um vazamento de memória - presumivelmente porque eu estou criando uma nova instância do componente para cada célula da tabela (para ~ 30k linhas). Certamente quando eu substituí minha mesa scala com um JTable
(usando exatamente o mesmo coluna e dados modelos) meu vazamento de memória vai embora.
A minha pergunta é, portanto, o tipo de código que as pessoas usam quando substituindo o método rendererComponent
assumindo um tem os próprios prestadores de celulares?
Solução
A maneira idiomática de usar Scala prestadores de célula de tabela é usar Table.AbstractRenderer
(se implementar o seu próprio) ou uma de suas subclasses:
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)
}
}
Neste caso prepare
é um método você definiria em sua própria classe de processador:
class MyRenderer extends Label {
def prepare(o: MyObj) {
text = o.toString //or whatever
}
}
Então este é usado, substituindo o método rendererComponent
em 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 vem com suas próprias implementações de AbstractRenderer
, nomeadamente LabelRenderer
que leva uma função como um argumento, a conversão de uma instância de MyObj a um Tuple2
consistindo de um String
e um Icon
, para esse rótulo para exibição :
val ltcr = new LabelRenderer[MyObj] ( (o: MyObj) => (null, o.toString) )
Outras dicas
Graças a tonelada por seu exemplo oxbow_lakes!
IMHO este scala coisa se tornou tão feio como table-rendering pode eventualmente chegar. Tentando esconder o máximo possível ...
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
}
Usando como (pelo menos o "configure" é ido ...)
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
}
}