web-dev-qa-db-ja.com

JPanelはJframeのサイズを変更するまで更新されません

JPanelをサブクラス化してpaintComponent(Graphics)を上書きし、jframeのjpanelに画像を描画します。

しかし、jframeのサイズを変更するまで、画像が表示されません。これは私のコードです:

public class ImagePanel extends JPanel{

    public void setImage(BufferedImage bi)
    {
        image = bi;
        revalidate();
    }

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if(image != null)
        {
            g.drawImage(image, 0, 0, this);
        }
    }
}
17
nautilusvn

コンポーネントを追加して setVisible() を呼び出した後、pack()afterを呼び出すことを確認します。この関連 で説明されています。また、適切な layout を採用する必要がある場合もあります。推奨される here のようにrepaint()を呼び出すと、症状が修正される可能性がありますが、根本的な原因は修正されません。

18
trashgod

JPanelを「更新」する場合は、repaint()を呼び出す必要があります。これにより、paintComponent()が呼び出されます。これで問題が解決するはずです。

public void setImage(BufferedImage bi)
{
    image = bi;
    EventQueue.invokeLater(new Runnable()
    {
        public void run()
        {
            repaint();
        }
    });
}

EDTを使用してGUIを更新および変更することをお勧めします。興味がある場合は、EDTに関する詳細を以下に示します。

イベントディスパッチスレッドはどのように機能しますか?

repaintは、EDTから呼び出す必要はありません。テキストをJLabelに設定するなど、GUIを変更する場合は、EDT内にある必要があります。 EDTの外で何を呼び出すことができるかについての詳細情報を次に示します(Nice cOwの好意による)。

EDT外でComponent.repaint()を使用しても安全ですか?

7
John

_Java.awt.Container_から継承するJPanel.add()の-​​ the docs を見てください。

指定されたコンポーネントをこのコンテナの最後に追加します。これは、addImpl(Java.awt.Component、Java.lang.Object、int)の簡易メソッドです。このメソッドはレイアウト関連の情報を変更するため、はコンポーネントの階層を無効にします。コンテナがすでに表示されている場合は、追加されたコンポーネントを表示するために、その後階層を検証する必要があります。

強調が追加されました。

したがって、コンテナが変更された場合すでに表示されている場合は、を呼び出す必要がありますvalidate()を呼び出すには表示されます。 repaint()を呼び出すだけでは不十分です。 setVisible(true)の呼び出しも機能することに気づいたかもしれません。これは 内部でvalidate()を呼び出す であるためです。

7
Nateowami

同じ問題があり、setVisible(true)を呼び出して修正しました。私が使っていたJFrame.

例:JFrameが使用後に更新されない場合:

jframe.setContentPane(new MyContentPane());

それを修正する:

jframe.setContentPane(new MyContentPane());
jframe.setVisible(true);

JFrameがすでに表示されている場合でも、これを行うのは馬鹿げているように聞こえますが、これがこれまでにこの問題を修正するための唯一の方法です(上記の解決策ではうまくいきませんでした)。

以下は完全な例です。それを実行してから、「f.setVisible(true);」のコメントを外してください。クラスPanel1とPanel2の説明を参照すると、違いがわかります。インポートを忘れないでください(自動インポートの場合はCtrl + Shift + O)。

主なクラス:

public class Main {
    private static JFrame f;
    public static void main(String[] args) {
        f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setContentPane(new Panel1(f));
        f.pack();    
        f.setVisible(true);
    }
}

Panel1クラス:

public class Panel1 extends JPanel{
    private JFrame f;
    public Panel1(JFrame frame) {
        f = frame;
        this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
        JButton b = new JButton("Panel 1");
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                f.setContentPane(new Panel2(f));
                // Uncomment the instruction below to fix GUI "update-on-resize-only" problem
                //f.setVisible(true);
            }
        });
        add(b);
    }
}

Panel2クラス:

public class Panel2 extends JPanel{
    private JFrame f;
    public Panel2(JFrame frame) {
        f = frame;
        this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
        JButton b = new JButton("Panel 2");
        b.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                f.setContentPane(new Panel1(f));
                // Uncomment the instruction below to fix GUI "update-on-resize-only" problem
                //f.setVisible(true);
            }
        });
        add(b);
    }
}

お役に立てば幸いです。

よろしく。

3
user2154283

私も同じ問題を抱えていましたが、解決策を見つけました。上部にjframeオブジェクトを作成し、下部にあるjf.pack()jf.setVisible()jf.setSize()jf.setDefaultCloseOpetion()は、そのフレームに追加されたすべてのUIの一番下にある必要があります。

1