Description
At the beginning selecting a row using mouse click or keyboard arrows is possible with the selected row being colored with the normal row selection color.
User can select single row.
User can lock the selected row using code invoked by lock button. Locking itself is made possible by imitating a selected row (which I'm not sure if it is the proper way of doing so) and is done by two things:
- Color the selected row using
DefaultTableCellRenderer
- Disable
JTable
row selectionsetRowSelectionAllowed(false)
- Color the selected row using
User can unlock/restore normal selection using code invoked by unlock button. Unlocking is simply to undo the steps of locking:
- Remove coloring of the selected row using
DefaultTableCellRenderer
- Enable
JTable
row selectionsetRowSelectionAllowed(true)
<-- (does not work)
- Remove coloring of the selected row using
How to restore normal JTable
row selection?
Code in SSCCE | MCVE format
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);
});
}
}
CodePudding user response:
Your problem is with you TableCellRenderer
The following...
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());
}
is overwriting the selection color made by the super
call.
Instead, change it to...
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;
}
};
One thing I might suggest is, instead of switching the cell renderer (which doesn't seem to be working), apply the cell renderer to the table and make use of the put/getClientProperty
support of the 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);
}
}
}
and...
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;
}
}
Runnable example...
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;
}
}
}
CodePudding user response:
Excelent answer from MadProgrammer. Here I am only using it with little updates; for me as a reference and for future visitors.
Instead of using some column value as unique identifier to track the selected row, use the
TableModel
row index; that is row content agnostic solution.When user unlocks the selected row; the same selected row is kept selected, for good user experience.
Code:
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;
}
}
}