JTable 的排序+过滤+渲染

本文介绍了JTable在Java Swing中的排序、过滤功能及其在实际操作中的应用。通过设置TableRowSorter的RowFilter实现表格数据的过滤,点击表头可触发内置排序。尽管表格显示的内容会因排序和过滤而变化,但底层TableModel的数据顺序保持不变。当需要获取选中数据在Model中的索引时,需注意转换方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,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,则全部列可渲染,但无法排序了...
一个神奇的问题,有空再研究研究...


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值