【问题标题】:Java JTable: Setting a primary column to always sort by firstJava JTable:将主列设置为始终首先排序
【发布时间】:2016-06-09 17:20:21
【问题描述】:

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

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

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

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

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

你的

安德烈亚斯

【问题讨论】:

  • 您在这里有两个问题:a) 始终将特定值保持在另一个之上,独立于排序顺序 - 如果不对 rowSorter 的内部进行深入更改,这是不可能的。 b)使用其他列的知识对各个列进行排序:这可以通过具有行对象的自定义模型,比较该行对象的复合比较器和查看每列的行对象的自定义排序器来完成(看看 sun.以 awt.shell.FilePane 为例)

标签: java swing sorting jtable


【解决方案1】:

尝试使用下一个对您有帮助的代码:

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 实现进行比较。试试看!

Comparator

RowSorter tutorial

【讨论】:

  • 感谢您的建议,但我相信这只会按字符串对第 1 列进行排序。单击第 3 列后,它将按时间戳排序;第 1 列中的文件和文件夹可以混合使用。该表仍应主要按第 1 列排序,其次按第 3 列排序。单击第 2 列的方式相同(仍按 1 排序,而不是按 2 排序)。
  • 我向您展示了如何将自定义Comparator 设置为RowSorter,将您的逻辑写入其中,您将得到结果。在compare 方法中获取对象的行并首先按文件或文件夹对其进行排序,然后按第二个顺序排列第二列,然后……为此编写您自己的比较器。
【解决方案2】:

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

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

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

另一种方法是让 Comparator 了解列排序关系。一种简单的方法是将有关排序优先级的信息添加到 Comparator.compare 的对象中。简单的优先级可以用前缀字符串表示(例如:第 1 列的前缀“a”和所有其他列的前缀“b”)。

PrefixedData 可以包含字符串、布尔值、时间戳或空值和前缀:

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:

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

};

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

【讨论】:

    【解决方案3】:

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

    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 实现不提供对列表的访问。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-31
      • 2022-01-10
      • 2011-02-26
      • 2023-04-02
      • 2012-02-19
      • 1970-01-01
      • 2023-03-05
      相关资源
      最近更新 更多