Во-первых: никогда -всегда изменять подсказки по размеру на уровне компонента. Особенно, когда у вас есть мощный LayoutManager, такой как MigLayout, который поддерживает настройку на уровне менеджера.
В коде, настраивая размер pref чего угодно:
// calculate some width to add to pref, f.i. to take the scrollbar width into account
final JScrollPane pane = new JScrollPane(comp);
int prefBarWidth = pane.getVerticalScrollBar().getPreferredSize().width;
// **do not**
comp.setPreferredSize(new Dimension(comp.getPreferredSize().width + prefBarWidth, ...);
// **do**
String pref = "(pref+" + prefBarWidth + "px)";
content.add(pane, "width " + pref);
Тем не менее: в основном вы столкнулись с (спорной) ошибкой в ScrollPaneLayout. Хотя кажется, что ширина полосы прокрутки учитывается, на самом деле это не так во всех случаях. Соответствующий фрагмент из selectedLayoutSize
// filling the sizes used for calculating the pref
Dimension extentSize = null;
Dimension viewSize = null;
Component view = null;
if (viewport != null) {
extentSize = viewport.getPreferredSize();
view = viewport.getView();
if (view != null) {
viewSize = view.getPreferredSize();
} else {
viewSize = new Dimension(0, 0);
}
}
....
// the part trying to take the scrollbar width into account
if ((vsb != null) && (vsbPolicy != VERTICAL_SCROLLBAR_NEVER)) {
if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) {
prefWidth += vsb.getPreferredSize().width;
}
else if ((viewSize != null) && (extentSize != null)) {
boolean canScroll = true;
if (view instanceof Scrollable) {
canScroll = !((Scrollable)view).getScrollableTracksViewportHeight();
}
if (canScroll &&
// following condition is the **culprit**
(viewSize.height > extentSize.height)) {
prefWidth += vsb.getPreferredSize().width;
}
}
}
это виновник, потому что
- он сравнивает преф представления с префиксом вьюпорта
- они одинаковые большую часть времени
В результате вы видите то, что вы видите: полоса прокрутки перекрывает (в смысле обрезания ширины) представление.
Обходной путь — это пользовательский ScrollPaneLayout, который добавляет ширину полосы прокрутки, если высота представления меньше, чем фактическая высота области просмотра, грубый пример (остерегайтесь: не производственное качество) для воспроизведения с участием
public static class MyScrollPaneLayout extends ScrollPaneLayout {
@Override
public Dimension preferredLayoutSize(Container parent) {
Dimension dim = super.preferredLayoutSize(parent);
JScrollPane pane = (JScrollPane) parent;
Component comp = pane.getViewport().getView();
Dimension viewPref = comp.getPreferredSize();
Dimension port = pane.getViewport().getExtentSize();
// **Edit 2** changed condition to <= to prevent jumping
if (port.height < viewPref.height) {
dim.width += pane.getVerticalScrollBar().getPreferredSize().width;
}
return dim;
}
}
Изменить
хм ... посмотрите на прыжки (между отображением и отсутствием отображения вертикальной полосы прокрутки, как описано в комментарии): когда я заменяю текстовое поле в моем примере другой панелью прокрутки, то изменение размера «около» его ширины pref демонстрирует проблему. Таким образом, хак недостаточно хорош, возможно, время запроса области просмотра для его экстента неверно (в середине процесса макета). В настоящее время не знаю, как сделать лучше.
Изменить 2
предварительное отслеживание: при попиксельном изменении ширины это похоже на разовую ошибку. Изменение условия с <
на <=
, похоже, устраняет прыжки - за счет постоянного добавления ширины полосы прокрутки. Так что в целом это приводит к первому шагу с более широкой завершающей вставкой ;-) При этом полагая, что вся логика scollLlayout нуждается в улучшении...
Подводя итог вашим вариантам:
- отрегулируйте ширину префа в (MigLayout) componentConstraint. Это самое простое, недостатком является дополнительный пробел в конце, если полоса прокрутки не отображается.
- исправить файл scrollPaneLayout. Требует некоторых усилий и тестов (см. код ядра ScrollPaneLayout, что нужно сделать), преимуществом является последовательное заполнение без полосы прокрутки.
- не вариант вручную установить предустановленную ширину компонента
Ниже приведены примеры кода, с которыми можно поиграть:
// adjust the pref width in the component constraint
MigLayout layout = new MigLayout("wrap 2", "[][]");
final JComponent comp = new JPanel(layout);
for (int i = 0; i < 10; i++) {
comp.add(new JLabel("some item: "));
comp.add(new JTextField(i + 5));
}
MigLayout outer = new MigLayout("wrap 2",
"[][grow, fill]");
JComponent content = new JPanel(outer);
final JScrollPane pane = new JScrollPane(comp);
int prefBarWidth = pane.getVerticalScrollBar().getPreferredSize().width;
String pref = "(pref+" + prefBarWidth + "px)";
content.add(pane, "width " + pref);
content.add(new JTextField("some dummy") );
Action action = new AbstractAction("add row") {
@Override
public void actionPerformed(ActionEvent e) {
int count = (comp.getComponentCount() +1)/ 2;
comp.add(new JLabel("some Item: "));
comp.add(new JTextField(count + 5));
pane.getParent().revalidate();
}
};
frame.add(new JButton(action), BorderLayout.SOUTH);
frame.add(content);
frame.pack();
frame.setSize(frame.getWidth()*2, frame.getHeight());
frame.setVisible(true);
// use a custom ScrollPaneLayout
MigLayout layout = new MigLayout("wrap 2", "[][]");
final JComponent comp = new JPanel(layout);
for (int i = 0; i < 10; i++) {
comp.add(new JLabel("some item: "));
comp.add(new JTextField(i + 5));
}
MigLayout outer = new MigLayout("wrap 2",
"[][grow, fill]");
JComponent content = new JPanel(outer);
final JScrollPane pane = new JScrollPane(comp);
pane.setLayout(new MyScrollPaneLayout());
content.add(pane);
content.add(new JTextField("some dummy") );
Action action = new AbstractAction("add row") {
@Override
public void actionPerformed(ActionEvent e) {
int count = (comp.getComponentCount() +1)/ 2;
comp.add(new JLabel("some Item: "));
comp.add(new JTextField(count + 5));
pane.getParent().revalidate();
}
};
frame.add(new JButton(action), BorderLayout.SOUTH);
frame.add(content);
frame.pack();
frame.setSize(frame.getWidth()*2, frame.getHeight());
frame.setVisible(true);
person
kleopatra
schedule
21.07.2012