Ячейки в JTable изменяют внешний вид после горизонтальной прокрутки - PullRequest
0 голосов
/ 29 октября 2019

В моем приложении Java 8 JTable используется внутри JScrollPane. В настоящее время таблица содержит более 10 столбцов, и данные добавляются с использованием DefaultTableModel 's addRow(someObjectArray). Все ячейки в столбце в настоящее время имеют один и тот же тип данных, но столбцы 4+ могут содержать ячейки со значением «ноль» (0-3 всегда содержат данные! = Ноль!).

Если данные в первых трех столбцахне совпадают с данными в предыдущей строке, первые три ячейки в текущей строке написаны черным, жирным шрифтом, в противном случае они используют обычный, простой и темно-серый шрифт - это просто способ отметить началоновых данных:

enter image description here

При заполнении таблицы и прокрутке по вертикали шрифт жирный и обычный шрифт устанавливаются правильно, но при прокрутке вправо, а затем обратно к первым трем столбцамжирный шрифт испорчен: иногда он устанавливается для каждой ячейки, иногда для неправильных ячеек, а иногда меняет середину текста:

enter image description here

ПрокруткаВниз и вверх сбрасывает ячейки так, как они должны выглядеть.

Вот как настроена таблица:

JTable myTable = new JTable() {
    @Override
    public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
        Component c = super.prepareRenderer(renderer, row, column);

        if (column<3) {
            if(row==0) { //Always mark column 0-2 in row 0
                c.setFont(c.getFont().deriveFont(Font.BOLD));
                c.setForeground(Color.BLACK);
            } else {
                if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
                    Object prevColumnA = getValueAt(row-1, column); //String
                    Object currColumnA = getValueAt(row, column); //String
                    Object prevColumnB = getValueAt(row-1, column+1); //int
                    Object currColumnB = getValueAt(row, column+1); //int
                    Object prevColumnC = getValueAt(row-1, column+2); //String
                    Object currColumnC = getValueAt(row, column+2); //String

                    if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
                        markRow = true;
                        c.setFont(c.getFont().deriveFont(Font.BOLD));
                        c.setForeground(Color.BLACK);
                    } else {
                        markRow = false;
                        c.setFont(c.getFont().deriveFont(Font.PLAIN));
                        c.setForeground(Color.DARK_GRAY);
                    }
                } else { //Mark column 1-2 (or not)
                    if(markRow) {
                        c.setFont(c.getFont().deriveFont(Font.BOLD));
                        c.setForeground(Color.BLACK);
                    } else {
                        c.setFont(c.getFont().deriveFont(Font.PLAIN));
                        c.setForeground(Color.DARK_GRAY);
                    }
                }
            }
        } else {
            c.setFont(c.getFont().deriveFont(Font.PLAIN));
            c.setForeground(Color.DARK_GRAY);
        }

        //System.out.println("row="+row+", column="+column+", markRow="+markRow);
        return c;
    }
};

Как это исправить?

Редактировать: MRE скопировано с чдо :

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class SomeMRE extends JPanel {
    boolean markRow = false;

    public SomeMRE() {
        JTable myTable = new JTable() {
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                Component c = super.prepareRenderer(renderer, row, column);

                if (column<3) {
                    if(row==0) { //Always mark column 0-2 in row 0
                        c.setFont(c.getFont().deriveFont(Font.BOLD));
                        c.setForeground(Color.BLACK);
                    } else {
                        if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
                            Object prevColumnA = getValueAt(row-1, column); //String
                            Object currColumnA = getValueAt(row, column); //String
                            Object prevColumnB = getValueAt(row-1, column+1); //int
                            Object currColumnB = getValueAt(row, column+1); //int
                            Object prevColumnC = getValueAt(row-1, column+2); //String
                            Object currColumnC = getValueAt(row, column+2); //String

                            if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
                                markRow = true;
                                c.setFont(c.getFont().deriveFont(Font.BOLD));
                                c.setForeground(Color.BLACK);
                            } else {
                                markRow = false;
                                c.setFont(c.getFont().deriveFont(Font.PLAIN));
                                c.setForeground(Color.DARK_GRAY);
                            }
                        } else { //Mark column 1-2 (or not)
                            if(markRow) {
                                c.setFont(c.getFont().deriveFont(Font.BOLD));
                                c.setForeground(Color.BLACK);
                            } else {
                                c.setFont(c.getFont().deriveFont(Font.PLAIN));
                                c.setForeground(Color.DARK_GRAY);
                            }
                        }
                    }
                } else {
                    c.setFont(c.getFont().deriveFont(Font.PLAIN));
                    c.setForeground(Color.DARK_GRAY);
                }

                //System.out.println("row="+row+", column="+column+", markRow="+markRow);
                return c;
            }
        };

        myTable.setModel(new DefaultTableModel(
            new Object[][] {
                {"ColumnA text 1", 123, "ColumnC text 1", 1, null, "bla", "bla", "bla", null},
                {"ColumnA text 2", 234, "ColumnC text 2", 2, null, "bla", "bla", null, null},
                {"ColumnA text 2", 234, "ColumnC text 2", 3, null, "bla", "bla", "bla", null},
                {"ColumnA text 2", 234, "ColumnC text 2", 4, null, "bla", "bla", null, null},
                {"ColumnA text 2", 234, "ColumnC text 2", 5, null, "bla", null, null, null},
                {"ColumnA text 1", 123, "ColumnC text 1", 6, null, "bla", "bla", "bla", null},
                {"ColumnA text 2", 234, "ColumnC text 2", 7, null, "bla", null, null, null},
                {"ColumnA text 2", 234, "ColumnC text 2", 8, null, "bla", "bla", null, null},
                {"ColumnA text 2", 234, "ColumnC text 2", 9, null, "bla", "bla", null, null}
            },
            new String[] {
                "ColumnA", "ColumnB", "ColumnC", "ColumnD", "ColumnE", "ColumnF", "ColumnG", "ColumnH", "ColumnI"
            }
        ));

        setLayout( new BorderLayout() );
        add(new JScrollPane(myTable));
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("SomeMRE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SomeMRE());
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}

