2

我正在使用带有 3 列的 JTable,显示有关文件和文件夹的信息:

Col1:“文件”或“文件夹”(字符串)
Col2:文件或文件夹的名称(字符串)
Col3:创建日期(Timstamp)

我可以轻松地按任何列对表格进行排序。但我无法实现以下期望的行为:

无论用户单击哪个列标题,文件夹都应始终显示在文件之前。因此,基本上不可能以除降序以外的任何顺序按 Col1 进行排序。此外,如果按 Col3 排序,任何文件夹仍应显示在文件之前,无论其时间戳如何。

非常感谢任何有关如何执行此操作的提示。

你的

安德烈亚斯

4

3 回答 3

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 列的自定义设置ComparatorTableRowSorter。您可以以不同的方式将您的行与您自己的Comparator实现进行比较。试试看!

比较器

RowSorter 教程

于 2013-11-06T19:55:40.053 回答
0

也许这是一种方法。可能有一种更优雅的方式来解决它。

DefaultRowSorter 比较器一次只比较一列的值,如果没有其他信息,它本身无法确保列集中的一列(比如第 1 列)必须始终是排序依据的第一列。

如果您弄乱了 DefaultRowSorter.toggleSortOrder 并在调用 setSortKeys 之前将新的 SortKey 添加到位置 0 的键(这将是您始终希望按第一个 [column 1] 排序的主列),则该列中显示的升序和降序箭头标题不断标记第 1 列(这是正确的),但这不是用户单击第 2 列时所期望的。

另一种方法是让 Comparator 知道列排序关系。一种简单的方法是将有关排序优先级的信息添加到 Comparator.compare 的对象中。简单的优先级可以用前缀字符串表示(例如:第 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,则 JTable 单元格可以像这样为 String、Boolean 和 Timestamp 呈现:

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 可以决定事情的顺序:

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

};

非常欢迎对此或不同方法的任何建议。

于 2013-11-07T22:54:59.533 回答
0

好吧,这个问题很老了,但是当我有类似的任务时,谷歌将此页面返回给我。所以,我会在这里发布我的解决方案。希望它会帮助某人。

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 库(源代码)的一部分使用。

作用原理是这样的:在排序前将预定义的列添加到排序列列表中,并在排序后从该列表中删除。添加和删​​除是通过反射执行的,因为DefaultRowSorter实现不提供对列表的访问。

于 2016-06-09T09:12:15.510 回答