7

HTML テキストを含む JLabel は、使用可能なスペースを使用して行を自動的に折り返します。その JLabel を JSrollPane に追加する場合、preferredSize を適切な値に設定する必要があります。そうしないと、ラップされません。これはすべて、LayoutManager を使用して JPanel 内の他のコンポーネントに沿って正常に機能するはずです。

サイズ変更可能なアプリケーション ウィンドウが必要な理由 JScrollPane を拡張して、サイズ変更イベントを追跡し、ビューポートの幅に同期するサイズを動的に変更しました。基本的には機能しますが、レイアウト マネージャーによる適切な高さの計算が間違っている場合があります (値が大きすぎる、または小さすぎる)。たとえば、最初の行を通る赤い境界線の可視性は、高さの計算が間違っていることを示しています。

フレームグラブ

単一のラッピング JLabel では失敗を再現できません。

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class WrappedLabel implements Runnable {

    public static void main( String[] args ){
        SwingUtilities.invokeLater( new WrappedLabel() );
    }

    @Override
    public void run(){
        final JPanel panel = new JPanel( new GridBagLayout() );
        final GridBagConstraints gc = new GridBagConstraints();
        gc.fill = GridBagConstraints.BOTH;
        gc.weightx = 1.0;
        gc.weighty = 1.0;
        {
            gc.gridx = 0;
            gc.gridy = 0;
            final JLabel label = new JLabel(
                "<html>" + "please add some more text here"
            );
            label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
            panel.add( label, gc );
        }
        {
            gc.gridx = 0;
            gc.gridy = 1;
            final JLabel label = new JLabel(
                "<html>" + "please add some more text here"
            );
            label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
            panel.add( label, gc );
        }
        final JFrame frame = new JFrame();
        frame.add( new ScrollPane( panel ) );
        frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
        frame.setSize( 256, 256 );
        frame.setVisible( true );
    }

    private class ScrollPane extends JScrollPane implements ComponentListener {

        ScrollPane( Container view ){
            super( view );
            this.viewport.addComponentListener( this );
        }

        @Override
        public void componentHidden( ComponentEvent ce ){
        }

        @Override
        public void componentMoved( ComponentEvent ce ){
        }

        /** calculating required height is a 3 step process
          * 1. sync width of client and viewport, set height of client to high value
          * 2. let GridbagManager calculate required minimum size
          * 3. set preferredSize and revalidate
         **/
        @Override
        public void componentResized( ComponentEvent ce ){
            assert( this.viewport == ce.getSource() );
            final Container view = (Container) this.viewport.getView();
            final int width = this.viewport.getExtentSize().width;
            view.setPreferredSize( new Dimension( width, Integer.MAX_VALUE ) );
            final int height = view.getLayout().preferredLayoutSize( view ).height;
            view.setPreferredSize( new Dimension( width, height ) );
            view.revalidate();
        }

        @Override
        public void componentShown( ComponentEvent ce ){
        }

    }

}
4

1 に答える 1

1

どうやらそれは GridBagLayout のバグであるか、開発者がまったく予期しない方法でレイアウト エンジンを使用している可能性があります。内部に HTML を含む複数行のラベルがいくつかあり、優先サイズを設定し、すぐにバックドアで優先サイズを尋ねますか? うーん!

ウィンドウ サイズを縮小すると、レイアウトが正しく機能しないことがあります。スクロールペイン内のパネルが縮小せず、水平スクロールバーが表示されます。(ちなみに私はWindowsを使用しています)。

減少時

また、垂直スクロールバーが表示されていてパネルの高さが大きい場合、ウィンドウのサイズを大きくすると、パネルの高さが不当に大きくなり、ラベルの周りに隙間ができることがあります。

ここに画像の説明を入力

私にとっては、ウィンドウを縮小するたびにレイアウトが間違っています。増やすとうまくいきますが、うまくいかない場合は、毎回正しくありません。値をデバッグしてコンソールに出力してみました。パネルとスクロールペインの現在のサイズview.getLayout().preferredLayoutSize( view )だけでなく、にも依存しているようです。view.setPreferredSizeGridBagLayout のコードは複雑すぎて詳しく説明できません。

ダーティーハック

他のすべてのサイズ変更で正しい結果が得られるので、2 回サイズ変更してみませんか? ScrollPane.componentResizedおそらく、ScrollPane のサイズが同じままであるため、ハンドラーでの複製に失敗しました。ScrollPane 自体は、異なる値で 2 回サイズ変更する必要があります。最も簡単な方法でテストするために、JFrame をサブクラス化しました。これはcomponentResized、子ウィンドウをリッスンしてサイズを 2 回変更します。2 番目のサイズ変更は、 を介して延期する必要がありますSwingUtilities.invokeLater

行を置き換える

final JFrame frame = new JFrame();
frame.add( scroll );

final MyFrame frame = new MyFrame(scroll);

次のクラスを追加します。

private class MyFrame extends JFrame implements ComponentListener {

    private Component child;

    public MyFrame(Component child){
        this.child=child;
        setLayout(null);
        getContentPane().add(child);
        addComponentListener(this);
    }

    public void componentResized(ComponentEvent e) {
        Dimension size=getContentPane().getSize();
        child.setSize(new Dimension(size.width-1,size.height));
        validate();
        SwingUtilities.invokeLater(new ResizeRunner(size));
    }

    public void componentMoved(ComponentEvent e) {}
    public void componentShown(ComponentEvent e) {}
    public void componentHidden(ComponentEvent e) {}

    private class ResizeRunner implements Runnable {
        private Dimension size;
        public ResizeRunner(Dimension size){
            this.size=size;
        }
        public void run() {
            child.setSize(size);
            validate();
        }
    }

}

同じことは、レイアウト マネージャーをサブクラス化することによっても実現できます。

明らかに、このアプローチは洗練されておらず、非効率的ですが、JRE バグの回避策として、他に何も役に立たなければ... ;-)

于 2012-11-16T10:50:53.197 に答える