Java Swing - TableLayout - 未从布局中删除的组件

Java Swing - TableLayout - Components Not Removing From Layout

我目前正在开发 Java Swing 应用程序,该应用程序利用 here 提供的 TableLayout 布局管理器包。

我使用 TableLayout 的方式是动态地在布局中动态添加和删除组件,以及动态添加和删除行。在我启动应用程序并按照我希望的方式 运行 之后,我决定检查应用程序的内存使用情况,以查看它在程序中执行操作时的情况。

我注意到,当我添加组件时,内存使用量会略有上升,但当我删除它们时,内存不会下降。出于显而易见的原因,这令人担忧。所以我打开了 java 的 JVisualVM 看看有什么问题。

我的 UserPanel.java class 的实例数从 0 变为 6,这是意料之中的,因为我已将其中的 6 个添加到 table 布局中。当我删除所有 6 个时,令我沮丧的是,所有 6 个实例仍在内存中。我找到了 reference keeping the 6 items alive 结果是它们仍然卡在 TableLayout 的组件列表中。

为了从父面板中删除组件,我使用了:

    myJPanel.remove(userPanelInstance);

JPanel的remove方法调用方法

    removeLayoutComponent(Component comp);

每个布局管理器都有(据我所知)。 TableLayout的removeLayoutComponent方法如下:

    /** 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);
    }

问题最终是,当一个组件被添加到 table 布局时,它被包装到 TableLayout subclass "Entry".[=18= 的另一个对象中]

    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());
    }

条目内部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;
    }
}

因为每次添加新组件时都会将组件包装到此对象中,所以即使重写组件的equals()方法,removeLayoutComponent方法每次删除指定组件时都注定会失败。

为了使其正常工作并从内存中删除引用,我必须将 removeLayoutComponent 方法重写到此:

    /* (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;
        }
    }
}

我的问题是,这是 TableLayout 布局管理器中的实际设计缺陷吗?或者我只是在做一些愚蠢的事情,没有根据 equals(Object other) 方法或其他方法正确使用 TableLayout 布局管理器?我错过了什么?

我尝试搜索与此相关的类似问题,但没有找到任何内容。由于 TableLayout 是第三方的,这并不奇怪。如果有人可以 link 我回答另一个问题并回答这个问题或任何类型的信息,那将对我有益。

谢谢。

TableLayout 最后更新于 2002 年 1 月 16 日,而我最喜欢的布局管理器 MigLayout 于 2017 年 2 月 16 日更新。这些信息足以让我们不使用 TableLayout .

是的,这可能是一个错误。

MigLayout's执行removeLayoutComponent():

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

总而言之,只需在您的项目中使用 MigLayoutGroupLayout

简答

你是对的,removeLayoutComponent()方法不能正常工作。

更长的答案

虽然 TableLayout article on Oracle doesn't have a date, this November 2004 interview 与 TableLayout 作者 Daniel Barbalace 表明 Oracle (Sun) 文章可能来自 2001 年左右。

采访中还提到了 TableLayout 的更新,但不幸的是它没有给出任何可用更新的指示。

经过一番搜索,我偶然发现了一个托管在 java.net 上的 TableLayout 项目。此项目归 "Clearthought Software"1 所有,并在 info.clearthought.layout.

下重新打包了 TableLayout

该版本中的 removeLayoutComponent() 实现与您建议的类似。

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;
}

TableLayout 的更新代码可以在这个 svn repo 中找到:https://svn.java.net/svn/tablelayout~svn

  1. whois for www.clearthought.info 显示注册人为 Daniel Barbalace。