问题
I'm trying to use Scala/Swing to create a Table, one of whose columns is populated by Button
s.
My starting point is the SCells spreadsheet example from Odersky et al's book, and in particular the use of rendererComponent
to control the Component
appearing in each cell.
Unfortunately, while this creates a button successfully, the button is not clickable. Here's a reasonably minimal and self-contained example:
import swing._
import swing.event._
class TableButtons extends ScrollPane {
viewportView = new Table(2,2) {
rowHeight = 25
override def rendererComponent(isSelected: Boolean, hasFocus: Boolean,
row: Int, column: Int): Component =
if (column == 0) {
new Label("Hello")
} else {
val b = new Button { text = "Click" }
listenTo(b)
reactions += {
case ButtonClicked(`b`) => println("Clicked")
}
b
}
}
}
object Main extends SimpleSwingApplication {
def top = new MainFrame {
title = "Table button test"
contents = new TableButtons
}
}
When I run this, I get a table with two columns; the first contains labels, the second contains buttons, but the buttons aren't clickable.
Possibly related issue: the cells (including the ones containing buttons) are editable. What's the best way to disable editing?
I've seen this question (and this one) and have tried following the approach there (using Table.AbstractRenderer
) but that's also not working - and it's not at all obvious to me where to put reactions to button clicks in that version. (Is that approach outdated? Or is the approach from the Scala book too simplisitic?)
Thanks for any advice!
回答1:
You can make a column ineditable by providing a custom table model. However, your cell must be editable, because that is the only way the editing component becomes 'live' (repaints state changes, receives mouse events).
In the normal rendering (using renderComponent
), the component is only used to 'stamp' it, i.e. the table just calls paint
on the component. Thus, performance-wise, you should re-use one instance of each rendering component, instead of creating a new Label
/ Button
in every call.
So, you need to override the editor
method. Unfortunately it returns a plain javax.swing.table.TableCellEditor
, and thus you must step down to the plain javax.swing
stuff and loose all the Scala goodness...
The following almost works. Strangely, the button disappears when clicking on it -- have no idea why :-(
import scala.swing._
import scala.swing.event._
import javax.swing.{AbstractCellEditor, JTable}
import javax.swing.table.TableCellEditor
import java.awt.{Component => AWTComponent}
class TableButtons extends ScrollPane {
private val lb = new Label("")
private val b = new Button
private val buttonEditor = new AbstractCellEditor with TableCellEditor {
listenTo(b)
reactions += {
case ButtonClicked(`b`) =>
println("Clicked")
fireEditingStopped()
}
def getCellEditorValue: AnyRef = "what value?"
// ouch, we get JTable not scala.swing.Table ...
def getTableCellEditorComponent(tab: JTable, value: AnyRef, isSelected: Boolean,
row: Int, col: Int): AWTComponent = {
b.text = "Click!"
b.peer // ouch... gotta go back to AWT
}
}
viewportView = new Table(2, 2) {
rowHeight = 25
override def rendererComponent(isSelected: Boolean, hasFocus: Boolean,
row: Int, column: Int): Component =
if (column == 0) {
lb.text = "Hello"
lb
} else {
b.text = "Click?"
b
}
override def editor(row: Int, col: Int): TableCellEditor =
if (col == 1) buttonEditor else super.editor(row, col)
}
}
val top = new Frame {
title = "Table button test"
contents = new TableButtons
pack()
visible = true
}
In any case, check the Oracle JTable tutorial for the intricate details of renderers and editors.
来源:https://stackoverflow.com/questions/8635737/how-to-embed-a-working-button-in-a-swing-table-in-scala