如何恢复正常的 JTable 行选择?撤消 table.setRowSelectionAllowed(false)
Posted
技术标签:
【中文标题】如何恢复正常的 JTable 行选择?撤消 table.setRowSelectionAllowed(false)【英文标题】:How to restore normal JTable row selection?; undo table.setRowSelectionAllowed(false) 【发布时间】:2022-01-23 05:19:05 【问题描述】:说明
在开始时可以使用鼠标单击或键盘箭头选择行,所选行使用正常的行选择颜色着色。
用户可以选择单行。
用户可以使用锁定按钮调用的代码锁定所选行。通过模仿选定的行(我不确定这是否是正确的做法)可以锁定自身,并通过两件事完成:
使用DefaultTableCellRenderer
为选定的行着色
禁用JTable
行选择setRowSelectionAllowed(false)
用户可以使用解锁按钮调用的代码解锁/恢复正常选择。解锁就是解除锁定的步骤:
使用DefaultTableCellRenderer
删除选定行的颜色
启用JTable
行选择setRowSelectionAllowed(true)
不起作用)
如何恢复正常的JTable
行选择?
SSCCE 中的代码 | MCVE 格式
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TableRowSelectionControl
private JButton btnJTableSelectionLocked;
private JButton btnJTableSelectionUnlock;
private JPanel panelControl;
private JScrollPane scrollableTable;
private Integer selectedId;
private boolean lockedSelection;
private Color rowSelectionColor;
private Integer modelRow;
private JTable table;
private Object[][] data;
private JPanel createPanel()
rowSelectionColor = new Color(184, 207, 229);
btnJTableSelectionLocked = new JButton("Lock selected row");
btnJTableSelectionLocked.setEnabled(false);
btnJTableSelectionLocked.addActionListener(new BtnAction());
btnJTableSelectionUnlock = new JButton("Unlock selection");
btnJTableSelectionUnlock.setEnabled(false);
btnJTableSelectionUnlock.addActionListener(new BtnAction());
panelControl = new JPanel();
panelControl.add(btnJTableSelectionLocked);
panelControl.add(btnJTableSelectionUnlock);
DefaultTableModel model = new DefaultTableModel(new String[]"Id", "Name", "State", 0)
// Disable cell editing
@Override
public boolean isCellEditable(int row, int column)
// Disable cells editing.
return false;
;
data = new Object[][]
1, "Alpha", true,
5, "Beta", false,
3, "Gama", true,
4, "Giga", true,
7, "Coca", true,;
table = new JTable(model);
table.getSelectionModel().setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(new RowSelectionListener());
for (Object[] d : data)
model.addRow(d);
JPanel containerPanel = new JPanel(new BorderLayout());
containerPanel.setPreferredSize(new Dimension(350, 200));
scrollableTable = new JScrollPane(table);
containerPanel.add(panelControl, BorderLayout.PAGE_START);
containerPanel.add(scrollableTable, BorderLayout.CENTER);
return containerPanel;
private class RowSelectionListener implements ListSelectionListener
@Override
public void valueChanged(ListSelectionEvent event)
// Ensure single event invoked
if (!event.getValueIsAdjusting() && !lockedSelection)
DefaultListSelectionModel selectionModel = (DefaultListSelectionModel) event.getSource();
if (selectionModel.isSelectionEmpty())
// Empty selection: table row deselection occurred
btnJTableSelectionLocked.setEnabled(false);
else
btnJTableSelectionLocked.setEnabled(true);
int viewRow = table.getSelectedRow();
if (viewRow > -1)
int idsColumn = 0;
modelRow = table.convertRowIndexToModel(viewRow);
Object selectedIdObject = table.getModel().getValueAt(modelRow, idsColumn);
selectedId = Integer.parseInt(selectedIdObject.toString());
private DefaultTableCellRenderer getRowsColorRenderer(Integer selectedId)
DefaultTableCellRenderer renderer;
renderer = new DefaultTableCellRenderer()
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
Component tableCellRendererComponent
= super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Integer id = (Integer) table.getModel().getValueAt(row, 0);
if (selectedId != null && id.equals(selectedId))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else
setBackground(table.getBackground());
setForeground(table.getForeground());
return tableCellRendererComponent;
;
return renderer;
private class BtnAction implements ActionListener
@Override
public void actionPerformed(ActionEvent e)
Object btnClicked = e.getSource();
int columnsSize = 3;
if (btnClicked == btnJTableSelectionLocked)
lockedSelection = true;
btnJTableSelectionLocked.setEnabled(false);
btnJTableSelectionUnlock.setEnabled(true);
table.setRowSelectionAllowed(false); // <-- Works fine.
for (int i = 0; i < columnsSize; i++)
table.getColumnModel().getColumn(i).setCellRenderer(getRowsColorRenderer(selectedId));
else if (btnClicked == btnJTableSelectionUnlock)
lockedSelection = false;
btnJTableSelectionLocked.setEnabled(true);
btnJTableSelectionUnlock.setEnabled(false);
table.setRowSelectionAllowed(true); // <-- This line does not restore normal selection
for (int i = 0; i < columnsSize; i++)
table.getColumnModel().getColumn(i).setCellRenderer(getRowsColorRenderer(null));
if (modelRow != null)
// Enforce the same row to be selected on unloking;
// afterwords user can select any row.
table.setRowSelectionInterval(0, modelRow);
table.repaint();
public static void main(String[] args)
javax.swing.SwingUtilities.invokeLater(() ->
TableRowSelectionControl tableRowColorControl = new TableRowSelectionControl();
JFrame frame = new JFrame("TableRowSelectionControl");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(tableRowColorControl.createPanel());
frame.pack();
frame.setVisible(true);
);
【问题讨论】:
【参考方案1】:你的问题是你TableCellRenderer
以下...
Component tableCellRendererComponent
= super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
//...
if (selectedId != null && id.equals(selectedId))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else
setBackground(table.getBackground());
setForeground(table.getForeground());
正在覆盖super
调用所做的选择颜色。
改为...
DefaultTableCellRenderer renderer;
renderer = new DefaultTableCellRenderer()
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
Component tableCellRendererComponent
= super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Integer id = (Integer) table.getModel().getValueAt(row, 0);
System.out.println("id = " + id + "; selectedId = " + selectedId + "; isSelected = " + isSelected);
if (selectedId != null && id.equals(selectedId))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else if (!isSelected)
setBackground(table.getBackground());
setForeground(table.getForeground());
return tableCellRendererComponent;
;
我可能建议的一件事是,不要切换单元格渲染器(这似乎不起作用),而是将单元格渲染器应用于表格并利用 put/getClientProperty
的 JTable
支持
private class BtnAction implements ActionListener
@Override
public void actionPerformed(ActionEvent e)
Object btnClicked = e.getSource();
int columnsSize = 3;
if (btnClicked == btnJTableSelectionLocked)
System.out.println("Lock");
lockedSelection = true;
btnJTableSelectionLocked.setEnabled(false);
btnJTableSelectionUnlock.setEnabled(true);
table.setRowSelectionAllowed(false); // <-- Works fine.
table.putClientProperty("selectedRowId", selectedId);
else if (btnClicked == btnJTableSelectionUnlock)
System.out.println("Unlock");
lockedSelection = false;
btnJTableSelectionLocked.setEnabled(true);
btnJTableSelectionUnlock.setEnabled(false);
table.setRowSelectionAllowed(true); // <-- This line does not restore normal selection
table.putClientProperty("selectedRowId", null);
还有……
public class LockableTableCellRenderer extends DefaultTableCellRenderer
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Integer id = (Integer) table.getModel().getValueAt(row, 0);
Integer selectedId = (Integer) table.getClientProperty("selectedRowId");
if (selectedId != null && id.equals(selectedId))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else if (!isSelected)
setBackground(table.getBackground());
setForeground(table.getForeground());
setBorder(noFocusBorder);
return tableCellRendererComponent;
可运行示例...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
public class Test
private JButton btnJTableSelectionLocked;
private JButton btnJTableSelectionUnlock;
private JPanel panelControl;
private JScrollPane scrollableTable;
private Integer selectedId;
private boolean lockedSelection;
private Color rowSelectionColor;
private Integer modelRow;
private JTable table;
private Object[][] data;
private JPanel createPanel()
rowSelectionColor = new Color(184, 207, 229);
btnJTableSelectionLocked = new JButton("Lock selected row");
btnJTableSelectionLocked.setEnabled(false);
btnJTableSelectionLocked.addActionListener(new BtnAction());
btnJTableSelectionUnlock = new JButton("Unlock selection");
btnJTableSelectionUnlock.setEnabled(false);
btnJTableSelectionUnlock.addActionListener(new BtnAction());
panelControl = new JPanel();
panelControl.add(btnJTableSelectionLocked);
panelControl.add(btnJTableSelectionUnlock);
DefaultTableModel model = new DefaultTableModel(new String[]"Id", "Name", "State", 0)
// Disable cell editing
@Override
public boolean isCellEditable(int row, int column)
// Disable cells editing.
return false;
;
data = new Object[][]
1, "Alpha", true,
5, "Beta", false,
3, "Gama", true,
4, "Giga", true,
7, "Coca", true,;
table = new JTable(model);
TableColumnModel columnModel = table.getColumnModel();
LockableTableCellRenderer cellRenderer = new LockableTableCellRenderer();
for (int column = 0; column < columnModel.getColumnCount(); column++)
columnModel.getColumn(column).setCellRenderer(cellRenderer);
table.getSelectionModel().setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(new RowSelectionListener());
for (Object[] d : data)
model.addRow(d);
JPanel containerPanel = new JPanel(new BorderLayout());
containerPanel.setPreferredSize(new Dimension(350, 200));
scrollableTable = new JScrollPane(table);
containerPanel.add(panelControl, BorderLayout.PAGE_START);
containerPanel.add(scrollableTable, BorderLayout.CENTER);
return containerPanel;
private class RowSelectionListener implements ListSelectionListener
@Override
public void valueChanged(ListSelectionEvent event)
// Ensure single event invoked
if (!event.getValueIsAdjusting() && !lockedSelection)
DefaultListSelectionModel selectionModel = (DefaultListSelectionModel) event.getSource();
if (selectionModel.isSelectionEmpty())
// Empty selection: table row deselection occurred
btnJTableSelectionLocked.setEnabled(false);
else
btnJTableSelectionLocked.setEnabled(true);
int viewRow = table.getSelectedRow();
if (viewRow > -1)
int idsColumn = 0;
modelRow = table.convertRowIndexToModel(viewRow);
Object selectedIdObject = table.getModel().getValueAt(modelRow, idsColumn);
selectedId = Integer.parseInt(selectedIdObject.toString());
private class BtnAction implements ActionListener
@Override
public void actionPerformed(ActionEvent e)
Object btnClicked = e.getSource();
int columnsSize = 3;
if (btnClicked == btnJTableSelectionLocked)
System.out.println("Lock");
lockedSelection = true;
btnJTableSelectionLocked.setEnabled(false);
btnJTableSelectionUnlock.setEnabled(true);
table.setRowSelectionAllowed(false); // <-- Works fine.
table.putClientProperty("selectedRowId", selectedId);
else if (btnClicked == btnJTableSelectionUnlock)
System.out.println("Unlock");
lockedSelection = false;
btnJTableSelectionLocked.setEnabled(true);
btnJTableSelectionUnlock.setEnabled(false);
table.setRowSelectionAllowed(true); // <-- This line does not restore normal selection
table.putClientProperty("selectedRowId", null);
public static void main(String[] args)
javax.swing.SwingUtilities.invokeLater(() ->
Test tableRowColorControl = new Test();
JFrame frame = new JFrame("TableRowSelectionControl");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(tableRowColorControl.createPanel());
frame.pack();
frame.setVisible(true);
);
public class LockableTableCellRenderer extends DefaultTableCellRenderer
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Integer id = (Integer) table.getModel().getValueAt(row, 0);
Integer selectedId = (Integer) table.getClientProperty("selectedRowId");
if (selectedId != null && id.equals(selectedId))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else if (!isSelected)
setBackground(table.getBackground());
setForeground(table.getForeground());
setBorder(noFocusBorder);
return tableCellRendererComponent;
【讨论】:
谢谢;像一个魅力;我会采纳你的建议。put/getClientProperty
对我来说是一个新 概念。这是否意味着当调用put
时,表格会以某种方式使用相应的get
? 重新呈现其内容。您可以粘贴任何资源(甚至来自*** Q&A)来了解它吗? 它是如何工作的,如何在自己的类中实现这种支持/实现。我用谷歌搜索了它,但没有找到有用的东西。
“这是否意味着当 put 被调用时...” - 不。这只是一种将键/值对与给定组件实例相关联的方法。奇怪的是,它不是经常被使用的东西【参考方案2】:
来自 MadProgrammer 的优秀 answer。在这里,我只使用它并进行少量更新;供我作为参考和未来的访问者使用。
不要使用某些列值作为唯一标识符来跟踪所选行,而是使用TableModel
行索引;这是行内容不可知解决方案。
当用户解锁所选行时;相同的选中行保持选中状态,以获得良好的用户体验。
代码:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
public class TableRowSelectionControl
private JButton btnJTableSelectionLock;
private JButton btnJTableSelectionUnlock;
private JPanel panelControl;
private JScrollPane scrollableTable;
private boolean lockedSelection;
private Color rowSelectionColor;
private final String selectedRowKey = "selectedRow";
private Integer selectedModelRow;
private Integer oldSelectedModelRow;
private JTable table;
private Object[][] data;
private JPanel createPanel()
rowSelectionColor = new Color(184, 207, 229);
btnJTableSelectionLock = new JButton("Lock selected row");
btnJTableSelectionLock.setEnabled(false);
btnJTableSelectionLock.addActionListener(new BtnAction());
btnJTableSelectionUnlock = new JButton("Unlock selection");
btnJTableSelectionUnlock.setEnabled(false);
btnJTableSelectionUnlock.addActionListener(new BtnAction());
panelControl = new JPanel();
panelControl.add(btnJTableSelectionLock);
panelControl.add(btnJTableSelectionUnlock);
DefaultTableModel model = new DefaultTableModel(new String[]"Id", "Name", "State", 0)
// Disable cell editing
@Override
public boolean isCellEditable(int row, int column)
// Disable cells editing.
return false;
;
data = new Object[][]
1, "Alpha", true,
5, "Beta", false,
3, "Gamma", true,
4, "Giga", true,
7, "Coca", true,;
table = new JTable(model);
TableColumnModel columnModel = table.getColumnModel();
LockableTableCellRenderer cellRenderer = new LockableTableCellRenderer();
for (int column = 0; column < columnModel.getColumnCount(); column++)
columnModel.getColumn(column).setCellRenderer(cellRenderer);
table.getSelectionModel().setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(new RowSelectionListener());
for (Object[] d : data)
model.addRow(d);
JPanel containerPanel = new JPanel(new BorderLayout());
containerPanel.setPreferredSize(new Dimension(350, 200));
scrollableTable = new JScrollPane(table);
containerPanel.add(panelControl, BorderLayout.PAGE_START);
containerPanel.add(scrollableTable, BorderLayout.CENTER);
return containerPanel;
private class RowSelectionListener implements ListSelectionListener
@Override
public void valueChanged(ListSelectionEvent event)
// Ensure single event invoked
if (!event.getValueIsAdjusting() && !lockedSelection)
DefaultListSelectionModel selectionModel = (DefaultListSelectionModel) event.getSource();
if (selectionModel.isSelectionEmpty())
// Empty selection: table row deselection occurred
btnJTableSelectionLock.setEnabled(false);
else
btnJTableSelectionLock.setEnabled(true);
int viewRow = table.getSelectedRow();
if (viewRow > -1)
selectedModelRow = table.convertRowIndexToModel(viewRow);
private class BtnAction implements ActionListener
@Override
public void actionPerformed(ActionEvent e)
Object btnClicked = e.getSource();
if (btnClicked == btnJTableSelectionLock)
System.out.println("Lock");
lockedSelection = true;
btnJTableSelectionLock.setEnabled(false);
btnJTableSelectionUnlock.setEnabled(true);
table.setRowSelectionAllowed(false);
oldSelectedModelRow = selectedModelRow;
table.putClientProperty(selectedRowKey, selectedModelRow);
else if (btnClicked == btnJTableSelectionUnlock)
System.out.println("Unlock");
lockedSelection = false;
btnJTableSelectionLock.setEnabled(true);
btnJTableSelectionUnlock.setEnabled(false);
table.setRowSelectionAllowed(true);
table.putClientProperty(selectedRowKey, null);
table.setRowSelectionInterval(0, oldSelectedModelRow);
public static void main(String[] args)
javax.swing.SwingUtilities.invokeLater(() ->
TableRowSelectionControl tableRowColorControl = new TableRowSelectionControl();
JFrame frame = new JFrame("TableRowSelectionControl");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(tableRowColorControl.createPanel());
frame.pack();
frame.setVisible(true);
);
public class LockableTableCellRenderer extends DefaultTableCellRenderer
@Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column)
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Integer renderedRow = table.convertRowIndexToModel(row);
Integer selectedRow = (Integer) table.getClientProperty(selectedRowKey);
if (selectedRow != null && renderedRow.equals(selectedRow))
setBackground(rowSelectionColor);
setForeground(table.getForeground());
else if (!isSelected)
setBackground(table.getBackground());
setForeground(table.getForeground());
setBorder(noFocusBorder);
return tableCellRendererComponent;
【讨论】:
以上是关于如何恢复正常的 JTable 行选择?撤消 table.setRowSelectionAllowed(false)的主要内容,如果未能解决你的问题,请参考以下文章