Java 在选择行之前,在JTable中执行操作
大家好 在我的应用程序中,我需要创建一个被动JTable。被动的意思是,行的选择不是由JTable直接完成的,而是由另一个组件请求时完成的。因此,当用户转到新行时,表不会立即做出反应,而是首先要求数据集根据所需的新行更新其内部状态,然后数据集回调表以进行真正的选择。因此,我只是尝试在表中选择新行之前执行一个操作 我创建了一个小原型,让你知道我想要什么。在原型下面你会发现我的问题Java 在选择行之前,在JTable中执行操作,java,swing,jtable,Java,Swing,Jtable,大家好 在我的应用程序中,我需要创建一个被动JTable。被动的意思是,行的选择不是由JTable直接完成的,而是由另一个组件请求时完成的。因此,当用户转到新行时,表不会立即做出反应,而是首先要求数据集根据所需的新行更新其内部状态,然后数据集回调表以进行真正的选择。因此,我只是尝试在表中选择新行之前执行一个操作 我创建了一个小原型,让你知道我想要什么。在原型下面你会发现我的问题 import java.awt.BorderLayout; import java.awt.Dimension; i
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
public class SSCCE extends JPanel
{
public SSCCE()
{
setLayout(new BorderLayout());
final JLabel selectedRow = new JLabel();
final Table table = new Table();
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
selectedRow.setText(
"Selected row: " + table.getSelectedRow());
}
}
}
);
new DataSet(table);
add(new JScrollPane(table), BorderLayout.CENTER);
add(selectedRow, BorderLayout.PAGE_END);
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Table Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SSCCE());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(
new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
}
);
}
}
class DataSet
{
private final Table _table;
private int _currentIndex;
DataSet(Table table)
{
_table = table;
_table.setDataSet(this);
}
int getCurrentIndex()
{
return _currentIndex;
}
void moveTo(int index) throws MovementException
{
if (index < 0 || index > 4)
{
throw new IndexOutOfBoundsException();
}
// Let's suppose there was a problem moving to the 2nd index. Maybe
// the data set was in edit mode and couldn't persist the changes
// because of a validation error.
if (index == 2)
{
throw new MovementException();
}
_currentIndex = index;
// Notifies the table that the data was moved so that the table can
// update its selection model based on the current index of the
// data set.
_table.dataMoved();
}
}
class MovementException extends RuntimeException
{
}
class Table extends JTable
{
private DataSet _dataSet;
// When true signals that the data was moved in the data set, so selection
// is allowed.
private boolean _dataMoved;
// Previous selected column.
private int _oldSelectedColumn;
Table()
{
super(new Model());
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setCellSelectionEnabled(true);
getTableHeader().setReorderingAllowed(false);
setPreferredScrollableViewportSize(new Dimension(500, 170));
getColumnModel().setSelectionModel(new ColumnSelectionModel());
}
void setDataSet(DataSet dataSet)
{
_dataSet = dataSet;
}
// Called by DataSet#moveTo.
void dataMoved()
{
_dataMoved = true;
try
{
int rowIndex = _dataSet.getCurrentIndex();
// Select the new row.
setRowSelectionInterval(rowIndex, rowIndex);
}
finally
{
_dataMoved = false;
}
}
@Override
protected ListSelectionModel createDefaultSelectionModel()
{
return new RowSelectionModel();
}
private class ColumnSelectionModel extends DefaultListSelectionModel
{
@Override
public void setSelectionInterval(int index0, int index1)
{
// Save the old selected column to be restored in
// RowSelectionModel#setSelectionInterval in case of an error.
_oldSelectedColumn = getSelectedColumn();
super.setSelectionInterval(index0, index1);
}
}
private class RowSelectionModel extends DefaultListSelectionModel
{
@Override
public void setSelectionInterval(int index0, int index1)
{
if (_dataMoved || index1 == _dataSet.getCurrentIndex())
{
super.setSelectionInterval(index0, index1);
}
else
{
try
{
_dataSet.moveTo(index1);
}
catch (MovementException ex)
{
// There was a problem in the data set. Restore the old
// selected column.
setColumnSelectionInterval(
_oldSelectedColumn, _oldSelectedColumn);
throw ex;
}
}
}
}
private static class Model extends AbstractTableModel
{
private String[] columnNames =
{"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"};
private Object[][] data = {
{"Kathy", "Smith", "Snowboarding", 5, false},
{"John", "Doe", "Rowing", 3, true},
{"Sue", "Black", "Knitting", 2, false},
{"Jane", "White", "Speed reading", 20, true},
{"Joe", "Brown", "Pool", 10, false}
};
public int getColumnCount()
{
return columnNames.length;
}
public int getRowCount()
{
return data.length;
}
public String getColumnName(int col)
{
return columnNames[col];
}
public Object getValueAt(int row, int col)
{
return data[row][col];
}
public Class<?> getColumnClass(int c)
{
return getValueAt(0, c).getClass();
}
public void setValueAt(Object value, int row, int col)
{
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
}
覆盖表#更改选择方法:
但我没有使用这种方法,尽管它比选择模型简单得多,因为changeSelection方法的文档说明:
键盘或鼠标对所选内容的大多数更改
UI接收到的事件通过此方法传递,以便
我的行为将被一个子类覆盖
所以我解释说
大多数变化
因为不是所有的更改,这意味着可能有一些选择更改没有通过此方法。我在这一点上是正确的还是可以相信changeSelection方法
先谢谢你
Marcos您的方法的主要缺陷是(导航)模型(又名:DataSet)和视图之间的硬连线双向耦合。解决方法类似于VetoableSelectionModel:然后您可以将数据集注册为选择模型的vetoablePropertyChangeListener,这是一种松散耦合,可以在不对表进行子类化的情况下进行配置 总体布线的一些代码片段:
final JTable table = new JTable(new Model());
VetoableListSelectionModel selectionModel = new VetoableListSelectionModel();
table.setSelectionModel(selectionModel);
VetoableChangeListener veto = new VetoableChangeListener() {
@Override
public void vetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException {
if (2 == (Integer) evt.getNewValue()) throw new PropertyVetoException("", evt);
}
};
selectionModel.addVetoableChangeListener(veto);
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
selectedRow.setText(
"Selected row: " + table.getSelectedRow());
}
}
}
);
骨架选择模型(完整代码在-注意:未维护!)
这解决了什么问题?@trashgod在选择JTable中的行之前执行操作的问题。如果需要,甚至可以取消对行的选择。在上面的示例中,如果数据集无法根据表建议的行索引更新其内部状态,则不会选择表中的新行。因此,表中的行仅在响应数据集更改时被选择,反之亦然。@trashgod换句话说:表应反映数据集(其当前索引)的状态。如果数据集无法更改其currentIndex,则表中选定的行也不会更改。同意。高耦合总是一件坏事。不过,在我的实际应用程序中,情况稍微干净了一点。很高兴在您的审查中,耦合问题是您发现的唯一问题。也许我错了,但我认为该问题与RowInserted有关,请了解此代码如何能够同步OPs_unknow_rowIndex@mKorbel“你能再详细一点吗?”马科斯高兴地说?数据/控制器和视图之间的强耦合是桌面应用程序中可能发生的最坏情况;-)@克利奥帕特拉是的,我知道,但还有一件更糟糕的事情需要注意:正确性。如果它不正确,如果有缺陷,其他一切,即使是低耦合,都无关紧要。
@Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
if (_dataMoved)
{
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
else
{
if (rowIndex != _dataSet.getCurrentIndex())
{
_dataSet.moveTo(rowIndex);
}
super.changeSelection(_dataSet.getCurrentIndex(), columnIndex, toggle, extend);
}
}
final JTable table = new JTable(new Model());
VetoableListSelectionModel selectionModel = new VetoableListSelectionModel();
table.setSelectionModel(selectionModel);
VetoableChangeListener veto = new VetoableChangeListener() {
@Override
public void vetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException {
if (2 == (Integer) evt.getNewValue()) throw new PropertyVetoException("", evt);
}
};
selectionModel.addVetoableChangeListener(veto);
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener()
{
@Override
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
selectedRow.setText(
"Selected row: " + table.getSelectedRow());
}
}
}
);
/**
* Quick impl of a list selection model which respects a veto before
* changing selection state. The veto is effect in SINGLE_SELECTION mode
* only.
*/
public class VetoableListSelectionModel extends DefaultListSelectionModel {
private VetoableChangeSupport vetoableChangeSupport;
/**
* Defaults to SINGLE_SELECTION mode.
*
*/
public VetoableListSelectionModel() {
super();
setSelectionMode(SINGLE_SELECTION);
}
@Override
public void setSelectionInterval(int index0, int index1) {
if (isVetoable()) {
try {
fireVetoableChange(getMinSelectionIndex(), index0);
} catch (PropertyVetoException e) {
// vetoed - do nothing
return;
}
}
super.setSelectionInterval(index0, index1);
}
// similar for all methods that change the selection
...
// methods to add/remove listeners and fire the event
...
}