javaswingjtablejscrollpaneabstracttablemodel

TableModel not called in a JScrollPane containing a JTable


I am having an unexpected behavior with the Java GUI. I want to create a JScrollPane containing a JTable then add this JScrollPane to a Frame.

Here is the code :

public class UrlsScrollPanel extends JScrollPane {
private static final long serialVersionUID = 1L;

public UrlsScrollPanel() {

    //setup urls and collections
    ArrayList<URL> urls = URL.getAll();
    ArrayList<Collection> collections = new ArrayList<>();
    for(URL url : urls) collections.add(new Collection(url));

    //table
    String[] columns = {            
        "database",
        "status",
        "consumption",
        "last snapshot date",
        "last message",
        "details",
        "stop collect"
    };  

    AbstractTableModel dataModel = new AbstractTableModel() {
        private static final long serialVersionUID = 1L;

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            System.out.printf("row: %d, column: %d \n", rowIndex, columnIndex); //is never called.
            URL url = urls.get(rowIndex);
            Collection collection = collections.get(rowIndex); 
            ArrayList<Message> lastMessages = collection.getLastSnapshot().getMessages();
            Message lastMessage = lastMessages.get(lastMessages.size() - 1);

            if(columnIndex == 0) return url.toString();
            if(columnIndex == 1) return collection.getStatus(); 
            if(columnIndex == 2) return ConsumptionChart.getChartPanel(collection);
            if(columnIndex == 3) return collection.getLastSnapshot().getDate();
            if(columnIndex == 4) return String.format("[ %s ] %s", lastMessage.getDate(), lastMessage.getBody()); 
            return "Comming soon.";
        }

        @Override
        public int getRowCount() {
            return urls.size();
        }

        @Override
        public int getColumnCount() {
            return columns.length;
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }
    };
    JTable table = new JTable(dataModel);
    add(table);
    setBackground(Color.red);
    setSize(500, 500);
}
}

And here is how I call it :

public static void main(String[] args) {        
    JFrame frame = new JFrame();
    frame.setVisible(true);
    frame.setResizable(true);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setExtendedState(JFrame.MAXIMIZED_BOTH );
    frame.setLocationRelativeTo(null);
    UrlsScrollPanel panel = new UrlsScrollPanel();
    frame.add(panel, BorderLayout.CENTER);
    SwingUtilities.updateComponentTreeUI(frame);
}

The result is a red square blinking at the top left of the frame then disappearing immediately. Also the dataModel seems to be never called.

Any help in what I am doing wrong is much appreciated, thanks for your time!


Solution

  • Don't extend JScrollPane. Instead, use your table to construct a JScrollPane:

    frame.add(new JScrollPane(table), BorderLayout.CENTER);
    

    The approach is described here; a complete example is shown here; an alternative approach using setViewportView() is examined here.

    Are these two not similar?

    JScrollPane panel = new JScrollPane(); panel.add(table);
    …
    JScrollPane panel = new JScrollPane(table);
    

    No. As shown in How a Scroll Pane Works, the first formulation adds the table directly to the JScrollPane, replacing the JViewport component that occupies the central position in the ScrollPaneLayout and that would have been used to display the table. The second formulation invokes setViewportView(table) internally, which tells the scroll pane's JViewport what component to display.