简体   繁体   English

更新JTable单元格的ImageIcon

[英]Update ImageIcon of JTable cell

I am creating my first JTable that requires me to create a custom AbstractTableModel , TableCellEditor , and DefaultTableCellRenderer . 我正在创建我的第一个JTable,它要求我创建一个自定义AbstractTableModelTableCellEditorDefaultTableCellRenderer Given that I have not needed to create these before, I've made some significant progress in getting my table to behave as desired. 鉴于我以前不需要创建它们,因此我在使表按需运行方面取得了重大进展。

However, I am getting overwhelmed with all the different methods I am overriding, and am spinning my wheels trying to figure out how to modify the ImageIcon of a particular cell. 但是,我不知道要使用的所有不同方法,而且正在摸索着尝试找出如何修改特定单元格的ImageIcon。 The cell must contain a JLabel, as it needs both an ImageIcon as well as a text string . 该单元必须包含一个JLabel,因为它既需要 ImageIcon 也需要 一个文本字符串 I can already set the initial ImageIcon (although I am probably doing it incorrectly), but I can't set an updated ImageIcon . 我已经可以设置初始ImageIcon (尽管我可能做错了),但是我无法设置更新的ImageIcon Nothing fails, but no change is made. 没有失败,但是没有任何改变。

In a general sense, what is the best way to get and set an icon to a JLabel cell of a JTable , assuming all of these models, editors, and renderers have already been instantiated? 从一般意义上讲,假设已经实例化了所有这些模型,编辑器和渲染器,那么将图标设置到JTableJLabel单元并将其设置的最佳方法是什么?

My model has already been defined to return JLabel.class for these cells, if you're wondering, and I also do a fireTableCellUpdated(row, col) once the change has supposedly been made. 如果您想知道的话,已经定义了我的模型为这些单元格返回JLabel.class ,并且在假设进行了更改之后,我还会执行fireTableCellUpdated(row, col) If I do a System.out.println(getIcon()) before and after the update, I can even see the source has changed. 如果在更新前后执行System.out.println(getIcon()) ,我什至可以看到源已更改。

Here is some of the code (updated with URL/ImageIcon fix in place) : 这是一些代码(已使用URL / ImageIcon修复程序进行了更新)

class MonitorTable extends JTable {
   MonitorTableModel model = new MonitorTableModel(rows, columnNames);
   setModel(model);
   ...
   public void setIconAt(ImageIcon icon, int row, int col) {
      model.setIconAt(icon, row, col);
   } // End setIconAt(ImageIcon, int, int)
   ...

   class MonitorTableModel extends AbstractTableModel {
      ...
      public void setIconAt(ImageIcon icon, int row, int col) {
         StatusTableCellRenderer cell =
            (StatusTableCellRenderer)getColumnModel().getColumn(col).getCellRenderer().
            getTableCellRendererComponent(myTableObject, null, false, false, row, col);

         System.out.println(cell.getIcon()); // Shows initial icon source
         cell.setIcon(icon);
         fireTableCellUpdated(row, col);     // Should update the table
         System.out.println(cell.getIcon()); // Shows new icon source
         System.out.println("Cell updated");
      } // End setIconAt(ImageIcon, int, int)
   } // End class MonitorTableModel

   public class StatusTableCellRenderer extends DefaultTableCellRenderer {
      public Component getTableCellRendererComponent(JTable table, Object value,
         boolean isSelected, boolean hasFocus, int row, int col) {

         setIcon(imgGray);
         setText((String)value);
         return this;
      } // End getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
   } // End class StatusTableCellRenderer
} // End class MonitorTable

My model has already been defined to return JLabel.class for these cells, 我的模型已经定义为针对这些单元格返回JLabel.class,

But according to the code in your renderer you expect a String value in these cells: 但是根据渲染器中的代码,您希望这些单元格中具有String值:

setText((String)value); 

I don't like your setIcon() method. 我不喜欢您的setIcon()方法。 I would not pass in the URL. 我不会传递URL。 I would pass in the Icon. 我会通过图标。 Maybe you have a problem that the icon has not been read into memory at the time the cell is rendered. 可能存在一个问题,即在渲染单元时尚未将图标读入内存。

what is the best way to get and set an icon to a JLabel cell of a JTable, 获取并设置图标到JTable的JLabel单元格的最佳方法是什么,

You should not store a JLable in the TableModel. 您不应该在TableModel中存储JLable。 It is expensive to store Swing components in the model, that is why Swing components use renderers. 在模型中存储Swing组件非常昂贵,这就是Swing组件使用渲染器的原因。 Instead you store a custom Object like "LabelInfo" which contains two properties, the text and the Icon. 相反,您存储一个自定义对象,如“ LabelInfo”,其中包含两个属性,即文本和图标。 Then your custom renderer will extend the default renderer and invoke super.getTableCellRendererComponent(). 然后,您的自定义渲染器将扩展默认渲染器并调用super.getTableCellRendererComponent()。 You can then access your object and rest the text/icon properties of the renderer. 然后,您可以访问您的对象并保留渲染器的文本/图标属性。 You should not be creating objects in the renderer. 您不应该在渲染器中创建对象。

