我正在使用带有3列的JTable,显示有关文件和文件夹的信息:
Col1:“文件”或“文件夹”(字符串)
Col2:文件或文件夹的名称(String)
第3栏:创作日期(Timstamp)
我可以轻松地按任何列对表格进行排序。但我无法达到以下预期的行为:
无论用户点击哪个列标题,文件夹都应始终显示在文件之前。所以基本上不应该按照除降序之外的任何顺序按Col1排序。此外,如果按Col3排序,任何文件夹仍应显示在文件之前,无论其时间戳如何。
非常感谢任何提示如何做到这一点。
此致
安德烈亚斯
答案 0 :(得分:0)
尝试使用它可以帮助您的下一个代码:
public class Example extends JFrame {
public Example() {
DefaultTableModel defaultTableModel = new DefaultTableModel(new Object[][]{{"1"},{"3"},{"2"}},new Object[]{"text"});
JTable t = new JTable(defaultTableModel);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(defaultTableModel);
sorter.setComparator(0, new Comparator<Object>() {
@Override
public int compare(Object arg0, Object arg1) {
return arg0.toString().compareTo(arg1.toString());
}
});
t.setRowSorter(sorter);
add(new JScrollPane(t));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String...strings ){
Example e = new Example();
}
}
我在这里为第0列设置自定义Comparator
到TableRowSorter
。您可以使用自己的Comparator
实现以不同方式比较行。试试吧!
答案 1 :(得分:0)
也许这是一种方法。这可能是一种更优雅的方式。
DefaultRowSorter Comparator一次只比较一列的值,没有附加信息,单独无法确保列集中的一列(比如第1列)必须始终是要排序的第一列。
如果你搞乱了DefaultRowSorter.toggleSortOrder并在调用setSortKeys之前向位置0的键添加一个新的SortKey(这将是你总是希望先按[第1列]排序的主列),显示的是升序和降序箭头在列标题中不断标记第1列(正确地如此),但这不是用户在点击第2列时所期望的。
另一种方法是让Comparator了解列排序关系。一种简单的方法是将有关排序优先级的信息添加到Objects for Comparator.compare中。简单优先级可以由前缀String表示(例如:列1的前缀“a”和所有其他列的前缀“b”)。
PrefixedData可以包含String,Boolean,Timestamp或null值以及前缀:
public class PrefixedData {
private String sData=null, prefix="";
private Timestamp tData=null;
private Boolean bData=null;
public void setData(String data) {
sData=data;
}
public void setData(Timestamp data) {
tData=data;
}
public void setData(boolean data) {
bData=data;
}
public void setPrefix(String prefix) {
this.prefix=prefix;
}
public String getPrefix() {
return prefix;
}
public Object getData() {
if(sData!=null) return sData;
if(tData!=null) return tData;
if(bData!=null) return bData;
return null;
}
}
如果您的表模型为DefaultTableModel.getValueAt返回PrefixedData,则可以为String,Boolean和Timestamp呈现JTable单元格:
private static class PrefixedDataRenderer3 extends JLabel implements TableCellRenderer {
private static final SimpleDateFormat formatter = new SimpleDateFormat("MM.yyyy HH:mm:ss");
private static final JCheckBox box = new JCheckBox();
public PrefixedDataRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
remove(box);
PrefixedData p=(PrefixedData) value;
if(p.getData()==null) return this;
if(p.getData().getClass().equals(String.class))
{
setText((String)p.getData());
return this;
}
if(p.getData().getClass().equals(Timestamp.class))
{
setText(formatter.format((Timestamp)p.getData()));
return this;
}
if(p.getData().getClass().equals(Boolean.class))
{
box.setSelected(((Boolean)p.getData()).booleanValue());
add(box);
}
return this;
}
}
最后,比较者可以决定事情的顺序:
Comparator<Object> PrefixedDataComparator = new Comparator<Object>() {
private List<? extends SortKey> sortKeys;
@Override
public int compare(Object o1, Object o2) {
PrefixedData p1=(PrefixedData) o1;
PrefixedData p2=(PrefixedData) o2;
//First: compare prefixes (precedence data)
int prefixResult=p1.getPrefix().compareTo(p2.getPrefix());
sortKeys = tableOU.getRowSorter().getSortKeys();
//The prefixes are not the same, so return the result of the prefix comparision
//The result has to be inverted if we're sorting in descending order
//for the column the user has clicked
if(prefixResult!=0) return (sortKeys.get(0).getSortOrder().equals(SortOrder.ASCENDING) ? prefixResult : -prefixResult );
//Only if the prefixes are the same do we have to compare the payload data
//Try to impose an order for null
if(p1.getData()==null && p2.getData()!=null) return -1;
if(p1.getData()==null && p2.getData()==null) return 0;
if(p1.getData()!=null && p2.getData()==null) return 1;
//Objects compared are identical
if(p1.getData().equals(p2.getData())) return 0;
//Compare String
if(p1.getData().getClass().equals(String.class)) {
String s1=(String) p1.getData();
String s2=(String) p2.getData();
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
//Compare Timestamp
if(p1.getData().getClass().equals(Timestamp.class)) {
Timestamp t1=(Timestamp) p1.getData();
Timestamp t2=(Timestamp) p2.getData();
if(t1.before(t2)) return -1;
if(t1.equals(t2)) return 0;
return 1;
}
//Compare Bool
if(p1.getData().getClass().equals(Boolean.class)) {
boolean b1=((Boolean)p1.getData()).booleanValue();
boolean b2=((Boolean)p2.getData()).booleanValue();
if(b1==false && b2==true) return -1;
if(b1==b2) return 0;
return 1;
}
return 0;
}
};
对此或不同方法的任何建议都非常受欢迎。
答案 2 :(得分:0)
嗯,这个问题已经过时了,但是当我遇到类似的任务时,Google会将此页面返回给我。所以,我会在这里发布我的解决方案。希望它能帮助别人。
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.DefaultRowSorter;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.log4j.Logger;
public class PredefinedRowSorter extends TableRowSorter< TableModel > {
private static final Logger LOGGER = Logger.getLogger( PredefinedRowSorter.class );
private static final SortKey EMPTY_ARRAY[] = new SortKey[ 0 ];
private final JTable table;
private SortKey preColumns[] = EMPTY_ARRAY;
private SortKey postColumns[] = EMPTY_ARRAY;
public PredefinedRowSorter( JTable table ) {
super( table.getModel() );
this.table = table;
}
private void check( SortKey modelColumns[], SortKey crossCheckColumns[], boolean post ) {
TableModel tm = table.getModel();
int max = tm.getColumnCount();
Set< Integer > used = new HashSet< Integer >();
for ( SortKey key : modelColumns ) {
if ( key == null )
throw new IllegalArgumentException( "SortKey must be non-null" );
if ( key.getColumn() < 0 )
throw new IllegalArgumentException( "SortKey column must be non-negative" );
if ( key.getColumn() >= max )
throw new IllegalArgumentException( "SortKey column is too high (out of model scope)" );
if ( key.getSortOrder() == SortOrder.UNSORTED )
throw new IllegalArgumentException( "SortKey must be ordered (ascending or descending)" );
if ( !used.add( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is repeating)" );
}
for ( SortKey key : crossCheckColumns )
if ( used.contains( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is already contained in " + ( post ? "post" : "pre" ) + " columns list)" );
}
public PredefinedRowSorter withPreColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( preColumns, modelColumns ) ) {
check( modelColumns, postColumns, true );
preColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public PredefinedRowSorter withPostColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( postColumns, modelColumns ) ) {
check( modelColumns, preColumns, false );
postColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public JTable getTable() {
return table;
}
public SortKey[] getPreColumns() {
return preColumns.length == 0 ? preColumns : preColumns.clone();
}
public SortKey[] getPostColumns() {
return postColumns.length == 0 ? postColumns : postColumns.clone();
}
private void setSortKeysInternal( List< ? extends SortKey > sortKeys ) {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "sortKeys" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
field.set( this, sortKeys );
if ( !accessible )
field.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
}
private Object getViewToModelInternal() {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "viewToModel" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
Object ret = field.get( this );
if ( !accessible )
field.setAccessible( false );
return ret;
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
return null;
}
private void sortExistingDataInternal() {
try {
Method method = DefaultRowSorter.class.getDeclaredMethod( "sortExistingData" );
boolean accessible = method.isAccessible();
if ( !accessible )
method.setAccessible( true );
method.invoke( this );
if ( !accessible )
method.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchMethodException e ) {
LOGGER.error( null, e );
} catch ( InvocationTargetException e ) {
LOGGER.error( null, e );
LOGGER.error( null, ( ( InvocationTargetException )e ).getCause() );
}
}
@Override
public void setSortKeys( List< ? extends SortKey > sortKeys ) {
List< ? extends SortKey > oldSortKeys = getSortKeys();
List< ? extends SortKey > newSortKeys;
if ( sortKeys != null && !sortKeys.isEmpty() ) {
int max = getModelWrapper().getColumnCount();
for ( SortKey key : sortKeys )
if ( key == null || key.getColumn() < 0 || key.getColumn() >= max )
throw new IllegalArgumentException( "Invalid SortKey" );
newSortKeys = Collections.unmodifiableList( new ArrayList< SortKey >( sortKeys ) );
} else
newSortKeys = Collections.emptyList();
setSortKeysInternal( newSortKeys );
if ( !newSortKeys.equals( oldSortKeys ) ) {
fireSortOrderChanged();
boolean wasChanged = false;
if ( preColumns.length > 0 || postColumns.length > 0 ) {
List< SortKey > editableSortKeys = new ArrayList< SortKey >( newSortKeys );
for ( int i = preColumns.length - 1; i >= 0; i-- ) {
int modelColumn = preColumns[ i ].getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length - i - 1, editableSortKeys.size(), modelColumn );
SortOrder sortOrder = idx < 0 ? preColumns[ i ].getSortOrder() : editableSortKeys.remove( idx ).getSortOrder();
editableSortKeys.add( 0, new SortKey( modelColumn, sortOrder ) );
}
int to = editableSortKeys.size();
for ( SortKey postColumn : postColumns ) {
int modelColumn = postColumn.getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length, to, modelColumn );
SortOrder sortOrder;
if ( idx < 0 )
sortOrder = postColumn.getSortOrder();
else {
sortOrder = editableSortKeys.remove( idx ).getSortOrder();
to--;
}
editableSortKeys.add( new SortKey( modelColumn, sortOrder ) );
}
if ( wasChanged = !editableSortKeys.equals( newSortKeys ) )
setSortKeysInternal( editableSortKeys );
}
if ( getViewToModelInternal() == null )
sort();
else
sortExistingDataInternal();
if ( wasChanged )
setSortKeysInternal( newSortKeys );
}
}
private int indexOfColumn( List< SortKey > sortKeys, int fromIncl, int toExcl, int column ) {
for ( int i = toExcl - 1; i >= fromIncl; i-- )
if ( sortKeys.get( i ).getColumn() == column )
return i;
return -1;
}
};
用法:
table.setRowSorter( new PredefinedRowSorter( table )
.withPreColumns( new SortKey( 0, SortOrder.ASCENDING ),
new SortKey( 1, SortOrder.ASCENDING ) ) );
这将在前两列设置前面的排序。
可以进行后期排序。
最终用户也可以对这两列进行排序,从而切换排序顺序(升序/降序)。
此外,此类作为JBroTable库(source code)的一部分提供。
操作原理是这样的:预定义列在排序之前添加到排序列列表中,并在排序后从此列表中删除。添加和删除是通过反射执行的,因为DefaultRowSorter
实现不提供对列表的访问。