deutsche Version

Swing


How to sort a JTable?

The problem can split in more then one task:

  1. How to sort the JTable data?
  2. How to change the JTable to react on header mouse click?
  3. How to merge the piecses?

How to sort the JTable data?

The answer is easy. The data from JTable are most in a Collection. The java.util.Collections class has an class method sort for us. We need only to implements the java.util.Comparable interface and can use the sort method.
Step by step:

A simple table model for our sample...

package de.bastie.sample;

import java.util.ArrayList;

import javax.swing.table.AbstractTableModel;


/**
 * This is the simple table model
 * @author Bastie - Sebastian Ritter
 */
public class SimpleTableModel extends AbstractTableModel {

  /** Our business objects [bo] for display in the JTable*/
  protected ArrayList<Person> bo = new ArrayList<Person> ();

  public SimpleTableModel () {
    this.loadData ();
  }

  /**
   * We fill the collection with some bo.
   */
  protected void loadData () {
    fachObjekte.add (new Person ("Ritter", "Sebastian", "05.09.1975", "married"));
    fachObjekte.add (new Person ("Ritter", "Sebastián", "unknown", "single"));
    fachObjekte.add (new Person ("Knight", "Sebastian", "01.01.2222", "single"));
    fachObjekte.add (new Person ("Knight", "Sebastián", "05.09.1975", "single"));
  }

  private String [] headerNames = new String [] {"last name","first name","birthday","family status"};
  public int getCountOfHeaderNames () {
    return this.headerNames.length;
  }

  public String getColumnName (final int spalte) {
    if (spalte < this.getCountOfHeaderNames()) {
      return headerNames [spalte];
    }
    else {
      return super.getColumnName(spalte);
    }
  }

  public boolean isCellEditable(int rowIndex, int columnIndex) {
    return false;
  }

  public int getRowCount() {
    return this.bo.size();
  }

  public int getColumnCount() {
    return 4;
  }


  public Object getValueAt(final int row, final int column) {
    switch (column) {
    case 0 :
      return this.headerNames.get(row).getLastName ();
    case 1 :
      return this.headerNames.get(row).getFirstName();
    case 2 :
      return this.headerNames.get(row).getFormattedBirthday();
    case 3 :
      return this.headerNames.get(row).getFamilyStatus();
    default:
      return null;
    }
  }

}

Also we need the buisiness classes - here the class Person:

package de.bastie.sample;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Logger;

/**
 * Sample BO
 * @author Bastie - Sebastian Ritter
 */
public class Person {
  private SimpleDateFormat sdf = new SimpleDateFormat ("dd.MM.yyyy");

  public Person (){}
  public Person (final String lastName, final String firsName,final String birthdayFormat_dd_mm_yyyy,final String familyStatus) {
    this.setLastName(name);
    this.setFirstNameame(vorname);
    this.setBirthday(gebDatumFormat_dd_mm_yyy);
    this.setFamilienstand(familyStatus);
  }

  public String getFamilyStatus() {
    return familyStatus;
  }
  public void setFamilyStatus(String familyStatus) {
    this.familyStatus = familyStatus;
  }

  public Date getBirthday () {
    return this.birthday;
  }

  public String getFormattedBirthday() {
    return sdf.format(this.birthday);
  }
  public void setBirthday (final String dd_mm_yyyy) {
    try {
      this.birthday = sdf.parse(dd_mm_yyyy);
    }
    catch (final ParseException e) {
      Logger.global.throwing(this.getClass().getName() ,"setBirthday", e);
      this.birthday = new Date (0l);
    }
  }
  public String getLastName() {
    return name;
  }
  public void setLastName(String name) {
    this.lastName = name;
  }
  public String getFirstName() {
    return this.firstName;
  }
  public void setFirstName(String name) {
    this.firstName = name;
  }
  private String lastName;
  private String firstName;
  private String familayStatus;
  private Date birthday = new Date (0l);
}

A sortable model

We need to make our model sortable and for this create we the method sort. We need also an PersonComparator because the attributes of Person are the sort attributes. In this sample we implements the comparator as inner class.

package de.bastie.sample;

import java.util.Collections;
import java.util.Comparator;

/**
 * A sortable table model for Person
 * @author Bastie - Sebastian Ritter
 */
public class SortableTableModel extends SimpleTableModel {

  public void sort (final int column) {
    Collections.sort(this.bo, new PersonComparator (column));
  }

  private class PersonComparator implements Comparator {

    private final int column;

    public PersonComparator (final int column) {
      this.column = column;
    }

