Идиоматические средства рендеринга ячеек таблицы в Scala
-
19-09-2019 - |
Вопрос
Я использовал традиционную Java TableCellRenderer
подход к предоставлению средств визуализации в scala.swing.Table
где я объявляю свои средства визуализации на столе TableColumnModel
.Код для этого выглядел так:
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])
}
}
К сожалению, похоже, это утечка памяти - предположительно потому, что я создаю новый экземпляр компонента для каждой ячейки таблицы (около 30 тыс. строк).Конечно, когда я заменяю свою таблицу Scala на JTable
(используя точно такой же столбец и данные модели) моя утечка памяти исчезла.
Поэтому мой вопрос заключается в том, какой код люди используют при переопределении rendererComponent
метод, предполагающий, что у вас есть собственные средства рендеринга ячеек?
Решение
Идиоматический способ использования средств визуализации ячеек таблицы Scala заключается в использовании Table.AbstractRenderer
(если реализуете свой собственный) или один из его подклассов:
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)
}
}
В этом случае prepare
это метод, который вы бы определили в своем собственном классе рендеринга:
class MyRenderer extends Label {
def prepare(o: MyObj) {
text = o.toString //or whatever
}
}
Затем это используется путем переопределения rendererComponent
метод на 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 поставляется со своими собственными реализациями AbstractRenderer
, а именно LabelRenderer
который принимает функцию в качестве аргумента, преобразуя экземпляр МойОбж к Tuple2
состоящий из String
и Icon
, чтобы эта метка отображалась:
val ltcr = new LabelRenderer[MyObj] ( (o: MyObj) => (null, o.toString) )
Другие советы
Огромное спасибо за ваш пример oxbow_lakes!
ИМХО, эта скала-вещь стала настолько уродливой, насколько это возможно.Стараюсь это максимально скрыть...
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
}
Использование лайка (по крайней мере, «настройка» исчезло...)
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
}
}