Ky -
Ky -

Reputation: 32173

Best way to display Components in a JTable?

I'm not asking HOW to display a Component in a JTable, as there are several tutorials and examples online. However, I want to know what the BEST way of going about this might be.

For instance, most tutorials I run into have examples that create separate classes (Main class, one that extends JTable, on that extends TableModel, one that extends TableCellRenderer, et cetera). However, I found that you cannot only do it in one class, but one method by simply using the following:

Example Code (SSCCE)


Main

public class Main
{
  public static void main(String[] args)
  {
    javax.swing.JFrame jf = new javax.swing.JFrame("A table with components");
    jf.setLayout(new java.awt.BorderLayout());
    jf.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    jf.add(new TableWithCompsPanel(), java.awt.BorderLayout.CENTER);
    jf.setVisible(true);
  }
}

TableWithComps

public class TableWithCompsPanel extends java.awt.Container
{
  private Class<?> tableColumnClassArray[];
  private javax.swing.JTable jTableWithComps;
  private Object tableContentsArray[][];

  public TableWithCompsPanel()
  {
    tableContentsArray = new Object[][]
      {
        {"This is plain text",                                            new javax.swing.JButton("This is a button")    },
        {new javax.swing.JLabel("This is an improperly rendered label!"), new javax.swing.JCheckBox("This is a checkbox")}
      };
    tableColumnClassArray = new Class<?>[]{String.class, java.awt.Component.class};
    initGUI();
  }

  private void initGUI()
  {
    setLayout(new java.awt.BorderLayout());
    jTableWithComps = new javax.swing.JTable(new javax.swing.table.AbstractTableModel()
      {
        @Override public int getRowCount()
        {
          return tableContentsArray.length;
        }

        @Override public int getColumnCount()
        {
          return tableContentsArray[0].length;
        }

        @Override public Object getValueAt(int rowIndex, int columnIndex)
        {
          return tableContentsArray[rowIndex][columnIndex];
        }

        @Override public Class<?> getColumnClass(int columnIndex)
        {
          return tableColumnClassArray[columnIndex];
        }
      });
    jTableWithComps.setDefaultRenderer(java.awt.Component.class, new javax.swing.table.TableCellRenderer()
    {
      @Override public java.awt.Component getTableCellRendererComponent(javax.swing.JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
      {
        return value instanceof java.awt.Component ? (java.awt.Component)value : new javax.swing.table.DefaultTableCellRenderer();
      }
    });
    add(jTableWithComps, java.awt.BorderLayout.CENTER);
  }
}

Question


What I'm wondering is that, if it can be accomplished in such a short amount of code, why do examples go out of their way to separate it out into three, or sometimes even more, classes? Is my code somehow less efficient at runtime? I can understand separating the main class and the class that has the example GUI, but not why you would separate out the example GUI into several classes.

Edit: I see alot of people giving great reasons why this code is impractical. I would appreciate your answers more if you provided an alternative!

Upvotes: 4

Views: 5425

Answers (4)

mKorbel
mKorbel

Reputation: 109823

  1. Renderer is only for visually decorating a cell's contents, e.g. setFont, setForeground, setBackground, isEnable, isVisible, etc.

  2. Don't create, set, change JComponents type in the Renderer at Runtime; this is a job for TableModel.

  3. If is possible, use DefaultTableModel and leverage the existing renderers for types known to JTable.

  4. You have know which type of Object/JComponent is/are present in the JTable's cell.

Upvotes: 1

Kevin K
Kevin K

Reputation: 9584

The TableModel models the data you want to track in the simplest way possible, for the sake of memory efficiency. The TableCellRenderer defines how to display that data in a table cell.

In your iTunes checkbox example, the simplest way to model the information from a checkbox is a boolean value (true/false). It's a lot more memory efficient to store a collection of 10,000 boolean objects than 10,000 JCheckBox objects.

The TableCellRenderer can then store a single JCheckBox object, and when it is asked for a component to use to paint the cell, it can check/uncheck the checkbox based on the value and return the same component every time. That way, you aren't creating thousands of UI components over and over again as the user scrolls through the table.

Upvotes: 5

helios
helios

Reputation: 13841

In general the division in so many components interacting is because of the design. This design tries to apply good principles, like separation of concerns. You can build one big thing that does everything or identify smaller parts each one with its responsibily. In the later case your code is more suitable for change because each class does only one thing and changes a lot of time implies touching one responsibility or two without breaking the general architecture of the solution.

In particular Swing applies MVC pattern that is a little bit verbose but tries to grasp a lot of good design principles. I understand it's not always the most simple thing to maintain but I have to recognize responsabilities are very decoupled.

The samples may be short. But they have to adhere to the philosophy and architecture of Swing. That's why they implement the same roles independently of the size of the code.

The rule of thumb for this (IMHO) is to find for each division the reason why the division was made.

Performance: don't worry if your code is split in several classes. It doesn't affect performance. Other thing (like time complexity) does. Or maybe some incorrect use of components, but if all is used as intended things would be fine.

Edit: hope this answer is useful! As you can see it's not swing oriented at all...

Upvotes: 3

Ashwinee K Jha
Ashwinee K Jha

Reputation: 9317

Somethings to consider:

  • From your example it appears the cell value is a Component. Is not that going to take huge memory for every non-trivial table?
  • Does the component paint properly when say row is selected, is in focus etc?
  • Unless your component is very smart you may have trouble making the cell editable.

Upvotes: 2

Related Questions