-2

I would like to know how to add code in my action listener that would make the row I select or press on with my mouse change from the color red to the color white. I have tried getRowSelected() and tried to use the index but that ultimately only changes the row color when it is selected and it goes back to red. I have also attempted to user Renderer which is a newer concept to me but didn't know how to implement it the right way. Any help or guidance would be appreciated.

Tried getRowSelected() but the row color change was only temporary and went back to red once it was unselected. Tried Renderer but didn't know how to fully implement it as it is a new concept to me.

1 Answers1

2

One way to do this is to:

  1. Create a JTable and table model with an invisible extra column, one that holds a boolean value, set to false initially. One way to make the column invisible is by removing the column from the JTable's TableColumnModel as suggested here by Swing expert Rob Camick and as per his previous answer to a Swing question on the topic.
  2. Create a table cell renderer that colors the background of a row based on the value of the boolean mentioned above
  3. Toggle the value of this boolean using either a mouse listener or a list selection listener.

For example:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;
import javax.swing.table.*;

public class TestTableRowColor {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ChangeRowColorPanel mainPanel = new ChangeRowColorPanel();

            JFrame frame = new JFrame("GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(mainPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

}
@SuppressWarnings("serial")
class ChangeRowColorPanel extends JPanel {
    private static final String[] COLUMN_NAMES = { "One", "Two", "Three", "Selected" };
    private DefaultTableModel model = new DefaultTableModel(COLUMN_NAMES, 0);
    private JTable table = new JTable(model);

    public ChangeRowColorPanel() {
        TableColumnModel columnModel = table.getColumnModel();
        columnModel.removeColumn(columnModel.getColumn(columnModel.getColumnCount() - 1));
        table.setDefaultRenderer(Object.class, new RowColorRenderer());
        table.addMouseListener(new MyMouse());

        int max = 5;
        for (int i = 0; i < max; i++) {
            Object[] row = new Object[COLUMN_NAMES.length];
            for (int j = 0; j < COLUMN_NAMES.length - 1; j++) {
                row[j] = (int) (100 * Math.random());
            }
            row[COLUMN_NAMES.length - 1] = false;
            model.addRow(row);
        }

        setLayout(new BorderLayout());
        add(new JScrollPane(table));
    }
}
class MyMouse extends MouseAdapter {
    @Override
    public void mousePressed(MouseEvent e) {
        JTable table = (JTable) e.getSource();
        TableModel model = table.getModel();
        boolean selected = (boolean) model.getValueAt(table.getSelectedRow(), model.getColumnCount() - 1);
        model.setValueAt(!selected, table.getSelectedRow(), model.getColumnCount() - 1);
        table.repaint();
    }
}
@SuppressWarnings("serial")
class RowColorRenderer extends DefaultTableCellRenderer {
    private static final Color SELECTED_COLOR = Color.PINK;

    public RowColorRenderer() {
        setOpaque(true);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        TableModel model = table.getModel();
        int selectedColumn = model.getColumnCount() - 1;
        boolean selected = (boolean) model.getValueAt(row, selectedColumn);
        Color background = selected ? SELECTED_COLOR : null;
        renderer.setBackground(background);
        return this;
    }

}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • 1
    Swing really needs a "renderer chain" concept, where you can take series of renderers and chain then together (so you can make use of the default renders for the cell type and then chain additional renderers together) ... what could possibly go wrong – MadProgrammer Dec 03 '22 at 23:58
  • @camickr: I actually tried that first, I removed a table column, but then I had difficulty accessing and changing the data held in the removed column. I will try it again to see where I went wrong. – Hovercraft Full Of Eels Dec 04 '22 at 00:55
  • 1
    @camickr: changes made as per your suggestions above and [your answer here](https://stackoverflow.com/a/19758630/522444), and they (of course) work. Many thanks! – Hovercraft Full Of Eels Dec 04 '22 at 02:02
  • well .. that's the problem with suboptimal questions: we all are playing guessing games - which is attractive, particularly when choosing and trying to solve an _interesting_ aspect :) - as to what the problem __really__ is. Still guessing (a bit guided by the other answer having been accepted): here working through a tutorial on how to implement custom renderers would suffice. Also @MadProgrammer – kleopatra Dec 04 '22 at 12:08
  • @MadProgrammer the approach in SwingX - exhumation might reveal the one and other pearl ;) - is to apply decorators to the renderer: separated in content (String/IconValue) and visuals (Highlighter for colors, borders, fonts .. ). While the swingx implementation is rather elaborate (cross component for all tree/table/list), the basic idea can be implemented in table's prepareRenderer: let super do it's work and then apply whatever different config is needed. Doing so would not require to implement the visual deco in every type of renderer – kleopatra Dec 04 '22 at 12:31
  • as to the implementation here: the repaint after setting the value bothers me (as does the idea of the "hidden" column: the status column in the original code example - now deleted in the question) was visible). I'm aware that the default model fires an update for a single cell - thus not updating the rest of the row. But then: also in the deleted example, the model was extended anyway - so going one step further and firing a rowUpdate on status value change would make the manual repaint obsolete. – kleopatra Dec 04 '22 at 12:38