использование собственного JComponent Swing в TableCellRenderer - PullRequest
0 голосов
/ 14 мая 2009

ОК, я знаю, как сделать простой пользовательский 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 (), то он перерисовывается только когда я щелкаю по соответствующей ячейке. Что дает? Должно быть, я упускаю что-то действительно простое.

Ответы [ 3 ]

2 голосов
/ 15 мая 2009

По соображениям производительности 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;
    }
}

Затем вы можете изменить Integer в вашей TableModel, и это вызовет перерисовку панели (вам может понадобиться TableModel.fireTableCellUpdated, зависящий от используемой реализации TableModel).

2 голосов
/ 19 мая 2009

Вы оба (Расс Хейворд и Эндрю) помогли, ключом по сути было сделать следующее:

  • сохранить состояние, которое будет сделано видимым в самой TableModel, а не в рендерере
  • убедитесь, что при изменении состояния TableModel, fireTableCellUpdated() называется
  • имеет только один объект TableCellRenderer и один JComponent для моего пользовательского столбца (не один на ячейку)
    • в TableCellRenderer.getTableCellRendererComponent() хранит состояние ячейки для целей визуализации вскоре после этого (долговременное хранение в TableModel)
    • предоставить это состояние JComponent
    • вернуть JComponent
    • переопределить JComponent.PaintComponent()
  • одна удобная возможность для пользовательского средства визуализации - расширить JComponent и реализовать TableCellRenderer, затем в TableCellRenderer.getTableCellRendererComponent() вы сохраняете состояние ячейки и return this;

Вот соответствующая выдержка из моего кода, которая теперь работает:

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;
    }
}
1 голос
/ 15 мая 2009

Если вы создаете таблицу, скажем, с 3 строками, каждая из которых имеет свой Xval, то правильно ли он изначально отображается, что означает, что каждая ячейка имеет различный вид строки?

Когда вы говорите, что он не перерисовывается, пока вы не щелкнете по нему, произошло ли что-то с вашими базовыми данными, что должно было привести к изменению визуального отображения данных (визуализированной панели)?

Если данные изменились, но таблица сразу не рендерится, то я бы сказал, что ваша TableModel не работает должным образом.

изменения основных данных -> изменения TableModel -> запускает TableModelEvent -> JTable повторно отображает

Посмотрите на учебник TableModel: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#data

чтобы убедиться, что вы все делаете правильно.

...