简体   繁体   中英

Java Swing - TableLayout - Components Not Removing From Layout

I am currently developing a Java Swing application that makes use of the TableLayout layout manager package offered here .

I make use of TableLayout in a manner where I am dynamically adding and removing components from the layout on the fly as well as dynamically adding and removing rows. After I got the application up and running the way I wanted it to, I decided to check the memory usage of the application to see what it looked like as I performed operations within the program.

I noticed that as I added components, memory usage would go up slightly but as I removed them, the memory would not drop back down. This was alarming for obvious reasons. So I opened up java's JVisualVM to see what the deal was.

The number of instances of my UserPanel.java class went from 0 to 6 which is to be expected as I had added 6 of them to the table layout. When I removed all 6, to my dismay, all 6 instances were still in memory. I found the reference keeping the 6 items alive and it turned out to be that they were stuck in the TableLayout's component list still.

To remove the components from the parent panel, I used:

    myJPanel.remove(userPanelInstance);

JPanel's remove method calls the method

    removeLayoutComponent(Component comp);

that every layout manager has (to my knowledge). TableLayout's removeLayoutComponent method is as follows:

    /** List of components and their sizes */
    protected LinkedList list;

    /**
     * Removes the specified component from the layout.
     *
     * @param component    component being removed
     */

    public void removeLayoutComponent (Component component)
    {
        list.remove (component);
    }

The problem ended up being that when a component is added to the table layout, it is wrapped into another object of the TableLayout subclass "Entry".

    public void addLayoutComponent (Component component, Object constraint)
    {
        if (constraint instanceof String)
        {
            // Create an entry to associate component with its constraints
            constraint = new TableLayoutConstraints((String) constraint);

            // Add component and constraints to the list
            list.add (new Entry(component, (TableLayoutConstraints)         constraint));
        }
        else if (constraint instanceof TableLayoutConstraints)
        {
            // Add component and constraints to the list
            list.add (new Entry(component, (TableLayoutConstraints) constraint));
        }
        else if (constraint == null)
            throw new IllegalArgumentException("No constraint for the component");
        else
            throw new IllegalArgumentException
                ("Cannot accept a constraint of class " + constraint.getClass());
    }

Entry Inner Class:

// The following inner class is used to bind components to their constraints
protected class Entry extends TableLayoutConstraints
{
    /** Component bound by the constraints */
    protected Component component;

    /** Does the component occupy a single cell */
    protected boolean singleCell;

    /**
     * Constructs an Entry that binds a component to a set of constraints.
     *
     * @param component     component being bound
     * @param constranit    constraints being applied
     */

    public Entry (Component component, TableLayoutConstraints constraint)
    {
        super (constraint.col1, constraint.row1,
               constraint.col2, constraint.row2,
               constraint.hAlign, constraint.vAlign);

        singleCell = ((row1 == row2) && (col1 == col2));
        this.component = component;
    }

    /**
     * Determines whether or not two entries are equal.
     *
     * @param object    object being compared to; must be a Component if it
     *                  is equal to this TableLayoutConstraints.
     *
     * @return    True, if the entries refer to the same component object.
     *            False, otherwise.
     */

    public boolean equals (Object object)
    {
        boolean equal = false;

        if (object instanceof Component)
        {
            Component component = (Component) object;
            equal = (this.component == component);
        }

        return equal;
    }
}

Because the component is wrapped into this object every single time a new component is added, the removeLayoutComponent method is destined to fail every single time at removing the specified component even if you override the component's equals() method.

In order to get this to work correctly and remove the references from memory, I had to override the removeLayoutComponent method into this:

    /* (non-Javadoc)
 * @see layout.TableLayout#removeLayoutComponent(java.awt.Component)
 */
@Override
public void removeLayoutComponent(Component component)
{
    for(int i = 0; i < list.size(); i++)
    {
        Entry compEntry = (Entry) list.get(i);
        if(compEntry.equals(component))
        {
            list.remove(i);
            break;
        }
    }
}

My question is, is this an actual design flaw within the TableLayout layout manager? Or am I just doing something stupid and not using the TableLayout layout manager correctly in terms of an equals(Object other) method or something? What am I missing?

I tried searching around for similar issues related to this but I wasn't able to find anything. Being that TableLayout is third party, its not really surprising. If someone can link me to another question with an answer to this or information of any kind, it would be beneficial to me.

Thanks.

Short answer

You are right, the removeLayoutComponent() method doesn't work properly.

Longer answer

Although the TableLayout article on Oracle doesn't have a date, this November 2004 interview with the TableLayout author Daniel Barbalace indicates the Oracle (Sun) article could be from around 2001.

The interview also contains mention of updates to TableLayout, although unfortunately it doesn't give any indication where the updates might be available.

After a bit of searching, I stumbled on a TableLayout project hosted on java.net . This project is owned by "Clearthought Software" 1 and has repackaged TableLayout under info.clearthought.layout .

The removeLayoutComponent() implementation in that version is similar to the one you suggested.

public void removeLayoutComponent (Component component)
{
    // Remove the component
    ListIterator iterator = list.listIterator(0);

    while (iterator.hasNext())
    {
        Entry entry = (Entry) iterator.next();

        if (entry.component == component)
            iterator.remove();
    }

    // Indicate that the cell sizes are not known since
    dirty = true;
}

The updated code for TableLayout can be found in this svn repo: https://svn.java.net/svn/tablelayout~svn

  1. whois for www.clearthought.info shows the registrant as Daniel Barbalace.

TableLayout was last updated on January 16, 2002 while MigLayout , my favourite layout manager, on February 16, 2017. This information is sufficient for us not to use TableLayout .

Yes, this might be a bug.

MigLayout's implementation of the removeLayoutComponent() :

@Override
public void removeLayoutComponent(Component comp)
{
    synchronized(comp.getParent().getTreeLock()) {
        scrConstrMap.remove(comp);
        ccMap.remove(new SwingComponentWrapper(comp));
        grid = null; // To clear references
    }
}

To sum it up, simply use MigLayout or GroupLayout in your projects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM