JTable render stops rendering sometimes but resumes on window resize

好久不见. 提交于 2020-01-06 15:16:04

问题


I`ve got a mysterious problem with my custom JTable and a custom TableRenderer. In 95%-99,99% it works perfectly, but sometimes the renderer just stops doing his job, and leaves a portion of the table (which is inside a JScrollPane) blank.

The problem case looks like that:

In all other cases, and after a slight resize of the window, the Table look like that:

Now both columns has a TextAreaCellRenderer associated to, which works as follows:

public class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {

    private final Color evenColor = new Color(252, 248, 202);


    public TextAreaCellRenderer() {
        super();
        setLineWrap(true);
        setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    }


    @Override
    public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
        if (isSelected) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
        } else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
            setBackground((row % 2 == 0) ? evenColor : getBackground());
        }
        setWrapStyleWord(true);
        setFont(table.getFont());
        setText((value == null) ? "" : value.toString());
        return this;
    }
}

I also have to override the doLayout method of the JTable to be able to calculate the hight of a cell depending on the content. The custom table looks like that:

public class MediaMetaDataTable extends JTable {

    @Override
    public void doLayout() {
        TableColumn col = getColumnModel().getColumn(1);
        for (int row = 0; row < getRowCount(); row++) {
            Component c = prepareRenderer(col.getCellRenderer(), row, 1);
            if (c instanceof JTextArea) {
                JTextArea a = (JTextArea) c;
                int h = getPreferredHeight(a) + getIntercellSpacing().height;
                if (getRowHeight(row) != h) {
                    setRowHeight(row, h);
                }
            }
        }
        super.doLayout();
    }


    private int getPreferredHeight(final JTextComponent c) {
        Insets insets = c.getInsets();
        View view = c.getUI().getRootView(c).getView(0);
        int preferredHeight = (int) view.getPreferredSpan(View.Y_AXIS);
        return preferredHeight + insets.top + insets.bottom;
    }
}

The table is instantiated once with the following parameters:

metaTable = new MediaMetaDataTable();
metaTable.setModel(new MediaMetaDataTableModel());
metaTable.setEnabled(false);
metaTable.setShowGrid(false);
metaTable.setTableHeader(null);
metaTable.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());
metaTable.getColumnModel().getColumn(1).setCellRenderer(new TextAreaCellRenderer());
metaTable.setPreferredScrollableViewportSize(new Dimension(-1, -1));
metaTable.setShowHorizontalLines(false);
metaTable.setShowVerticalLines(false);

Each time the data to show changes i update table by replacing the underlying models data:

List<MediaMetaData> metaInformation = mediaSearchHit.getMetaInformation();
        if (metaInformation != null) {
            ((MediaMetaDataTableModel) metaTable.getModel()).replaceMetaInfos(metaInformation);
        }

On update the model itself fires a table data changed event:

public class MediaMetaDataTableModel extends AbstractTableModel {

    private List<MediaMetaData> metaInfos = new LinkedList<MediaMetaData>();

    public static final int COL_INDEX_NAME = 0;
    public static final int COL_INDEX_VALUE = 1;


    public void replaceMetaInfos(final List<MediaMetaData> metaInfos) {
        this.metaInfos = null;
        this.metaInfos = metaInfos;
        fireTableDataChanged();
    }
...

Now does anybody has a idea, what causes the described rendering problem?

Thanks for any advices.


回答1:


I also have to override the doLayout method of the JTable to be able to calculate the hight of a cell depending on the content.

To achieve this goal there's no need to override doLayout() method. I think the simplest way to do this is by adding the text area used to render the cell content into a JPanel with BorderLayout and set the row height based on the panel's preferred size. This way the layout manager will do the trick for you and all the cell's content will be visible:

@Override
public Component getTableCellRendererComponent(...) {
    ...
    JPanel contentPane = new JPanel(new BorderLayout());            
    contentPane.add(this);
    table.setRowHeight(row, contentPane.getPreferredSize().height); // sets row's height
    return contentPane;
}

As @mKorbel pointed out, there's no need to make the renderer extend from JTextArea: a single variable will work. Keeping this in mind take a look to this implementation based on your work:

class TextAreaRenderer implements TableCellRenderer {

    private JTextArea renderer;
    private final Color evenColor = new Color(252, 248, 202);

    public TextAreaRenderer() {
        renderer = new JTextArea();            
        renderer.setLineWrap(true);
        renderer.setWrapStyleWord(true);
        renderer.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        if (isSelected) {
            renderer.setForeground(table.getSelectionForeground());
            renderer.setBackground(table.getSelectionBackground());
        } else {
            renderer.setForeground(table.getForeground());
            renderer.setBackground((row % 2 == 0) ? evenColor : table.getBackground());
        }            
        renderer.setFont(table.getFont());
        renderer.setText((value == null) ? "" : value.toString());
        JPanel contentPane = new JPanel(new BorderLayout());            
        contentPane.add(renderer);
        table.setRowHeight(row, contentPane.getPreferredSize().height); // sets row's height
        return contentPane;
    }

}

Screenshot




回答2:


If I had to guess I would say it could be a concurency problem. Are you doing everything in the GUI-Thread? If yes, it can't be a concurency problem. Otherwhise try to call everything with Thread.InvokeLater() in an inital debug step, if you don't encounter the error anymore after a long time of testing, you know the cause of the problem.

In a second step you would then check exactly where it is necessary to make the calls with invokelater() and where not (because you shouldn't do that all the time, because it leads to very poor performance.

As I said, just a wild guess... It can of youre just be another bug. Are you using Java7? There are millions of Bugs in Swing with java 7 code (just all the code that from oracle came).



来源:https://stackoverflow.com/questions/20332134/jtable-render-stops-rendering-sometimes-but-resumes-on-window-resize

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!