Friday, November 14, 2014

Working with tables in Java

Good evening, fellow readers!

Today, I was working with some tables for my application, which reminded me how confusing they were when I started learning the language. Guides help, and so does the documentation at Oracle, but I would like to point out and explain some important aspects that were not clear for me personally.

My specific area of focus will be the implementation of tables in Swing. The first important thing to understand is that tables in Java were designed using the MVC (model-view-controller) pattern. The JTable class is responsible for the "view" part, and the TableModel class is responsible for the "model". "Control" is handled by various Listeners, but I won't be touching that now.

The only job of the JTable class is to display the table on the screen. The data to display, along with details of how it is obtained, sorted, and displayed, are handled by a TableModel associated with the JTable. It is possible to create simple tables without having to worry about a TableModel (or knowing that such a thing exists). When you create a new JTable, unless you specify a TableModel, an associated DefaultTableModel is automatically created.

If you have to display anything more than the most basic set of data, it is best to create your own model. This can be done by extending one of the two classes that implement the TableModel interface - AbstractTableModel or DefaultTableModel. The AbstractTableModel implements the majority of the methods defined in the TableModel interface and leaves three methods (getRowCount, getColumnCount, getValueAt) for the user to implement. It's the basis for any custom table model a programmer might create. The DefaultTableModel class is such an implementation. It is a subclass of the AbstractTableModel with the three methods implemented where the data is stored in a Vector of Vectors.

The decision between the two mostly comes down to the data. If you are OK with storing your data within the outdated Vector cl, using the DefaultTableModel is a lot less work. If you decide to build from the AbstractTableModel, you can choose to store the data however you want to but you'll need to implement the methods that allow the data to interact with the JTable seamlessly.

At the very least, you will have to implement the getRowCount, getColumnCount and getValueAt methods. As you may have guessed from the names, the getRowCount method should describe how to find out the number of rows in the table, the getColumnCount does the same for the number of columns, and the getValueAt is responsible for describing how the data is distributed among the cells. Here is a simple example of a custom AbstractTableModel:

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class EmployeeTable extends CustomTable {

private List dataList = new ArrayList();

        //declare the names of the column headers
private String columnNames[] = {"Name", "Phone"}; 



//in the constructor, retrieve an ArrayList of the data to be displayed
public EmployeeTable (){
super();
DatabaseReader readData = new DatabaseReader();
dataList = readData.read();
}

// the number of columns will be equal to the number of column headers we declared
@Override
public int getColumnCount(){
return columnNames.length;
}
  
        /*
         *to count the number of rows, we count the number of entries in the ArrayList.
         *The will be a row for each entry.
         */
@Override
public int getRowCount() {
return dataList.size();
}

        /*
         *This part is a bit tricky. When drawing the table, your JTable class will go through
         *each cell one by one. For each cell, this method will be called. We provide
         *instructions on how to get the data for the cell that is currently being drawn. First,
         *get the data from the current row, then get the data specific to the current column.
         */
@Override
public Object getValueAt(int row, int column) {
myData d =  dataList.get(row);
switch (column) {
            case 0:
                return d.getName();
            case 1:
                return d.getPhone();
        }
return new String();
}
}



No comments:

Post a Comment