    public int compare(Object o1, Object o2) {
      if (o1 == null && o2 == null) {
        return 0;
      }
      else if (o1 == null) {
        return 1;
      }
      else if (o1 instanceof Person && o2 instanceof Person) {
        switch (this.column) {
        case 0 :
          return ((Person)o1).getLastName().compareTo(((Person)o2).getKastName());
        case 1 :
          return ((Person)o1).getFirstName().compareTo(((Person)o2).getFirstName());
        case 2 :
          return ((Person)o1).getBirthday().compareTo(((Person)o2).getBirthday());
        case 3 :
          return ((Person)o1).getFamilyStatus().compareTo(((Person)o2).getFamilyStatus());
        default :
          return 0;
        }
      }
      else {
        return 1;
      }
    }

  }

}

Thats all - we can sort our JTable; first task is released. But how can we perform our sort method?
One alternative are use an ActionListener like this.

package de.bastie.sample;

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

import javax.swing.JTable;

/**
 * Sort a JTable with sortable table model
 * @author Bastie - Sebastian Ritter
*/
public class SortTableActionListener implements ActionListener {

  private final JTable table;

  public SortTableActionListener(final JTable table) {
    this.table = table;
  }

  public void actionPerformed(ActionEvent e) {
    if (table.getModel() instanceof SortableTableModel) {
      ((SortableTableModel)tabelle.getModel()).sort(Integer.parseInt(e.getActionCommand()));
    }
  }

}

How to change the JTable to react on header mouse click?

Thats not so easy because the JTable (Java 1.5) has no method to add an listener for us. The folle four classes can general fix our problem. We extends the classes from Sun allowing add an ActionListener to table header. You need only to register an ActionListener and implements a Comparator for your sorting method to use this classes. The BstJTable extends a JTable and is fully compatible.

We extends the JTable to register our table header and column model.

package de.bastie.swing.tabelle;

import java.util.logging.Logger;

import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;

/**
 * Extends the JTable .
 * @author Bastie - Sebastian Ritter
 */
public class BstJTable extends JTable {

  public BstJTable () {
    this (null, null);
  }

  public BstJTable (final TableModel model) {
    this (model, null);
  }

  /**
   * Do not use this Constructor because the TableColumnModel
   * is replacing.
   * @param model model that represents the business
   * @param columnModel is ignored
   * @param selectionModel how can users selected
   * @deprecated TableColumnModel is ignored. Better you use one of the simplifier constructors.
   */
  public BstJTable (final TableModel model,
                    final TableColumnModel columnModel,
                    final ListSelectionModel selectionModel) {
    this (model, selectionModel);
    Logger.getLogger(this.getClass().getName()).warning("TableColumnModel ignored - used constructor ist deprecated");
  }

