JTable 的排序+过滤+渲染

xiaoxiao2021-02-28  9

首先,jtable本身只是一个框架,它是根据tablemodel来显示数据的,我们能看到几行,table就有几行,但tablemodel

并非如此,因为可能只显示了其中的几行。

排序:

从jdk1.6开始,jtable可以通过点击header来排序了!使用方法如下:

TableRowSorter<DefaultTableModel> sorter= new TableRowSorter<DefaultTableModel>(tablemodel); jtable.setRowSorter(sorter); 可见,TableRowSorter需要一个底层的tablemodel来装载数据。一般选择重写tablemodel的方法来自定义排序规则,加快排序速度:

DefaultTableModel tablemodel = new DefaultTableModel(obj,goodsInfo) { private static final long serialVersionUID = 1L; //--设置不可编辑------- public boolean isCellEditable(int row,int column) { return false; } //--定义按类的比较规则排序--------- public Class<?> getColumnClass(int column) { Class<?> returnValue = Object.class; if ((column >= 0) && (column < getColumnCount())&& (getRowCount()>0)) { //排除某些cell是空的情况 for(int i=0;i<getRowCount();i++) if(getValueAt(i, column)!=null) returnValue = getValueAt(i, column).getClass(); } return returnValue; } };

过滤:

TableRowSorter有个方法是setRowFilter,可以实现过滤,它继承自DefaultRowSorter,使用方法:

sorter.setRowFilter(RowFilter.regexFilter(".*foo.*")); 可以用正则表达式来过滤。

这时候,我们看到的行数变了,即table.getRowCount()变了,但底层的tablemodel行数是不会变的!

接下来,讲另一个问题,排序或者过滤后,选择的那一行的数据和原来的可能有变化,这时,如果还是用table.getSelectedRow()得到的行索引,和实际该数据在tablemodel中的索引不一样,举个例子: (内容乱写的...)

开始时的样子,未做任何处理,即model和table统一。

按单价排序,顺序发生变化,看左边的显示。

按关键字进行过滤,再按单位排序,看左边的显示。

结论:只要model不变,里面的数据无论怎么排序、筛选,顺序都是不变的。我们看到的只是table的变化。

ps:将table中选中行的索引转换成model中的索引方法:

int row_real = table.convertRowIndexToModel(table.getSelectedRow()); 类似地,要在基于底层模型model坐标的 JTable 中选择一行或多行,执行以下相反的操作:

table.setRowSelectionInterval(table.convertRowIndexToView(3),table.convertRowIndexToView(4));这里3和4即为底层model的索引,无论外界条件怎么变,该方法都会找到3和4对应与视图jtable中的索引。 列同理。另:自己编写DefaultTableModel时也要注意,重写方法里面的行列都是指model的行列,例如要使 table某一cell可编辑(原来都是不可编辑),则输入的i,j应为model的,例如现在要渲染选中行的"单价"单元格, 自定义DefaultTableModel: class My_tablemodel extends DefaultTableModel { private static final long serialVersionUID = 1L; private int i=-1,j=-1; //--设置不可编辑------- My_tablemodel(Object[][] obj,String[] str) { super(obj,str); } public boolean isCellEditable(int row,int column) { if(row==i&&column==j)return true; return false; } //--定义按类的比较规则排序--------- public Class<?> getColumnClass(int column) { Class<?> returnValue = Object.class; if ((column >= 0) && (column < getColumnCount()&& getRowCount()>0)) { for(int i=0;i<getRowCount();i++) if(getValueAt(0, column)!=null) returnValue = getValueAt(0, column).getClass(); } return returnValue; } public void setij(int i,int j) { this.i = i; this.j = j; } }使用: My_tablemodel.setij(row,column);table显示时会自动转换! 来自DefaulTableModel源代码的警告: Warning: DefaultTableModel returns a column class of Object. When DefaultTableModel is used with a TableRowSorter,this will result in extensive use of toString, which for non-String data types is expensive. If you use DefaultTableModel with a TableRowSorter you are strongly encouraged to override getColumnClass to return the appropriate type.这告诉我们,如果不重写getColumnClass方法,java默认使用Object的toString方法比较元素大小,除了 AbstractTableModel类: public Class<?> getColumnClass(int columnIndex) { return Object.class; }DefaultTableModel类并没有再次覆盖这个方法。 渲染: JTable有个方法叫setDefaultRenderer(),下面是该方法的定义: public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) { if (renderer != null) { defaultRenderersByColumnClass.put(columnClass, renderer); } else { defaultRenderersByColumnClass.remove(columnClass); } }其中columnClass是选择要将哪些列进行渲染,列默认的类为Object.class,当然也可以自己在tablemodel中重载getColumnClass方法来进行定义。renderer是此 columnClass 要使用的默认单元格渲染器,可自己定义。 从源代码中可以看出,他使用了一个HashTable来存<类,渲染器>,如果将renderer设为null,则去掉相应该类的渲染器。 (此处类是指属于该类的列) 下面是自己定义的一个TableCellRenderer,通过传入两个参数i,j来使那个单元格变色: class My_TableCellRender implements TableCellRenderer { private int i=-1,j=-1; My_TableCellRender(int i,int j) { super(); this.i = i; this.j = j; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component renderer = new DefaultTableCellRenderer().getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); Color foreground, background; if(row==i&&column==j) { foreground = Color.RED; background = Color.green; renderer.setForeground(foreground); renderer.setBackground(background); } return renderer; } }使用方法:table.setDefaultRenderer(Object.class, new My_TableCellRender(row,column));时遇到了一个问题,只有属于String那几列的被修饰了,Integer和Float的没有。而换成:table.setDefaultRenderer(Integer.class, new My_TableCellRender(row,column));或者:table.setDefaultRenderer(Float.class, new My_TableCellRender(row,column));都只是渲染了该类的列,按道理说Object.class不是应该全部渲染的吗??? 这是重写的DefaultTableModel:public Class<?> getColumnClass(int column) { Class<?> returnValue = Object.class; if ((column >= 0) && (column < getColumnCount()&& getRowCount()>0)) { for(int i=0;i<getRowCount();i++) if(getValueAt(i, column)!=null) { returnValue = getValueAt(i, column).getClass(); break; } } return returnValue; 如果将if语句注释掉,即全部返回Object.class,则全部列可渲染,但无法排序了...一个神奇的问题,有空再研究研究...

转载请注明原文地址: https://www.6miu.com/read-1150317.html

最新回复(0)