简体   繁体   English

Java JTable:将主列设置为始终按第一排序

[英]Java JTable: Setting a primary column to always sort by first

I'm using a JTable with say 3 columns, displaying information about files and folders: 我正在使用带有3列的JTable,显示有关文件和文件夹的信息:

Col1: Either "file" or "folder" (String) Col1:“文件”或“文件夹”(字符串)
Col2: Name of the file or folder (String) Col2:文件或文件夹的名称(字符串)
Col3: Creation date (Timstamp) 第3栏:创建日期(即时贴)

I can easily sort the table by any column. 我可以轻松按任何列对表格进行排序。 But I'm not able to achieve the following desired behavior: 但是我无法实现以下期望的行为:

No matter what column header the user clicks, folders should always display before files. 无论用户单击什么列标题,文件夹都应始终显示在文件之前。 So basically sorting by Col1 in any order other than descending should not be possible. 因此,基本上不可能按Col1以降序以外的任何其他顺序进行排序。 Also, if sorted by Col3, any folders should still display before files, regardless of their timestamp. 另外,如果按Col3排序,则无论其时间戳如何,任何文件夹都应仍显示在文件之前。

Any hint to how to do this is greatly appreciated. 任何有关如何执行此操作的提示将不胜感激。

Yours 你的

Andreas 安德烈亚斯

Try to use next code it helps you: 尝试使用下一个代码,它可以帮助您:

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

}

Here I set custom Comparator to TableRowSorter for column 0. You can compare your rows in different ways with your own Comparator implementation. 在这里,我将第0列的自定义Comparator设置为TableRowSorter 。您可以使用自己的Comparator实现以不同的方式比较行。 Try it! 试试吧!

Comparator 比较器

RowSorter tutorial RowSorter教程

Maybe this is a way to do it. 也许这是一种方法。 There's probably a more elegant way to do it around. 可能有更优雅的方法可以解决此问题。

The DefaultRowSorter Comparator only compares values for one column at a time, without additional information it alone can not make sure that one column in the set of columns (say column 1) must always be the first column to sort by. DefaultRowSorter比较器一次仅比较一列的值,而没有其他信息,仅凭它不能确保一组列(例如第1列)中的某一列必须始终是排序的第一列。

If you mess with DefaultRowSorter.toggleSortOrder and add a new SortKey to keys at position 0 before setSortKeys gets called (this would be the primary column you always want to sort by first [column 1]), the ascending and descending arrows displayed in the column headers constantly mark column 1 (rightfully so), but this is not what the user expects if she clicks on column 2. 如果您对DefaultRowSorter.toggleSortOrder感到困惑,并在调用setSortKeys之前将新的SortKey添加到位置0的键上(这将是您始终要按第一个[列1]进行排序的主要列),则该列中将显示升序和降序箭头标头不断标记第1列(的确如此),但这并不是用户单击第2列所期望的。

Another approach is to have Comparator know about column sort relationships. 另一种方法是让Comparator知道列排序关系。 An easy way is to add information about sort precedences to the Objects for Comparator.compare. 一种简单的方法是将有关排序优先级的信息添加到Comparator.compare的对象中。 Simple precedences can be represented by a prefix String (for example: prefix "a" for column 1 and prefix "b" for all other columns). 简单的优先级可以用前缀String表示(例如:列1的前缀“ a”和所有其他列的前缀“ b”)。

PrefixedData can contain String, Boolean, Timestamp or null values and a prefix: 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;
}
}

JTable cells can be rendered like this for String, Boolean and Timestamp, if your table model returns PrefixedData for DefaultTableModel.getValueAt: 如果您的表模型为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;
    }
}

And finally the Comparator can decide the order of things: 最后,比较器可以决定事物的顺序:

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

};

Any suggestions to this or different approaches a very welcome. 任何对这种或不同方法的建议都非常欢迎。

Well, the question is old but Google returned this page to me when I had a similar task. 好吧,这个问题很旧,但是当我执行类似的任务时,Google将该页面退给了我。 So, I'll post here my solution. 因此,我将在这里发布我的解决方案。 Hope that it would help someone. 希望对别人有帮助。

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

Usage: 用法:

table.setRowSorter( new PredefinedRowSorter( table )
  .withPreColumns( new SortKey( 0, SortOrder.ASCENDING ),
                   new SortKey( 1, SortOrder.ASCENDING ) ) );

This would set a preceding sorting on first two columns. 这将在前两列上设置前面的排序。

Post-sorting is available. 可以进行后排序。

An end-user may sort these two columns too, thus switching the order of sorting (ascending/descending). 最终用户也可以对这两列进行排序,从而切换排序顺序(升/降)。

Also, this class is available as a part of JBroTable library ( source code ). 此外,此类可作为JBroTable库的一部分( 源代码 )使用。

The principle of action is so: predefined columns are added to a sorting columns list before sorting and removed from this list after sorting. 作用原理是这样的:将预定义的列在排序之前添加到排序列列表中,并在排序后从此列表中删除。 Adding and removal is performed via reflection because DefaultRowSorter implementation doesn't provide an access to the list. 因为DefaultRowSorter实现不提供对列表的访问,所以添加和删除是通过反射执行的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM