Първо: никога -винаги променяйте съветите за оразмеряване на ниво компонент. Особено не е така, когато имате мощен LayoutManager като MigLayout, който поддържа настройване на ниво мениджър.
В код, коригиране на предпочитания размер на каквото и да е:
// 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. Въпреки че изглежда, че се взема предвид ширината на лентата за превъртане, всъщност не е във всички случаи. Съответният фрагмент от preferredLayoutSize
// 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;
}
}
Редактиране
хм ... вижте прескачането (между показване и непоказване на вертикалната лента за превъртане, както е описано в коментара): когато заменя текстовото поле в моя пример с друг scrollPane, тогава преоразмеряването "близо" до неговата предпочитана ширина показва проблема. Така че хакът не е достатъчно добър, възможно е времето на запитване на прозореца за изглед за неговия обхват да е неправилно (в средата на процеса на оформление). В момента няма идея как да се направи по-добре.
Редактиране 2
пробно проследяване: когато правите промяна на ширината пиксел по пиксел, усещането е като еднократна грешка. Промяната на условието от <
на <=
изглежда коригира прескачането - на цената на винаги добавяне на ширината на лентата за превъртане. Така че като цяло това води до първа стъпка с по-широка завършваща вмъкване ;-) Междувременно вярваме, че цялата логика на scollLlayout трябва да бъде подобрена ...
За да обобщим вашите възможности:
- коригира ширината на pref в (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