在 TableCellRenderer 中使用自定义 Swing JComponent

发布于 2024-07-19 04:40:55 字数 1004 浏览 13 评论 0原文

好的,我知道如何制作一个简单的自定义 JComponent。 我知道如何重写 TableCellRenderer。 我似乎无法将两者结合起来。

这是我创建的一个示例 JComponent

public static class BarRenderer extends JComponent
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    public void setXval(double x) { 
        this.xval = x;
        repaint();
    }
    public double getXval() { return xval; }
}

它作为独立的 JComponent 工作得很好。 我调用 setXval(something) ,它更新得很好。 (编辑:我有一个定期更新数据的 Swing Timer)

但是如果这个组件是我在 TableCellRenderer.getTableCellRendererComponent() 中返回的组件,那么它只会在我单击相关单元格时重新绘制。 是什么赋予了? 我一定遗漏了一些非常简单的东西。

OK, I know how to make a simple custom JComponent. I know how to override a TableCellRenderer. I can't seem to combine the two.

Here's a sample JComponent I created:

public static class BarRenderer extends JComponent
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    public void setXval(double x) { 
        this.xval = x;
        repaint();
    }
    public double getXval() { return xval; }
}

It works fine as a standalone JComponent. I call setXval(something) and it updates just fine. (edit: I have a Swing Timer that updates the data periodically)

But if this component is something I return in TableCellRenderer.getTableCellRendererComponent(), then it only repaints when I click on the cell in question. What gives? I must be leaving out something really simple.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

断念 2024-07-26 04:40:55

出于性能原因,JTable 重用渲染器组件来绘制多个单元格 - 因此,当您在 JTable 中看到该组件时,它实际上并不存在于传统意义上的容器中存在于某个位置的组件中。 这意味着在渲染器组件上调用 repaint() 不会执行任何操作。

最有效的选择是将条形的整数值存储在 TableModel 中。 然后,您的 TableCellRenderer 将如下所示:

public class BarTableCellRenderer implements TableCellRenderer {
    private final BarRenderer rendererComponent = new BarRenderer(0, 10);

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        rendererComponent.setXval((Integer)value);
        return rendererComponent;
    }
}

然后,您可以更改 TableModel 中的 Integer,它将触发栏的重新绘制(您可能需要 TableModel.fireTableCellUpdated ,具体取决于您正在使用的 TableModel 实现)。

For performance reasons a JTable reuses renderer components to paint multiple cells - so when you see the component in the JTable it isn't actually there in the traditional sense of a Component in a Container which is present at a location. This means that calling repaint() on the renderer component does nothing.

The most effective option would be to store the Integer value of the bar in your TableModel. Your TableCellRenderer would then look something like this:

public class BarTableCellRenderer implements TableCellRenderer {
    private final BarRenderer rendererComponent = new BarRenderer(0, 10);

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        rendererComponent.setXval((Integer)value);
        return rendererComponent;
    }
}

Then you could change the Integer in your TableModel and it would trigger a repaint of the bar (you may need a TableModel.fireTableCellUpdated dependent on the TableModel implementation you are using).

我的奇迹 2024-07-26 04:40:55

你们俩(Russ Hayward 和 Andrew)都提供了帮助,关键本质上是执行以下操作:

  • 存储要在 TableModel 本身中可见的状态,而不是在渲染器中
  • 确保当 TableModel 的状态更改时, fireTableCellUpdated( ) 被称为
  • 只有 一个 TableCellRenderer 对象和 一个 JComponent 用于我的自定义列(不是每个单元格一个)
    • TableCellRenderer.getTableCellRendererComponent() 中存储单元格的状态,以便稍后渲染(长期存储在 TableModel 中)
    • 向 JComponent 提供该状态
    • 返回 JComponent
    • 覆盖JComponent.PaintComponent()
  • 一种方便的可能性是自定义渲染器扩展 JComponent 并实现 TableCellRenderer,然后在 TableCellRenderer.getTableCellRendererComponent() 中存储单元格的状态并返回此值;

