Почему SWT Composite иногда требует вызова метода resize () для правильной разметки? - PullRequest
21 голосов
/ 25 февраля 2009

Иногда мы сталкиваемся с композицией SWT, которая абсолютно отказывается правильно выкладываться. Часто мы сталкиваемся с этим, когда мы вызываем dispose на композит, а затем заменяем его другим; хотя это, похоже, не ограничивается этим случаем.

Когда мы сталкиваемся с этой проблемой, примерно в 50% случаев мы можем вызывать pack() и layout() на проблемном соединении, и все будет хорошо. Приблизительно в 50% случаев мы должны сделать это:

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

У нас это случалось практически со всеми комбинациями менеджеров по расположению и тому подобным.

Хотелось бы, чтобы у меня был хороший, простой, воспроизводимый случай, но я этого не делаю. Я надеюсь, что кто-то распознает эту проблему и скажет: «Ну, да, ты скучаешь по XYZ ....»

Ответы [ 4 ]

31 голосов
/ 25 февраля 2009

Мне кажется, что кэш макета устарел и нуждается в обновлении .

Макеты в 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)

Надеюсь, это поможет ...

20 голосов
/ 26 марта 2009

Тем временем я немного больше узнал о недостатках SWT при изменении или изменении размеров иерархии элементов управления во время выполнения. ScrolledComposite s и ExpandBar s также должны быть явно обновлены, когда они должны адаптировать свои минимальные или предпочтительные размеры контента.

Я написал небольшой вспомогательный метод, который повторно проверяет макет иерархии элементов управления для элемента управления, который изменился:

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);
    }
}
14 голосов
/ 25 февраля 2009

Компоновка композита отвечает за размещение дочерних элементов этого композита. Поэтому, если размер композита не изменяется, но необходимо обновить относительные позиции и размеры дочерних элементов, вы вызываете layout() для композита. Однако, если размер или положение самого композита необходимо обновить, вам придется вызывать layout() на его родительском композите (и так далее, пока не дойдете до оболочки).

Практическое правило. Если вы добавили или удалили элемент управления или иным образом сделали что-то, требующее ретрансляции, переходите вверх по иерархии виджетов, пока не найдете композицию с полосами прокрутки и вызовите layout(). Причина остановки композита с полосами прокрутки заключается в том, что его размер не изменится в ответ на изменение - полосы прокрутки «поглотят» это.

Обратите внимание, что если изменение, требующее макета, не является новым или удаленным дочерним элементом, вы должны вызвать Composite.changed(new Control[] {changedControl}) перед вызовом макета.

3 голосов
/ 12 октября 2015

Я только что узнал о Composite.changed (Control [] children). Есть обширная статья, которую я прочитал пару лет назад:

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

В этой статье также упоминается вызов Composite.layout (логическое значение изменено, логическое значение все) для обновления макета: «Вызов layout () аналогичен вызову layout (true), который указывает ColumnLayout очищать свои кэши перед установкой границ из детей. " Это все правильно, и что я делаю с тех пор. Но это не то, что нужно, так как он в основном сводит на нет преимущества кэша макета, когда вы хотите обновить макет, потому что один или несколько элементов управления изменили требования.

Представьте, что у вас есть набор виджетов StyledText в GridLayout, и вам нужно изменить размер одного из них. Вызов computeSize () для StyledText очень дорогой. Вместо этого:

Неправильно:

parent.layout(true);

... который вызывает computeSize () для всех дочерних элементов, даже если их требования не изменились. Вы должны сделать это:

Справа:

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

Тогда либо

rootComposite.layout(false, true);

или

parent.layout(false);

Не очень интуитивно понятно. Параметр для layout () просто имеет неправильное имя. Вместо того, чтобы называть его «измененным», его нужно было назвать «ignoreCache» или что-то в этом роде. Интуитивно понятная вещь - передать «истину», когда что-то изменилось. Вместо этого вам нужно передать «false», но сделать недействительным кеш для только что измененного элемента управления с помощью метода change (), прежде чем вы ...

Обратите внимание, что вызов change () также рекурсивно сделает недействительной кэш-память только для родительского элемента управления в его собственном родительском элементе, что вполне логично. Поэтому, когда вы вызываете layout (), вы должны вызывать его для корневого композита (чаще всего для Shell) со значением all = true, если только вы не знаете, что размер родительского элемента измененного элемента управления изменится или не сможет измениться в ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...