0

I want to add hairline cross highlighting to a JXTable. But I can either highlight the current row or the current column; never both.

What I currently have is the red part, the yellow part is what I want to add.

enter image description here

Below a SSCCE1.

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;

import org.jdesktop.swingx.JXTable;

public class Application
{

  public static void main(String[] args)
  {
    EventQueue.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        JFrame frame = new JFrame();
        JXTable table = new JXTable(new CustomTableModel());

        boolean highlightRow = false;
        table.setRowSelectionAllowed(highlightRow);
        table.setColumnSelectionAllowed(!highlightRow);

        table.setDefaultRenderer(Object.class, new CustomTableCellRenderer());

        frame.add(new JScrollPane(table));
        frame.setVisible(true);
        frame.pack();
      }
    });
  }

  public static class CustomTableCellRenderer extends DefaultTableCellRenderer
  {

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
      Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
      if (row == table.getSelectedRow()) { component.setBackground(Color.YELLOW); }
      if (column == table.getSelectedColumn()) { component.setBackground(Color.RED); }
      return component;
    }
  }

  static class CustomTableModel extends AbstractTableModel
  {
    private final List<Object[]> data = new ArrayList<>();

    public CustomTableModel()
    {
      data.add(new Object[] {1, 2, 3, "A", "Collection", "of", "Random", "Strings", 9, 10});
      data.add(new Object[] {1, 2, 3, "A", "Collection", "of", "Random", "Strings", 9, 10});
      data.add(new Object[] {1, 2, 3, "A", "Collection", "of", "Random", "Strings", 9, 10});
      data.add(new Object[] {1, 2, 3, "A", "Collection", "of", "Random", "Strings", 9, 10});
    }

    @Override
    public int getRowCount() { return data.size(); }

    @Override
    public int getColumnCount() { return 10; }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) { return data.get(rowIndex)[columnIndex]; }
  }

}

Update

Using the code and suggestions provided by @camickr yields this:

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

import org.jdesktop.swingx.JXTable;

public class Application
{

  public static void main(String[] args)
  {
    EventQueue.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        JFrame frame = new JFrame();

        DefaultTableModel model = new DefaultTableModel(5, 10);
        JXTable table = new JXTable(model)
        {
            @Override
            public Component prepareRenderer(
                TableCellRenderer renderer, int row, int column)
            {
                Component c = super.prepareRenderer(renderer, row, column);

                if (column == getSelectedColumn()) { c.setBackground(Color.RED); }
                else if (row == getSelectedRow()) { c.setBackground(Color.YELLOW); }
                else c.setBackground(getBackground());

                return c;
            }
        };

        frame.add(new JScrollPane(table));
        frame.setVisible(true);
        frame.pack();
      }
    });
  }

}

But unfortunately I have problems with artifacts now. Similar to the picture below (which is taken from a version which is colored in red only).

enter image description here


I'm using Java 1.8.0_74 on Windows 10.0.16299 Build 16299. JXTable is from

        <dependency>
            <groupId>org.swinglabs.swingx</groupId>
            <artifactId>swingx-all</artifactId>
            <version>1.6.5-1</version>
        </dependency>
mike
  • 4,929
  • 4
  • 40
  • 80
  • I give up. You post your code which "works", but is completely wrong because repaint() should NOT be invoked from the prepareRenderer() method. Where is the `System.out.println(...)` statement added to the prepareRenderer() method that will shows this method runs in an endless loop? Why you would even consider this is beyond me. Yet you still have not posted my code, (which I say works on my machine), as a proper [mcve] for others to copy/paste/compile/test. – camickr May 23 '18 at 15:25
  • I also stated in my original answer that a JTable worked with your original code. So a different approach may be to not use the JXTable. This class is no longer supported. What functionality do you need? Maybe there is an easy way to add this to JTable. – camickr May 23 '18 at 15:27
  • "Your solution" is the second example. Did I get it wrong? – mike May 23 '18 at 15:27
  • It uses the CustomTableModel, which is irrelevant to the question. Anybody looking at this want the code a simple as possible using standard JDK classes. – camickr May 23 '18 at 15:28
  • Thought you wanted it to conserve the orignal code...I'll fix it. – mike May 23 '18 at 15:30
  • As to `repaint()`: I do get that it is wrong, but it "works" or to be specific: using the statement superficially yields the desired behavior. So for now it unfortunately is the best non-solution I've got. – mike May 23 '18 at 15:36
  • If your point is, that bad programming practices should not be openly displayed. Maybe that's a discussion for meta... – mike May 23 '18 at 15:40
  • 1
    I deleted my original answer and added back in my answer to highlight why a proper [mcve] is important to make sure suggestions are implemented properly. – camickr May 23 '18 at 15:47
  • And I have deleted the `repaint` code, since there is a proper solution now. Thank you! – mike May 23 '18 at 15:57

1 Answers1

1

It seems to work when using a JTable (although the order of the if statements needs to be changed if you want the column to have priority). So it seems that JXTable does some additional rendering that is causing the problem.

So here is an alternative approach based on Table Row Rendering that appears to work for JXTable:

JXTable table = new JXTable(new CustomTableModel())
{
    public Component prepareRenderer(
        TableCellRenderer renderer, int row, int column)
    {
        Component c = super.prepareRenderer(renderer, row, column);

        if (column == getSelectedColumn()) { c.setBackground(Color.RED); }
        else if (row == getSelectedRow()) { c.setBackground(Color.YELLOW); }
        else c.setBackground(getBackground());

        return c;
    }
};

The above code replaces the custom renderer:

//table.setDefaultRenderer(Object.class, new CustomTableCellRenderer());

Note: in your "MCVE" of my suggestion you removed:

boolean highlightRow = false;
table.setRowSelectionAllowed(highlightRow);
table.setColumnSelectionAllowed(!highlightRow);

Why? I only gave you the above 2 changes.

This is why you need to post a proper "MCVE" so we can be sure you implemented the suggestion properly.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • To be fair: I only left out the `CustomTableModel` in the MCVE -- but this is not constructive and I understand why it is important! – mike May 23 '18 at 15:52
  • Can you explain why the code is working, and only working with, row selection set to `false` and column seledtion set to `true`? – mike May 23 '18 at 15:53