这是我现在可以使用的代码的相关摘录:

class TraceControlTableModel extends AbstractTableModel {
    /* handle table state here */

    // convenience method for setting bar value (table model's column 2)
    public void setBarValue(int row, double x)
    {
        setValueAt(x, row, 2);
    }
}

// one instance of BarRenderer will be set as the
// TableCellRenderer for table column 2
public static class BarRenderer extends JComponent 
    implements TableCellRenderer 
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        super();
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    @Override
    public Component getTableCellRendererComponent(JTable arg0,
            Object value, 
            boolean isSelected, boolean hasFocus,
            int row, int col)
    {
        // save state here prior to returning this object as a component
        // to be painted
        this.xval = (Double)value;
        return this;
    }
}

Both of you (Russ Hayward and Andrew) helped, the key was essentially to do the following:

  • store the state to be made visible in the TableModel itself, not in the renderer
  • make sure that when the TableModel's state changes, fireTableCellUpdated() is called
  • have only one TableCellRenderer object and one JComponent for my custom column (not one per cell)
    • within TableCellRenderer.getTableCellRendererComponent() store the cell's state for purposes of being rendering soon after (long-term storage is in the TableModel)
    • provide that state to the JComponent
    • return the JComponent
    • override JComponent.PaintComponent()
  • one convenient possibility is for a custom renderer to extend JComponent and implement TableCellRenderer, then in TableCellRenderer.getTableCellRendererComponent() you store the cell's state and return this;

Here's the relevant excerpt of my code that now works:

class TraceControlTableModel extends AbstractTableModel {
    /* handle table state here */

    // convenience method for setting bar value (table model's column 2)
    public void setBarValue(int row, double x)
    {
        setValueAt(x, row, 2);
    }
}

// one instance of BarRenderer will be set as the
// TableCellRenderer for table column 2
public static class BarRenderer extends JComponent 
    implements TableCellRenderer 
{
    final private double xmin;
    final private double xmax;
    private double xval;
    public BarRenderer(double xmin, double xmax)
    {
        super();
        this.xmin=xmin;
        this.xmax=xmax;
    }

    @Override protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Rectangle r = g.getClipBounds();
        g.drawRect(r.x, r.y,
                (int)(r.width * ((xval-xmin)/(xmax-xmin))), r.height);
    }

    @Override
    public Component getTableCellRendererComponent(JTable arg0,
            Object value, 
            boolean isSelected, boolean hasFocus,
            int row, int col)
    {
        // save state here prior to returning this object as a component
        // to be painted
        this.xval = (Double)value;
        return this;
    }
}
萌化 2024-07-26 04:40:55

如果您制作一个包含 3 行的表格,每行都有不同的 Xval,那么它最初渲染器是否正确,这意味着每个单元格都有不同的外观栏?

当您说除非单击它否则它不会重新绘制时,您的基础数据是否发生了一些应该导致数据(渲染栏)的视觉显示发生变化的事情?

如果数据发生了变化,但表没有立即重新渲染,那么我会说你的 TableModel 无法正常工作。

底层数据变化-> TableModel 更改 -> 触发 TableModelEvent -> JTable 重新渲染

查看 TableModel tuturial: http: //java.sun.com/docs/books/tutorial/uiswing/components/table.html#data

确保您所做的一切都是正确的。

If you make a table with say 3 rows, each having a different Xval, then does it initially renderer correctly, meaning each cell has a different looking bar?

When you say it does not repaint unless you click it, has something happened to your underlying data that should have caused the visual display of the data (the rendered bar) to change?

If the data changed, but the table does not immediatley re-render, then I would say that your TableModel is not working properly.

underlying data changes -> TableModel changes -> fires TableModelEvent -> JTable re-renders

Look at the TableModel tuturial: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#data

to make sure you are doing everything correct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文