Идиоматические средства рендеринга ячеек таблицы в Scala

StackOverflow https://stackoverflow.com/questions/1195013

Вопрос

Я использовал традиционную 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
  }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top