11

同じウィンドウに2つのJScrollPanesがあります。左側のものは、含まれているパネルの内容を表示するのに十分な大きさです。右側のものは内容を表示するのに十分な大きさではないため、垂直スクロールバーを作成する必要があります。

JScrollPaneの問題

ただし、ご覧のとおり、問題は、垂直スクロールバーが表示されると、JScrollPaneの内側にスクロールバーが表示されることです。内部に含まれるコンテンツをカバーするため、すべてを表示するには水平スクロールバーが必要です。私はそれを修正したい。

垂直スクロールバーはいつでもオンにできることに気づきましたが、美的理由から、水平スクロールペインを表示する必要はなく、必要な場合にのみ表示したいと考えています。

編集:これを開始するための私のコードは、可能な限り単純です:

JScrollPane groupPanelScroller = new JScrollPane(groupPanel);
this.add(groupPanelScroller, "align center");

MigLayout(MigLayout.com)を使用していますが、この問題は、使用しているレイアウトマネージャーに関係なく発生するようです。また、ウィンドウを縮小して、左側のパネルがすべてを表示するのに十分な大きさでなくなると、右側のパネルと同じ動作が発生します。

4

6 に答える 6

12

まず、コンポーネントレベルでサイズ設定のヒントを微調整することは絶対にしないでください。特に、マネージャーレベルでの調整をサポートする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);
于 2012-07-21T16:23:02.883 に答える
3

私はそれを考え出した。問題は、JScrollPane がコンテンツのサイズに準拠し、スクロール バーが表示されるとオーバーラップすることです。そこで、スクロールバーが表示された場合に対応できるように、コンテンツの幅を広げることにしました。これは、インセットをスクロールバーの幅にすることによっても実行できます。これは、おそらくよりオーソドックスな方法です。

int scrollBarWidth = new JScrollPane().getVerticalScrollBar().getPreferredSize();
groupPanel.setLayout(new MigLayout("insets 1 " + scrollBarWidth + " 1 " + scrollBarWidth*1.5); //Adjust multipliers and widths to suit
//...
//Add everything in
//...
JScrollPane groupPanelScroller = new JScrollPane(groupPanel);
groupPanelScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(groupPanelScroller, "align center");

編集:上記の答えはおそらくより良い答えですが、実際にパネルのサイズを変更する好奇心旺盛な人のための私の最初の答えです。

JScrollPane groupPanelScroller = new JScrollPane(groupPanel);
groupPanel.setPreferredSize(new Dimension(groupPanel.getPreferredSize().width + groupPanelScroller.getVerticalScrollBar().getVisibleAmount()*2, 
        groupPanel.getPreferredSize().height));
groupPanelScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
this.add(groupPanelScroller, "align center");

これにより、コンテンツが同じサイズに加えて、スクロールバーの幅の 2 倍になります。すべてが中央に配置されているため、すべて同じように見え、スクロールバーが表示された場合、片側の余分なスペースの一部が埋められます。

于 2012-07-21T02:09:20.163 に答える
1

私は同じ問題を抱えていて、何時間も原因を見つけようとしてここにたどり着きました..私は自分の ViewportLayout を実装することになり、その解決策も共有したいと思いました.

根本的な問題は、ScrollPaneLayout が 1 つのディメンション (この場合はビューの高さ) が最大値に制限されることを認識していないため、スクロール バーが必要かどうかを事前に判断できないことです。したがって、他の柔軟な寸法(あなたの場合は幅)を調整することはできません。

カスタム ViewportLayout はこれを考慮して、その親に許可された高さを要求し、それによって ScrollPaneLayout がそれに応じて適切な幅を調整できるようにします。

public class ConstrainedViewPortLayout extends ViewportLayout {

    @Override
    public Dimension preferredLayoutSize(Container parent) {

        Dimension preferredViewSize = super.preferredLayoutSize(parent);

        Container viewportContainer = parent.getParent();
        if (viewportContainer != null) {
            Dimension parentSize = viewportContainer.getSize();
            preferredViewSize.height = parentSize.height;
        }

        return preferredViewSize;
    }
}

ViewportLayout は次のように設定されます。

scrollPane.getViewport().setLayout(new ConstrainedViewPortLayout());
于 2013-03-29T22:34:33.590 に答える
0
  • この画像について話しているGridLayoutと、両方の部分が同じサイズになっています

  • LayoutManager異なるPreferredSizeを受け入れる適切なものを使用する、BoxLayoutまたはGridBagLayout

于 2012-07-20T22:00:49.640 に答える