Ответы [ 2 ]

2 голосов
/ 30 октября 2019

Прежде всего, это НЕ в MRE. Ваша заявленная проблема была:

, но при прокрутке вправо и затем к первым трем столбцам жирный шрифт испортился:

Ну, код, который вы разместили, не 'не делайте горизонтальную прокрутку, чтобы вы не могли проверить опубликованный код, чтобы убедиться, что он демонстрирует вашу заявленную проблему.

Вы смотрели мой оригинальный комментарий об удалении переменной "markRow" и установке значений по умолчанию? рендеринг перед тестированием для жирных условий?

Когда я попробовал приведенное выше предложение, мне удалось значительно упростить код:

JTable myTable = new JTable()
{
    @Override
    public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
    {
        Component c = super.prepareRenderer(renderer, row, column);
        c.setForeground(Color.DARK_GRAY);

        if (column < 3)
        {
            if(row == 0) { //Always mark columns 0-2 in row 0
                c.setFont(c.getFont().deriveFont(Font.BOLD));
                c.setForeground(Color.BLACK);
            }
            else { // if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
                TableModel model = getModel();
                Object prevColumn0 = model.getValueAt(row-1, 0); //String
                Object currColumn0 = model.getValueAt(row, 0); //String
                Object prevColumn1 = model.getValueAt(row-1, 1); //int
                Object currColumn1 = model.getValueAt(row, 1); //int
                Object prevColumn2 = model.getValueAt(row-1, 2); //String
                Object currColumn2 = model.getValueAt(row, 2); //String

                if (!prevColumn0.equals(currColumn0)
                ||  !prevColumn1.equals(currColumn1)
                ||  !prevColumn2.equals(currColumn2))
                {
                    c.setFont(c.getFont().deriveFont(Font.BOLD));
                    c.setForeground(Color.BLACK);
                }
            }
        }

        return c;
    }
};

Насколько я понимаю, он перебирает ячейки, которыев настоящее время отображаются, а затем устанавливает визуализатор для каждого.

Не делайте предположений о порядке рендеринга. Каждая ячейка отображается независимо от других ячеек. Вот почему вы не хотите зависеть от переменной, которая установлена ​​из предыдущей визуализируемой ячейки.

Редактировать:

Лучшие getColumnClass(...) реализации будут выглядеть примерно так:

@Override
public Class getColumnClass(int column)
{
    switch (column)
    {
        case 2: return Date.class;
        default: return String.class;
    }
}

Вы использовали бы вышеупомянутый подход при создании пользовательской TableModel для POJO. См. Модель таблицы строк для пошагового подхода к созданию пользовательской модели.

Для более общей реализации, которую вы можете использовать при получении данных из базы данных, вы можете использовать:

@Override
public Class getColumnClass(int column)
{
    for (int row = 0; row < getRowCount(); row++)
    {
        Object o = getValueAt(row, column);

        if (o != null)
        {
            return o.getClass();
        }
    }

    return Object.class;
}
1 голос
/ 31 октября 2019

Лучше всего определять параметры отдельно для каждой ячейки отдельно, без использования переменной (markRow в вашем фрагменте кода), которая определяется по некоторой определенной координате ячейки (например, первая ячейка в строке).

Причина в том, что нет определенного способа вызова prepareRenderer. Нет гарантии, что этот метод будет сначала вызываться для первой ячейки в первой строке, затем для второй ячейки в первой строке и т. Д. Иными словами, не рассчитывайте на то, что prepareRenderer вызывается последовательно или в каком-либо определенном порядке.

Также обратите внимание, что компонент рендерера должен быть правильно инициализирован для каждой ячейки. Во фрагменте кода из первой версии вашего вопроса не было else части вашего начального if(column<3), что означает, что компонент мог быть произвольно инициализирован. Если существует дерево решений для подготовки рендерера, убедитесь, что все пути готовят рендерер так, как необходимо.

...