Now when you want to change something in the model you can do: 现在,当您想更改模型中的某些内容时,可以执行以下操作:

LabelInfo info = (LabelInfo)table.getValueAt(row, column);
info.setIcon(...);
table.setValueAt(info, row, column);

Thats all you need. 那就是您所需要的。 There is not custom code to repaint the cell or anything because that is already built intothe setValueAt(...) method. 没有自定义代码可以重画单元格或其他任何内容,因为setValueAt(...)方法中已经内置了该代码。 of your table model. 您的表格模型。

Edit: a simple example for using a custom Object in the TableModel. 编辑:在TableModel中使用自定义对象的简单示例。

1) to add the object to the model you do something like: 1)将对象添加到模型中,您可以执行以下操作:

LabelInfo info = new LabelInfo("some Text", yourIcon);
table.setValueAt(info, row, column);

2) the code for your custom renderer would be: 2)您的自定义渲染器的代码为:

class LabelInfoRenderer extends DefaultTableCellRenderer
{
    @Override
    public Component getTableCellRendererComponent(
        JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
        super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

        LableInfo info = (LabelInfo)value;
        setIcon( info.getIcon() );

        return this;
    }
}

Call the fireTableDataChanged from your model. 从模型中调用fireTableDataChanged。

Try call the repaint method from JLabel too. 也尝试从JLabel调用repaint方法。

The better way to do it, is implement a CellRenderer, with returns a JPanel and make the draw method in the paintComponent. 更好的方法是实现一个CellRenderer,它返回一个JPanel并在paintComponent中创建draw方法。 You can load a BufferedImage instead of a ImageIcon and use it to draw in the JPanel. 您可以加载BufferedImage而不是ImageIcon并将其用于绘制JPanel。

Try change you Renderer to: 尝试将您的渲染器更改为:

public class StatusTableCellRenderer extends DefaultTableCellRenderer {
     public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int col) {
        JLabel comp = new JLabel(new ImageIcon(imgGray));
        comp.setText((String)value);
        return comp;
     } // End getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
  }

You are doing some mess, this is not the right way to work with the TableModel, you should return the URL for the image in the getValueAt(int row, int col), after it, register the CellRenderer to correspond to cells with URL.class. 您正在做一些混乱,这不是使用TableModel的正确方法,应该在getValueAt(int row,int col)中返回图像的URL,然后注册CellRenderer以与具有URL的单元格相对应。类。 The renderer is automatically called from the JTable, you don't need to extends JTable either, you have only to implement the Renderer and the Model. 呈现器是从JTable自动调用的,您也不需要扩展JTable,只需实现Renderer和Model。 The setIconAt should only call the setValueAt and put your URL at the column, the renderer take care of the rest. setIconAt应该只调用setValueAt并将您的URL放在列中,渲染器负责其余的工作。

I fixed this by changing setIcon(imgGray) to if (getIcon() == null) setIcon(imgGray); 我通过将setIcon(imgGray)更改为if (getIcon() == null) setIcon(imgGray); .

The issue is my getTableCellRendererComponent method was setting the icon to imgGray every time. 问题是我的getTableCellRendererComponent方法每次都将图标设置为imgGray。 Apparently my setIconAt method, which calls getTableCellRendererComponent , was being overridden, even though the "new" icon value was processed after the "old" value was (re)set. 显然,我的setIconAt方法(调用getTableCellRendererComponent )已被覆盖,即使(重新)设置“旧”值后处理了“新”图标值。

I ended up removing all my setIcon methods and moved the relevant logic into my StatusTableCellRenderer class. 我最终删除了所有setIcon方法,并将相关逻辑移到了StatusTableCellRenderer类中。 That way I pass the value of the cell and let the renderer do the icon setting based on that value. 这样,我传递了单元格的值,然后让渲染器根据该值进行图标设置。 It makes more sense this way, and works beautifully. 这样更有意义,并且效果很好。 I have confirmed that initial setting and all subsequent updates are performing as expected. 我已确认初始设置和所有后续更新均按预期执行。

The logic of setting the icon is pretty simple -- set the predefined icon based on certain predefined threshold values. 设置图标的逻辑非常简单-根据某些预定义的阈值设置预定义的图标。

double val;
if (getIcon() == null) setIcon(imgGray);       // Initialize
if ((value == null) || (value == "")) {
   val = 0;
} else {
   val = Double.parseDouble(value.toString());
} // End if

if (val <= THRESHOLD1) {
   setIcon(icon1);
} else if (val <= THRESHOLD2) {
   setIcon(icon2);
...
} // End if
setText(value.toString());

I was very concerned about suggestions to make brand new objects to use, when the default JLabel was exactly what I needed. 当默认的JLabel正是我需要的东西时,我非常担心要使用全新的对象的建议。 It was both unnecessary and a potential performance hit to the JTable . 这既不必要,又可能JTable性能。 Thank you all for your insight and assistance. 谢谢大家的见解和帮助。 This was driving me batty! 这让我发疯了!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM