JTable:如何对特定行始终为第一行的 table 进行排序?

JTable: How to sort a table where a specific row is always the first row?

如何创建排序器,使特定行始终排在第一行,无论该列是按升序还是降序排序?我的代码如下。单击 "name" 2 次 "FIRSTROW" 每次都排在第一行。单击 "age" 直到 FIRSTROW 不再排在第一位,然后单击返回 "name",FIRSTROW 不再排在第一位。我希望每次单击 "name" 时首先对 FIRSTROW 进行排序,我该怎么做?

示例代码如下:

import java.awt.BorderLayout;
import java.util.Comparator;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;



public class TableTest implements Comparator<String> {
        @Override
        public int compare(String obj1, String obj2) {
            if (obj1 == obj2) {
                return 0;
            }
            if (obj1 == null) {
                return -1;
            }
            if (obj2 == null) {
                return -1;
            }
            if (obj2 instanceof String && obj2.trim( ).equals("FIRSTROW")) {
                return 0;
            }
            if (obj1 instanceof String && obj1.trim( ).equals("FIRSTROW")) {
                return 0;
            }
            return obj1.compareTo(obj2);
        }


  public static void main(String args[]) {
        JFrame frame = new JFrame("Fixed First Row");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        String rows[][] = { { "FIRSTROW", "23" }, { "R", "24", }, { "E", "21", }, { "D", "27", }, { "A", "25", },
            { "S", "22", }, };
        /* Specify column names */
        String columns[] = { "Name", "Age" };
        /* Create a TableModel */
        DefaultTableModel model = new DefaultTableModel(rows, columns) {
          @Override
        public Class getColumnClass(int column) {
            Class returnValue;
            if ((column >= 0) && (column < getColumnCount())) {
              returnValue = getValueAt(0, column).getClass();
            } else {
              returnValue = Object.class;
            }
            return returnValue;
          }
        };

        JTable table = new JTable(model);

        TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);

        table.setRowSorter(sorter);

        sorter.setComparator( 0, new TableTest() ); // Field column uses the random string comparator.

        JScrollPane pane = new JScrollPane(table);

        frame.add(pane, BorderLayout.CENTER);

        frame.setSize(300, 150);
        frame.setVisible(true);
      }
}

如果您只是进行简单的文本比较,您会在比较器中进行 return stringA.compareTo(stringB);。要确保某个值始终排在最前面,您需要明确地使其如此。

不过要小心,强制比较远离 "natural ordering" 很容易导致比较 "contract violations"。

编辑:

public int compare(String obj1, String obj2) {
  if (obj1 == obj2) {// if they are the same object
    return 0;
  }
  if (obj1 == null) {// sort null above not null
    return -1;
  }
  if (obj2 == null) {// sort not null below null
    return 1;
  }
  if (obj1.trim().toUpperCase().equals("B") &&
    !obj2.trim().toUpperCase().equals("B")) {// sort B above not-B
    return -1;
  }
  if (!obj1.trim().toUpperCase().equals("B") &&
    obj2.trim().toUpperCase().equals("B")) {// sort not-B below B
    return 1;
  }
  return obj1.compareTo(obj2);
}

当你想在选择后将一行保留在顶部时,你可能不会只使用它的一个属性来做到这一点,因为它可能与另一个相同,最好的方法是对象本身。

为此:

  1. 获取选中的行
  2. 根据需要使用其中一个对象属性对列表进行排序//
  3. 从列表中删除所选行
  4. 将所选行添加到列表顶部。

以下是此行为的测试示例:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import com.sun.prism.impl.Disposer.Record;

import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;

import javax.swing.JTextField;
import javax.swing.JComboBox;
import javax.swing.DefaultComboBoxModel;

