Java 在选择行之前,在JTable中执行操作

Java 在选择行之前,在JTable中执行操作,java,swing,jtable,Java,Swing,Jtable,大家好 在我的应用程序中,我需要创建一个被动JTable。被动的意思是,行的选择不是由JTable直接完成的,而是由另一个组件请求时完成的。因此,当用户转到新行时,表不会立即做出反应,而是首先要求数据集根据所需的新行更新其内部状态,然后数据集回调表以进行真正的选择。因此,我只是尝试在表中选择新行之前执行一个操作 我创建了一个小原型,让你知道我想要什么。在原型下面你会发现我的问题 import java.awt.BorderLayout; import java.awt.Dimension; i

大家好

在我的应用程序中,我需要创建一个被动JTable。被动的意思是,行的选择不是由JTable直接完成的,而是由另一个组件请求时完成的。因此,当用户转到新行时,表不会立即做出反应,而是首先要求数据集根据所需的新行更新其内部状态,然后数据集回调表以进行真正的选择。因此,我只是尝试在表中选择新行之前执行一个操作

我创建了一个小原型,让你知道我想要什么。在原型下面你会发现我的问题

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
    ...
}