为什么SWT Composite有时需要调用resize()来正确布局?

时间:2009-02-25 15:19:26

标签: java swt

有时我们会遇到一个绝对拒绝正确表达自己的SWT复合材料。当我们在复合材料上调用dispose然后用另一个替换它时,我们经常遇到这种情况;虽然它似乎并不严格限于这种情况。

当我们遇到这个问题时,大约有50%的时间,我们可以在有问题的复合词上调用pack()layout(),一切都会好的。但是,大约50%的时间我们必须这样做:

Point p = c.getSize();
c.setSize(p.x+1, p.y+1);
c.setSize(p);

我们已经在几乎所有布局管理器组合中实现了这一点。

我希望我有一个漂亮,简单,可重复的案例,但我没有。我希望有人会认出这个问题并说:“好吧,呃,你错过了xyz ......”

4 个答案:

答案 0 :(得分:31)

在我看来,布局的缓存已过时,需要刷新

SWT支持缓存中的布局,通常会缓存控件的首选大小,或者他们喜欢缓存的任何内容:

public abstract class Layout {
    protected abstract Point computeSize (Composite composite, int wHint, int hHint, boolean flushCache);
    protected boolean flushCache (Control control) {...}
    protected abstract void layout (Composite composite, boolean flushCache);
}

我对SWT编程(以前的Swing程序员)比较陌生,但遇到了布局没有正确更新的类似情况。我通常能够使用其他布局方法解决它们,这也会导致布局刷新其缓存:

layout(boolean changed)

layout(boolean changed, boolean allChildren)

希望有帮助...

答案 1 :(得分:20)

与此同时,我在运行时更改或调整控件层次结构的某些部分时,更多地了解了SWT的缺点。当应该调整其最小或首选内容大小时,还需要明确更新ScrolledCompositeExpandBar

我编写了一个小帮助方法,为已更改的控件重新验证控件层次结构的布局:

public static void revalidateLayout (Control control) {

    Control c = control;
    do {
        if (c instanceof ExpandBar) {
            ExpandBar expandBar = (ExpandBar) c;
            for (ExpandItem expandItem : expandBar.getItems()) {
                expandItem
                    .setHeight(expandItem.getControl().computeSize(expandBar.getSize().x, SWT.DEFAULT, true).y);
            }
        }
        c = c.getParent();

    } while (c != null && c.getParent() != null && !(c instanceof ScrolledComposite));

    if (c instanceof ScrolledComposite) {
        ScrolledComposite scrolledComposite = (ScrolledComposite) c;
        if (scrolledComposite.getExpandHorizontal() || scrolledComposite.getExpandVertical()) {
            scrolledComposite
                .setMinSize(scrolledComposite.getContent().computeSize(SWT.DEFAULT, SWT.DEFAULT, true));
        } else {
            scrolledComposite.getContent().pack(true);
        }
    }
    if (c instanceof Composite) {
        Composite composite = (Composite) c;
        composite.layout(true, true);
    }
}

答案 2 :(得分:14)

复合材料的布局负责布置该复合材料的子元素。因此,如果复合材料的大小没有变化,但需要更新子项的相对位置和大小,则可以在复合材料上调用layout()。但是,如果需要更新复合材料本身的大小或位置,则必须在其父复合材料上调用layout()(依此类推,直到到达shell)。

经验法则:如果您添加或删除了控件,或以其他方式完成了需要重新布局的操作,请向上移动窗口小部件层次结构,直到找到包含滚动条的复合并在其上调用layout()。使用滚动条停止复合的原因是它的大小不会随着变化而变化 - 它的滚动条会“吸收”它。

请注意,如果需要布局的更改不是新子项或已删除的子项,则应在调用布局之前调用Composite.changed(new Control[] {changedControl})

答案 3 :(得分:3)

我刚刚意识到Composite.changed(Control [] children)。几年前我读到了一篇文章:

http://www.eclipse.org/articles/article.php?file=Article-Understanding-Layouts/index.html

本文还提到调用Composite.layout(boolean changed,boolean all)来更新布局:“调用layout()与调用layout(true)相同,它告诉ColumnLayout在设置边界之前刷新其缓存孩子们。“这一切都是正确的,从那时起我一直在做的事情。但它并不是人们想要的,因为当你想要更新布局时,它基本上会失去布局缓存的好处,因为一个或几个控件已经改变了需求。

想象一下,GridLayout中有一堆StyledText小部件,您需要更改其中一个小部件的大小。在StyledText上调用computeSize()非常昂贵。而不是:

错:

parent.layout(true);

...在所有孩子身上调用computeSize(),即使他们的要求没有改变。你应该这样做:

右:

parent.changed(new Control[] { theChangedChild });

然后

rootComposite.layout(false, true);

parent.layout(false);

不是很直观。 layout()的参数命名很差。它不应该称之为“已更改”,而应将其称为“ignoreCache”或其他内容。直观的是当事情发生变化时传递“真实”。相反,您需要传递“false”,但在执行之前,仅使用changed()更改控件的缓存无效...

请注意,调用changed()也会递归地使其父级中的父控件的缓存无效,这完全有意义。因此,当您调用layout()时,应该使用all = true在根复合(通常是Shell)上调用它,除非您知道更改的控件的父级的大小将会响应或不能更改。