【问题标题】:How do I correctly use custom renderers to paint specific cells in a JTable?如何正确使用自定义渲染器来绘制 JTable 中的特定单元格?
【发布时间】:2012-03-25 08:18:45
【问题描述】:

我的 GUI 中有一个 JTable 组件,它显示算法的psuedocode。我想通过更改特定单元格的背景然后更改下面的单元格等等来突出显示当前的执行行。

现在我的代码改变了我的 JTable 中所有单元格的背景,如下图所示:

我用来存档当前状态的代码如下:

class CustomRenderer extends DefaultTableCellRenderer 
{
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
            JLabel d = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            if((row == 0) && (column == 0))
                d.setBackground(new java.awt.Color(255, 72, 72));
            return d;
        }
    }

然后我在构造函数中调用jTable2.setDefaultRenderer(String.class, new CustomRenderer());

我假设:

  • 对每个字符串类型的表格单元格调用此方法。
  • 这只会改变位置 (0,0) 处单元格的颜色

如何修复我的代码以使只有单元格 (0,0) 着色?

【问题讨论】:

  • 您发布的代码有什么问题?有用吗?
  • 它可以编译,但是当我只想将第一个单元格涂成红色时,它会将所有 10 个单元格涂成红色。
  • +1 将您的“大”问题分解为较小部分的好主意 :-)

标签: java swing jtable


【解决方案1】:

您忘记了 if 块的 else 部分,如果它是 不是重要行,则将背景绘制为默认值的代码:

        if (row == 0 && column == 0) {
           d.setBackground(new java.awt.Color(255, 72, 72));
        } else {
           d.setBackground(null);
        }

我的SSCCE

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class TestJTable {
   private static int highlightedRow = 0;
   private static void createAndShowGui() {
      String[] columnNames = {"Program"};
      Object[][] rowData = {{"Row 1"}, {"Row 2"}, {"Row 3"}, {"Row 4"}, 
            {"Row 1"}, {"Row 2"}, {"Row 3"}, {"Row 4"}, 
            {"Row 1"}, {"Row 2"}, {"Row 3"}, {"Row 4"}};
      final JTable myTable = new JTable(rowData , columnNames );
      myTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer()
      {
         @Override
         public Component getTableCellRendererComponent(JTable table,
               Object value, boolean isSelected, boolean hasFocus, int row,
               int column) {
            Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                  row, column);
            if (row == highlightedRow && column == 0) {
               c.setBackground(new java.awt.Color(255, 72, 72));
            } else {
               c.setBackground(null);
            }
            return c;
         }
      });


      JFrame frame = new JFrame("TestJTable");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new JScrollPane(myTable));
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);

      new Timer(1000, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            highlightedRow++;
            int rowCount = myTable.getRowCount();
            highlightedRow %= rowCount;
            myTable.repaint();
         }
      }).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

【讨论】:

  • +1 很棒的代码,但有(不重要)两点 1)我认为对于与模型或视图相关的任何东西都存在 prepareRenderer,2)嗯,为什么不测试isSelected 和 hasFocus
  • @mKorbel:你比我更了解 JTables。您可能想在这里展示您自己的 SSCCE,以便我们都可以接受这方面的教育。
  • 关于 down_votes 的那些,我记得大部分...,我认为 OP 有两个很好的答案,为什么要从杯子里往海里倒水
  • Ash 之前的问题表明,覆盖 prepareRenderer() 也可能会有所帮助。这个answer 指向一个comparison,其中包括有用的cmets。
  • 看看我的想法,为什么这有一个很小的缺点:-)
【解决方案2】:

if 中添加 else 子句:

if ((row == 0) && (column == 0)) {
    d.setBackground(new java.awt.Color(255, 72, 72));
}
else {
    d.setBackground(Color.WHITE);
}

请记住,相同的渲染器实例用于绘制所有单元格。

【讨论】:

  • 这行得通。既然你先回答了,我就奖励你答案。
  • 对不起鳗鱼,如果我可以选择两者,我会的。我也 +1 你的回答。
  • 其实我没有先回答。
  • 您在渲染器渲染第一个单元格时修改背景颜色。然后重复使用相同的实例来渲染所有其他单元格,并且由于您没有为它们重置背景颜色,它会保留您为第一个单元格设置的颜色。
  • @Ash:渲染器先应用到第0行,再应用到其他行。由于它已经设置为使用红色背景,因此渲染器不会更改颜色。请注意,您在这里看到的不是多个 JLabel,而是看到 一个 JLabel,它被用作橡皮图章,就像不同的标签一样显示。
【解决方案3】:

这不是一个答案 (*),对于两个答案的评论都太长了:两者都是正确的,因为 else 块是确保默认颜色用于不应该使用的单元格的重要事项突出显示。他们在如何达到这一点上犯了一些错误,两者的总体效果是一样的:他们错过了任何特殊的颜色,比如 f.i.由于选择,焦点,可编辑,dnd ...

他们通过不同的方式到达那个“错过”,效果略有不同

setBackground(Color.WHITE);

set 是一个固定的颜色,它可能是也可能不是默认的“正常”表格背景

setBackground(null);

设置没有颜色会导致显示“正常”背景颜色 - 由于 DefaultTableCellRenderer isOpaque 实现的内部技巧 :-)

问题的根本原因(也称为臭名昭著的色彩记忆,TM)是默认渲染器的异常糟糕的实现,使其基本上无法扩展:

 /**
 * Overrides <code>JComponent.setBackground</code> to assign
 * the unselected-background color to the specified color. 
 *
 * JW: The side-effect is documented and looks innocent enough :-) 
 */
public void setBackground(Color c) {
    super.setBackground(c); 
    unselectedBackground = c; 
}

// using that side-effect when configuring the colors in getTableCellRendererComp
// is what leads to the horrendeous problems
// in the following lines of the else (not selected, that is normal background color)
 Color background = unselectedBackground != null
           ? unselectedBackground : table.getBackground();
 super.setBackground(background);

看到这一点,出路(除了使用 SwingX 及其灵活、干净、强大、一致的 .. :-) 渲染器支持是 @Hovercraft 的,但相反:首先进行自定义着色(如果没有意图,则为 null)然后调用超级:

  @Override
  public Component getTableCellRendererComponent(JTable table,
        Object value, boolean isSelected, boolean hasFocus, int row,
        int column) {
      if (myHighlightCondition) {
          setBackground(Color.RED);
      } else {
          setBackground(null);
      }
     super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
           row, column);
     return this;
  }

(*) 毕竟,这条评论导致了答案,忘记了它可以在自定义渲染器级别修复:-)

顺便说一句:捕捉对渲染器的“第一次”调用非常脆弱,没有保证会发生哪个单元格,很可能是最后一列的最后一行。

【讨论】:

  • 谢谢,我已经在我的实现中解决了这个问题。它实际上适用于我的 HashMap 存储,因为如果没有存储任何值,则返回 null:D 您应该考虑对此进行社区 wiki QA。
猜你喜欢
  • 2012-08-05
  • 1970-01-01
  • 2011-11-08
  • 2011-10-02
  • 1970-01-01
  • 2013-12-16
  • 2012-10-03
  • 1970-01-01
  • 2019-01-31
相关资源
最近更新 更多