まず、コンポーネントレベルでサイズ設定のヒントを微調整することは絶対にしないでください。特に、マネージャーレベルでの調整をサポートするMigLayoutなどの強力なLayoutManagerがある場合はそうではありません。
コードでは、次のいずれかの設定サイズを調整します。
// 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
暫定的な追跡:ピクセルごとの幅の変更を行う場合、1回限りのエラーのように感じます。条件をから<
に変更<=
すると、ジャンプが修正されるようです。ただし、常にスクロールバーの幅が追加されます。したがって、全体として、これはより広い末尾の挿入図を持つステップ1につながります;-)一方、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);