好的,所以这需要稍微转变心态。要使这真正灵活,您将需要支持“依赖注入”和“委托”等概念。
这样做的原因是,您的“操作”需要大量信息,但是,我们应该努力使我们的类之间具有低水平的内聚或耦合。例如,您的“操作”不应该关心“如何”删除该行,只关心在请求时应该完成。
那么,让我们从一些基本的委托开始...
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
现在,很明显,为了满足我的更多需求,我过度简化了它,但在这里我提供了一个“基本”级别的委托和更集中的委托。为什么?因为如果你想提供一个“插入”动作会发生什么?为什么它应该具有“删除”功能?相反,我们故意隔离我们想要公开的功能。
接下来,我们需要设计我们的动作......
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
@Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
好吧,没什么特别的,这很重要。它监视选择状态,因此我们可以启用/禁用操作,当触发时,我们调用我们的委托来完成实际工作。这将操作与实现分离,因为操作不需要知道正在使用什么类型的 TableModel 或它可能使用的数据源类型,它只是想告诉委托它应该执行一些一种操作。
另外注意,我们设置了一个键盘快捷键,可以被JMenuItem和助记符支持使用(按住Alt或Option键)
好的,但这确实为我们做了很多,或者是......
让我们看看删除操作可能是什么样子...
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
@Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
@Override
public void removeSelectedRow() {
DefaultTableModel model = (DefaultTableModel) table.getModel();
int visibleRowIndex = table.getSelectedRow();
if (visibleRowIndex == -1) {
return;
}
int modelIndex = table.convertRowIndexToModel(visibleRowIndex);
// I'm guessing here, but if you're deleting a row, you should
// use the row data
String recordId = (String) model.getValueAt(modelIndex, 0);
try (PreparedStatement pst = getConnection().prepareStatement("delete from recipe where recipe_name = ?")) {
pst.setString(1, recordId);
// You could check the number of rows effected by this change
pst.executeUpdate();
JOptionPane.showMessageDialog(TestPane.this, "Record deleted", "Success", JOptionPane.INFORMATION_MESSAGE);
model.removeRow(modelIndex);
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(TestPane.this, "Failed to delete row from database", "Error", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
@Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
现在,这只是一个示例,但基本思想是,我们已经为 MutableTableSupportable 和 TableRowDeletable 接口提供了实现(但 DeleteRowAction 并不关心“如何”)和我们已经实现了removeSelectedRow 功能以从TableModel 和数据库中删除行。
同样,DeleteRowAction 并不关心这是如何实现的,它只是委派了该责任,因此您可以拥有多个 DeleteRowActions,它们同时与不同的 TableModels 和数据源一起工作?
代表团?
好的,但是所有这些如何协同工作?嗯,其实,其实真的很容易
可运行示例...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.sql.*;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
@Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
@Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
@Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
@Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
@Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
nb:此示例删除了数据库支持,因为我没有,而是显示一条消息
好的,让我们快速浏览一下这里的一些有趣的东西......
首先...
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
我们将菜单栏注入到面板中。这样做是为了让面板可以根据需要配置菜单栏。我们可以在这里使用一种类型的工厂或其他委托,但我会留给您自己解决。
下一步...
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
我们构建JMenu 并添加删除行操作并创建JButton,使用相同的Action ... 对于五行代码,我们实际上已经做了很多。我们已经能够设置每个组件显示的文本、工具提示文本、加速键和助记符......尝试手动设置,然后需要在轨道上进行更改?(想要支持本地化 - 需要在一个位置进行更改)
但是等等,我们可以做得更多!! ?
如果我们添加...
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
到构造函数的最后,我们可以提供一个键绑定给用户,这样当JTable有键盘焦点并且点击他们Delete/Backspace kbd> 键,它也会触发动作!!!
现在我们有四种触发动作的方法:
- 按下按钮
- 打开并触发菜单项
- 使用菜单键盘“加速器”键绑定
- 点击删除键
行动?
键绑定可运行示例...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
@Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
@Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
@Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
@Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
@Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
但这还不是全部!我们还可以为JToolBar 添加一个按钮,因为,为什么不呢!?