import traitement.CustomRenderer;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class TestSort extends JFrame implements ActionListener{
    private JTable table1;
    private JButton btnSort;
    private int selectedCombCriteria=-1;
    private JComboBox combCriteria;
    private Boolean showSelection=false;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
      TestSort ts=new TestSort();
      ts.setVisible(true);
    }
    TestSort()
    {
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
        this.setLocation(dim.width/2-this.getSize().width/2, dim.height/2-this.getSize().height/2);
        setSize(new Dimension(439, 325));
        setPreferredSize(new Dimension(600,400));
        JPanel pt=new JPanel();
        getContentPane().add(pt);
        pt.setLayout(null);

        combCriteria = new JComboBox();
        combCriteria.setModel(new DefaultComboBoxModel(new String[] {"Age", "Score"}));
        combCriteria.setSelectedIndex(0);
        combCriteria.setBounds(115, 205, 67, 20);
        combCriteria.addActionListener(this);
        pt.add(combCriteria);
        btnSort = new JButton("Sort");
        btnSort.addActionListener(this);
        btnSort.setBounds(324, 204, 89, 23);
        pt.add(btnSort);
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(20, 41, 393, 127);
        pt.add(scrollPane);
        table1 = new JTable();
        SortModel sm=new SortModel();
        table1.setModel(sm);
        table1.setBounds(50, 26, 329, 130);
        scrollPane.setViewportView(table1);
        table1.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
                    public void valueChanged(ListSelectionEvent event) {
                        SortModel.selectedRecord=((SortModel)table1.getModel()).getRowAt(table1.getSelectedRow());
                    showSelection=true;
                    }
                });
        table1.setDefaultRenderer(Object.class, new DefaultTableCellRenderer()
        {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
            {
                final Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                c.setBackground(row==0?Color.YELLOW:isSelected&&showSelection?Color.lightGray:Color.white);
                return c;
            }
        });

        JLabel lblSelectARow = new JLabel("Select a row to keep at the top and sort using one of the sorting criteria");
        lblSelectARow.setFont(new Font("Traditional Arabic", Font.PLAIN, 14));
        lblSelectARow.setHorizontalAlignment(SwingConstants.CENTER);
        lblSelectARow.setBounds(20, 11, 393, 23);
        pt.add(lblSelectARow);

        JLabel lblSortCreteria = new JLabel("Sorting criteria:");
        lblSortCreteria.setBounds(20, 208, 96, 14);
        pt.add(lblSortCreteria);
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==btnSort)
            if(SortModel.selectedRecord!=null)
            {
                ((SortModel)table1.getModel()).sort(selectedCombCriteria);
                table1.repaint();
                showSelection=false;

            }
            if(e.getSource()==combCriteria)
            {
                selectedCombCriteria=combCriteria.getSelectedIndex();

            }
    }
}
 class SortModel extends AbstractTableModel 
{
      public  SortModel(){
            data=fillOnce();
        }
    private String[] columns = {"First Name", "Last Name", "Profession", "Age","Score","Comment"};
    public  List<MyDataRecord> data=new ArrayList<MyDataRecord>();
    public static MyDataRecord selectedRecord;

    @Override
    public int getRowCount() {
        // TODO Auto-generated method stub
        return data.size();
    }
    @Override
    public String getColumnName(int column) {
        return columns[column];
    }
    @Override
    public int getColumnCount() {
        return columns.length;
    }
    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
        case 0:
            return data.get(rowIndex).getfName();
        case 1:
            return data.get(rowIndex).getlName();
        case 2:
            return data.get(rowIndex).getProfession();
        case 3:
            return data.get(rowIndex).getAge();
        case 4:
            return data.get(rowIndex).getScore();
        case 5:
            return data.get(rowIndex).getComment();
        default:
            throw new IllegalArgumentException();
        }
    }

    public MyDataRecord getRowAt(int rowIndex)
    {
        return data.get(rowIndex);
    }
    //sorting method
    public void sort(int sort)
    {
        //sort 
        Collections.sort(data, new Comparator<MyDataRecord>() {
            public int compare(MyDataRecord object1, MyDataRecord object2) {
                if(sort!=1)
                return Integer.compare(object1.getAge(), object2.getAge());
                else
                return Integer.compare(object1.getScore(), object2.getScore());
            }
        });
        //then remove he selected element
        data.remove(selectedRecord);
        //put the selected element on the top of the list
        if(selectedRecord!=null)
        {
        data.add(0, selectedRecord);
        }
    }

    public List<MyDataRecord> fillOnce()
    {
        List<MyDataRecord> tempList=new ArrayList<MyDataRecord>();
        Random r = new Random();
        for(int i=1;i<=6;i++)
        {
            MyDataRecord mdr=new MyDataRecord("FNtest"+i, "LNtest"+i, "PROFtest"+i, "COMtest"+i, r.nextInt(100-1) + 1, r.nextInt(100-1) + 1);
            tempList.add(mdr);
        }
        return tempList;
    }
}

 class MyDataRecord{
     private String fName,lName,profession,comment;
     private int age,score;

    public MyDataRecord(String fName, String lName, String profession,
            String comment, int age, int score) {
        super();
        this.fName = fName;
        this.lName = lName;
        this.profession = profession;
        this.comment = comment;
        this.age = age;
        this.score = score;
    }

    public String getfName() {
        return fName;
    }

    public void setfName(String fName) {
        this.fName = fName;
    }

    public String getlName() {
        return lName;
    }

    public void setlName(String lName) {
        this.lName = lName;
    }

    public String getProfession() {
        return profession;
    }

    public void setProfession(String profession) {
        this.profession = profession;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }
 }

已经制作了一个 CustomRowSorter,基本上是默认 DefaultRowSorter 的副本。 我无法扩展 class,因为我必须对排序方面的私有函数进行一些更改。

此外,我还必须制作自己的 TableRowSorter 副本,它使用了新的 CustomRowSorter。然后,我的 table 将使用 CustomTableRowSorter 来完成工作。这不是一个很好的实现,因为复制这两个文件是 1.5k 行的更改。