行、模型行和视图行的有趣行为——什么时候使用哪一个?

Interesting behaviour of row, model row and view row - which one to use when?

这很难解释,我会尝试逐步解释,运行 并排代码会有所帮助,但查看图像应该足以理解我的问题。

row和rowModel分别与数据显示和数据模型一致,但我完全看不懂rowView。 rowView什么时候有用?

1) 默认视图:B 列 (1,2,3,4)。

2) 点击B列排序 header:升序排序(1,2,3,4)。总是 dataRow = modelRow = viewRow.

3) 点击排序 B 列 header: 降序排列 (4,3,2,1).

行 0 = 行模型 3 = 行视图 3

行 1 = 行模型 2 = 行视图 2

行 2 = rowModel 1 = rowView 1

行 3 = rowModel 0 = rowView 0

一切如期!

4) 现在点击排序C列:升序排序(下图)

第 0 列:值“4”——第 0 行 = rowModel 3 = rowView 2 --- 为什么 rowView 是 2?

第 1 列:值“1”-- 行 0 = rowModel 3 = rowView 2 --- 为什么 rowView 为 2 且在剩余单元格中的行为一致,如下所示?

第 0 列:值“3”——第 1 行 = rowModel 2 = rowView 3 --- 为什么 rowView 是 3?

第 1 列:值“3”——第 1 行 = rowModel 2 = rowView 3 --- 为什么 rowView 是 3?

第 0 列:值“1”——第 2 行 = rowModel 0 = rowView 1 --- 为什么 rowView 是 1?

第 1 列:值“4”——第 2 行 = rowModel 0 = rowView 1 --- 为什么 rowView 是 1?

第 0 列:值“2”——第 3 行 = rowModel 1 = rowView 0 --- 为什么 rowView 为 0?

第 1 列:值“5”——第 3 行 = rowModel 1 = rowView 0 --- 为什么 rowView 为 0?

代码:

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;

public class SortTableWithColors_ extends JFrame {
    public static void main(String[] args) {
        SortTableWithColors_ frame = new SortTableWithColors_();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public SortTableWithColors_() {
        Object[] columnNames = {"B", "C"};
        Object[][] data = {{new Integer(1), new Integer(4)},
                {new Integer(2), new Integer(5)},
                {new Integer(3), new Integer(3)},
                {new Integer(4), new Integer(1)}};
        // table model
        DefaultTableModel model = new DefaultTableModel(data, columnNames);

        // set table model in Jtable
        JTable table = new JTable(model);
        table.setAutoCreateRowSorter(true);
        getContentPane().add(new JScrollPane(table));

        // Tell the table what to use to render our columns
        for (int i = 0; i < 2; i++) {
            table.getColumnModel().getColumn(i).setCellRenderer(new NewRenderer());
        }
    }

    // Custom Renderer
    public class NewRenderer extends DefaultTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent
                (JTable table, Object value, boolean isSelected,
                 boolean hasFocus, int row, int column) {
            JLabel cell = (JLabel) super.getTableCellRendererComponent
                    (table, value, isSelected, hasFocus, row, column);

            int rowModel = (int) table.convertRowIndexToModel(row);
            int colModel = (int) table.convertColumnIndexToModel(column);
            int rowView = (int) table.convertRowIndexToView(row);
            int colView = (int) table.convertColumnIndexToView(column);

            // set color
            cell.setBackground(new Color(0xFFFFFF));
            cell.setForeground(new Color(0x000000));

            //set selection colors
            if (isSelected) {
                cell.setBackground(new Color(0x4AC3FF));
                cell.setForeground(new Color(0x000000)); // AM
            }
            // Selective cell colouring based on value
            int val = (int) value;
            if (val == 1) {
                cell.setBackground(Color.GREEN);
            }
            return cell;
        }
    }
 }

请注意,渲染器的 rowcolumn 值是 view 坐标,因此只有两个转换有意义:

int rowModel = (int) table.convertRowIndexToModel(row);
int colModel = (int) table.convertColumnIndexToModel(column);
//int rowView = (int) table.convertRowIndexToView(row);
//int colView = (int) table.convertColumnIndexToView(column);

为所选行添加一行显示括号中的模型坐标,以查看效果。未排序时,模型和视图索引相同:

if (isSelected) {
    …
    System.out.println(row + " (" + rowModel + ") " + column + " (" + colModel +")");
}

什么时候要特别警惕:

  • 因为用户可以通过拖动更改列顺序,所以在访问模型时进行转换,如 tooltips 所示。

  • 因为用户可以通过排序改变行顺序,所以在TableCellRendererTableCellEditorListSelectionListener中访问模型时进行转换,如图here.

TableModel中数据的顺序是固定的。

JTable 中数据的顺序可以更改,因为:

  1. JTable 可以过滤或排序,这意味着 JTable 的行号可以与 TableModel
  2. 中的行号不同
  3. 用户可以对列重新排序,这意味着 JTable 的列号可以与 TableModel 中的列号不同。

访问数据有两种方式:

  1. 使用 JTable 您可以使用:table.getValueAt(...);
  2. 使用 TableModel 你可以使用 table.getModel().getValueAt(...);

假设您有一个包含 2 列 "FirstName, LastName" 的 table,并且您想要将所选行的数据更新到数据库中,因此您需要获取 firstName 和 lastName 的值。

在处理 JTable 时,所有方法都使用 row/column 相对于 table 的索引。所以你可以使用像这样的代码:

String firstName = table.getValueAt(selectedRow, 0);
String lastName = table.getValueAt(selectedRow, 1);

即使对 table 进行了排序或筛选,这仍然有效,因为所选行是相对于 table。

但是,如果用户对这两列重新排序怎么办。该代码将不起作用,因为 硬编码索引将是错误的。所以你有两个选择:

1) 通过JTable获取数据但转换列索引:

String firstName = table.getValueAt(selectedRow, table.convertColumnIndexToView(0));
String lastName = table.getValueAt(selectedRow, table.convertColumnIndexToView(1));

所以现在两个 row/column 索引都是相对于 table.

2) 通过TableModel获取数据,但此时需要转换行索引:

TableModel model = table.getModel();
int modelRow = table.convertRowIndexToModel( selectedRow );
String firstName = model.getValueAt(modelRow, 0);
String lastName = model.getValueAt(modelRow, 1);

我更喜欢这种方法,因为只需要一次转换。

关键是你永远不会同时使用所有的转换方法。您需要决定是否要通过 JTableTableModel 访问数据,然后进行适当的转换。