21

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

当我们遇到这个问题时,大约 50% 的情况下,我们可以在pack()layout()问题的组合上跟注,一切都会好起来的。但是,大约 50% 的时间,我们必须这样做:

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

几乎所有布局管理器等组合都会发生这种情况。

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

4

4 回答 4

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)
于 2009-02-25T15:48:27.973 回答
21

同时,在运行时更改或调整控制层次结构的某些部分时,我了解了更多关于 SWT 的缺点。ScrolledComposite当s 和ExpandBars 应该调整其最小或首选的内容大小时,也需要显式更新。

我写了一个小助手方法,它可以重新验证已更改控件的控件层次结构的布局:

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);
    }
}
于 2009-03-26T16:45:34.740 回答
16

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

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

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

于 2009-02-25T15:59:15.687 回答
4

我刚刚意识到 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”之类的。直观的事情是在发生变化时传递“true”。相反,您需要传递“false”,但在执行之前使用 changed() 使已更改控件的缓存无效...

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

于 2015-10-12T09:31:26.323 回答