繁体   English   中英

JTable 中的单元格在水平滚动后改变外观

[英]Cells in JTable change appearance after scrolling horizontally

我的 Java 8 应用程序在JScrollPane内使用JTable 目前该表有超过 10 列,数据是使用DefaultTableModeladdRow(someObjectArray)添加的。 列中的所有单元格当前具有相同类型的数据,但列 4+ 可以包含具有“null”的单元格(0-3 始终包含数据。= null!)。

如果前三列的数据与上一行的数据不同,则当前行的前三个单元格用黑色、粗体字书写,否则使用普通、纯色和深灰色字体——它只是一种标记新数据开始的方法:

在此处输入图像描述

填充表格并垂直滚动正确设置粗体字体和普通字体,但向右滚动然后回到前三列,粗体字体被搞砸了:有时为每个单元格设置,有时为错误的单元格设置,有时在中间更改-文本:

在此处输入图像描述

再次向下和向上滚动会将单元格重置为应有的样子。

表是这样设置的:

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() );
    }
}

首先,不是 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;
    }
}

在为 POJO 创建自定义 TableModel 时,您将使用上述方法。 有关创建自定义 model 的分步方法,请参见行表Model。

对于从数据库获取数据时可能使用的更通用的实现,您可以使用:

@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;
}

最好的方法是以独立的方式单独确定每个单元格的设置,而不使用在某个特定单元格坐标(例如,一行中的第一个单元格)处确定的变量(代码片段中的markRow )。

原因是没有确定调用prepareRenderer的方式。 不能保证首先为第一行中的第一个单元格调用此方法,然后为第一行中的第二个单元格调用此方法,依此类推。 换句话说,不要指望prepareRenderer以顺序方式或任何特定顺序被调用。

另请注意,应该为每个单元格正确初始化渲染器组件。 您问题的第一个版本的代码片段中,您的开始if(column<3)没有else部分,这意味着该组件可能已被任意初始化。 如果有准备渲染器的决策树,请确保所有路径都根据需要准备渲染器。

暂无
暂无

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

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