  /**
   * Construct a new JTable with a selectable table header.
   * 
All other constructors calls him. * @param model model that represents the business * @param selectionModel how can users selected */ public BstJTable (final TableModel model, final ListSelectionModel selectionModel) { super (model, null, selectionModel); // Own ColumnModel this.setColumnModel(new BstTableColumnModel()); // Own TableHeader this.setTableHeader(new BstJTableHeader()); this.getTableHeader().setColumnModel(this.getColumnModel()); } public BstJTableHeader getTableHeader () { return (BstJTableHeader) this.tableHeader; } }

We only register a new render in our table column model. This is an addon in our sample.

package de.bastie.swing.tabelle;

import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;

/**
 * TableColumnModel to react form selection a table header.
 * @author Bastie - Sebastian Ritter
 */
public class BstTableColumnModel extends DefaultTableColumnModel {

  public void addColumn (final TableColumn aColumn) {
    super.addColumn(aColumn);
    this.addMyHeaderRenderer (aColumn);
  }

  /**
   * @param column
   */
  private void addMyHeaderRenderer(final TableColumn column) {
    column.setHeaderRenderer(new BstSelectionTableCellRenderer ());
  }
}

The renderer himself creates an JLable for the column and format this.

package de.bastie.swing.tabelle;

import java.awt.Component;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.TableCellRenderer;

/**
 * TableCellRenderer for select a column header.
 * @author Bastie - Sebastian Ritter
 * @version last tested with Java 1.5
 */
public class BstSelectionTableCellRenderer implements TableCellRenderer {

  private int pressedColumn = -1;

  public Component getTableCellRendererComponent (final JTable table,
                                                  final Object value,
                                                  final boolean isSelected,
                                                  final boolean hasFocus,
                                                  final int row,
                                                  final int column) {
    JLabel b = new JLabel ((value == null) ? "" : value.toString());
    // render the component
    if (column == this.pressedColumn) {
      b.setBackground(UIManager.getColor("control"));
      b.setBorder(BorderFactory.createEtchedBorder(UIManager.getColor("controlHighlight"),UIManager.getColor("controlShadow")));
    }
    else {
      b.setBackground(UIManager.getColor("controlShadow"));
      b.setBorder(BorderFactory.createEtchedBorder(UIManager.getColor("controlLtHighlight"),UIManager.getColor("controlDkShadow")));
    }
    return b;
  }

  public void setPressedColumn (final int col) {
    this.pressedColumn = col;
  }

}

Now we take a look at class BstJTableHeader with the follow tasks...

package de.bastie.swing.tabelle;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;

/**
 * TableHeader for action on selection.
 * @author Bastie - Sebastian Ritter
 */
public class BstJTableHeader extends JTableHeader implements MouseListener {

  public BstJTableHeader () {
    this (null);
  }
  public BstJTableHeader (final TableColumnModel columnModel) {
    super (columnModel);
    this.addMouseListener(this);
  }

  private ArrayList listener = new ArrayList ();

  public void addActionListener (final ActionListener listener) {
    this.listener.add(listener);
  }

  public void removeActionListener (final ActionListener listener) {
    this.listener.remove(listener);
  }

  /**
   * Fire a ActionEvent with the table model column as
   * command.
   * @param column
   * @param when
   * @param modifiers
   */
  protected void fireActionEvent (final int column,
                                  final long when,
                                  final int modifiers) {
    final ActionEvent e = new ActionEvent (this,
                                     ActionEvent.ACTION_PERFORMED,
                                     ""+this.getTable()
                                            .getColumnModel()
                                            .getColumn(column)
                                            .getModelIndex(),
                                     when,
                                     modifiers);
    this.fireActionEvent(e);
  }
  protected void fireActionEvent (final ActionEvent e) {
    for (int i = 0; i < listener.size(); i++) {
      listener.get(i).actionPerformed(e);
    }
  }

  /**
   * Method calls fireActionEvent with this table column of view.
   * @param e MouseEvent
   */
  public void mouseClicked (final MouseEvent e) {
    this.fireActionEvent(this.columnAtPoint(e.getPoint()),e.getWhen(),e.getModifiers());
  }

  public void mousePressed(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getSource();
    int column = header.columnAtPoint(e.getPoint());
    TableCellRenderer cr = header.getTable()
                                 .getColumnModel()
                                 .getColumn(column)
                                 .getHeaderRenderer();
    if (cr instanceof BstSelectionTableCellRenderer) {
      ((BstSelectionTableCellRenderer) cr).setPressedColumn(column);
      header.repaint();
    }
  }

  public void mouseReleased(MouseEvent e) {
    JTableHeader header = (JTableHeader) e.getSource();
    int column = header.columnAtPoint(e.getPoint());
    TableCellRenderer cr = header.getTable().getColumnModel().getColumn(column).getHeaderRenderer();
    if (cr instanceof BstSelectionTableCellRenderer) {
      ((BstSelectionTableCellRenderer) cr).setPressedColumn(-1);
      header.repaint();
    }
  }

  public void mouseEntered(MouseEvent e) {}

  public void mouseExited(MouseEvent e) {}

}

To test the classes we need an small application / applet - at the bottom of this site.

package de.bastie.sample;

import java.awt.BorderLayout;

import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;

import de.bastie.swing.tabelle.BstJTable;

/**
 * A sample how you can react at mouse click at table header
 * @author Bastie - Sebastian Ritter
 */
public class SampleClickableTableHeader extends JApplet {

  private JTable table;

  /**
   * init the application / applet
   */
  public void init () {
    // init the view
    BstJTable table = new BstJTable ();

    tabelle.getTableHeader().addActionListener(new SortTableActionListener (table));

    tabelle.setModel (new SortableTableModel());
    this.getContentPane().add (new JScrollPane (table));
  }

  /**
   * start method for stand alone application
   * @param args
   */
  public static void main (final String [] args) {
    JFrame fenster = new JFrame ("clickable JTable header sample");
    fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    fenster.setSize(400,300);
    SampleClickableTableHeader sample = new SampleClickableTableHeader ();
    sample.init();
    fenster.getContentPane().add(sample, BorderLayout.CENTER);
    fenster.setVisible(true);
    sample.start();
  }
}
Java Archiv with sources and classes (german version).
all rights reserved © Bastie - Sebastian Ritter @: w³: http://www.Bastie.de
Diese Seite ist Bestandteil der Internetpräsenz unter http://www.Bastie.de


Java Cobol Software Resourcen Service Links Über mich