ペイント()/ペイントコンポーネント()が呼び出されないのはなぜですか?
質問
この 2 日間、私は次のことを試みました 理解する Java がグラフィックスを処理する方法ですが、それだけで惨めに失敗しました。私の主な問題は、paint() (または新しいPaintComponent())がいつどのように呼び出されるのか、呼び出されるべきなのかを正確に理解することです。
いつ作成されるかを確認するために作成した次のコードでは、自分で手動で呼び出しを追加するか、JFrame.paintAll()/JFrame.paintComponents() を呼び出さない限り、paintComponent() は呼び出されません。
(repaint() であっても) 呼び出されないという問題が解決することを期待して、paint() メソッドの名前をpaintComponent() に変更しましたが、うまくいきませんでした。
package jpanelpaint;
import java.awt.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
import java.util.ArrayList;
public class ImageLoadTest extends JComponent {
ArrayList<Image> list;
public ImageLoadTest() {
list = new ArrayList<Image>();
try { //create the images (a deck of 4 cards)
for(String name : createImageFileNames(4)){
System.err.println(name);
list.add(ImageIO.read(new File(name)));
}
} catch (IOException e) { }
}
protected void paintComponent(Graphics g) {
int yOffset=0;
System.err.println("ImageLoadTest.paintComponent()");
for(Image img : list) {
g.drawImage(img, 0, yOffset, null);
yOffset+=20;
}
}
public static void main(String args[]) throws InterruptedException {
JFrame frame = new JFrame("Empty JFrame");
frame.setSize(new Dimension(1000, 500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Thread.sleep(1000);
frame.setTitle("Loading images");
ImageLoadTest ilt = new ImageLoadTest();
frame.add(ilt);
//update the screen
//DOESN'T WORK. only works if I call frame.paintAll(frame.getGraphics())
ilt.repaint();
frame.repaint();
Thread.sleep(1000);
frame.setTitle("Setting background");
ilt.setBackground(Color.BLACK);
//update the screen - DOESN'T WORK even if I call paintAll ..
ilt.repaint();
frame.repaint();
//have to call one of these to get anything to display
// ilt.paintComponent(frame.getGraphics()); //works
frame.paintComponents(frame.getGraphics()); //works
}
//PRIVATE HELPER FUNCTIONS
private String[] createImageFileNames(int count){
String[] fileNames = new String[count];
for(int i=0; i < count; i++)
fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp";
return fileNames;
}
}
解決 4
元のコードが機能しない原因となった主な問題は次のとおりです。
- add() 操作の後に validate() を呼び出さない
- コンポーネントの優先サイズを設定しない。
- Super.paintComponent()を呼び出しないときにそれをオーバーライドするとき(これにより、setbackground()呼び出しが機能しません)
- JPanel を描画するには JPanel から継承する必要がありました。コンポーネントも JComponent も、ポイント 3 を修正した場合でも、setBackground() 呼び出しが機能するには十分ではありませんでした。
上記の作業を行った後は、paintComponent メソッドを呼び出しても、paint メソッドを呼び出しても、最初にスーパー コンストラクターを呼び出すことを覚えていれば、どちらも機能するようでした。
この情報は、@jitter、@tackline、@camickr が書いたものから集められたもので、大変賞賛に値します。
追伸自分の質問に答えることが悪い形式とみなされるかどうかはわかりませんが、必要な情報はいくつかの回答から集められたものであるため、他の回答を更新してこのように要約を書くのが最良の方法だと思いました。
他のヒント
paintComponent()が元のコードで呼び出されない理由の一つです。
あなたは、フレームにコンポーネントを追加して、フレーム見えるようにする場合、レイアウトマネージャがコンポーネントをレイアウトするために呼び出されるので、コードの並べ替えが働く理由があります。デフォルトでは、フレームはのBorderLayoutを使用し、デフォルトでコンポーネントは、それがペイントされますように、コンポーネントに利用可能なすべてのスペースを与える起こるのBorderLayoutの中央に追加されます。
ただし、FlowLayoutのがゼロであるコンポーネントの推奨サイズを尊重するので、あなたはまだ問題を抱えているだろう、FlowLayoutのように、コンテンツ・ペインのレイアウトマネージャを変更します。
あなたが本当にレイアウトマネージャは、自分の仕事を行うことができますので、あなたにあなたのコンポーネントを適切なサイズを割り当てている何をする必要があるか。
ここでの大きな問題の 1 つは、スイング コンポーネントを更新していないことです。 イベントディスパッチスレッド (EDT). 。main メソッド内のすべてのコードを次のようにラップしてみてください。
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// swing code here...
}
});
また:フレームを表示するように設定する前に、ImageLoadTest をフレームに追加してください。これは、コードをざっと読んだことに基づいています。さらにコードを読んで、他に何が見つかるか見ていきます。
編集:
上記の最初のアドバイスに従って、メイン メソッドを次のように単純化すると、paintComponent() が呼び出されます。
public static void main(String args[]) throws InterruptedException {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Empty JFrame");
frame.setSize(new Dimension(1000, 500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PaintComponentTest ilt = new PaintComponentTest();
frame.add(ilt);
frame.setVisible(true);
ilt.setBackground(Color.BLACK);
}
});
}
また、アニメーションを実行するためのタイマーの使用や、一般的な Swing イベントのディスパッチ、さまざまなペイント メソッドをいつどのようにオーバーライドするかについても読みました。
http://java.sun.com/products/jfc/tsc/articles/painting/
http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html
http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html
作る トム・ホーティン - タックライン ハッピー。もう一度書き直しました
いくつか変更した点があります(行を確認してください) //new
コメント)
完全に書き直しました
- きれいな新しいコンポーネント ファイルに分割 (
ImageLoadTest.java
) とそれをテストするためのファイル (Tester.java
)
オリジナルのポスターコードの改善
- 親のコンストラクターを呼び出す
ImageLoadTest
コンストラクター (super()
) - コンポーネントが表示する画像のリストを設定する 2 番目のコンストラクターを提供しました
- 重要:に呼び出します
setPreferredSize()
コンストラクター内のコンポーネントの。サイズが設定されていない場合、スイングはコンポーネントをペイントしません。推奨サイズは最大値に基づいています。すべての画像の幅とすべての画像の高さの合計 - に呼び出します
super.paintComponent(g)
オーバーライド中paintComponent()
かわった
paintComponent
自動的にベースにするyOffset
描画される画像の高さについてEDT上で行われるGUIの初期化
- を使用して元のコードとして
sleep()
画像の読み込みと読み込みに時間がかかる可能性があることを説明するためSwingWorker
が使用されています worker
待ってから新しいタイトルを設定し、画像をロードします- 完了すると、
worker
でdone()
最後にコンポーネントをJFrame
そしてそれを表示します。のコンテンツ ペインにコンポーネントを追加しましたJFrame
で説明されているように Jフレーム アピ。そして、javadocで説明されているように、必要な呼び出しを行いましたvalidate()
の上JFrame
電話した後add()
, として、JFrame
子が変更したすでに表示されているコンテナです。
javdocからの引用
validate()
検証方法は、コンテナにサブコンポーネントを再度レイアウトするために使用されます。コンテナが表示された後、このコンテナのサブコンポーネントが変更(コンテナに追加または削除された、またはレイアウト関連情報が変更された場合)が呼び出される必要があります。
- 2 番目のワーカーはさらに待機してから背景色を黒に設定します
- 使用済み
JPanel
の基本クラスとしてImageLoadTest
直すsetBackground()
私は仕事に取り組むことができませんでしたJComponent
.
したがって、コンポーネントの推奨サイズを設定していないことと、呼び出していないことが主な問題です validate()
で JFrame
すでに表示されているコンテナに何かを追加した後。
これは機能するはずです
jpanelpaint/ImageLoadTest.java
package jpanelpaint;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import java.util.List;
public class ImageLoadTest extends JPanel {
private List<Image> list;
public ImageLoadTest() {
super();
}
public ImageLoadTest(List<Image> list) {
this();
this.list = list;
int height = 0;
int width = 0;
for (Image img : list) {
height += img.getHeight(this);
width = img.getWidth(this) > width ? img.getWidth(this) : width;
setPreferredSize(new Dimension(width, height));
}
}
@Override
protected void paintComponent(Graphics g) {
int yOffset=0;
super.paintComponent(g);
System.err.println("ImageLoadTest.paintComponent()");
for(Image img : list) {
g.drawImage(img, 0, yOffset, null);
yOffset+=img.getHeight(this);
}
}
}
テスター.java
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import javax.swing.SwingUtilities;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import jpanelpaint.ImageLoadTest;
public class Tester {
private JFrame frame;
private ImageLoadTest ilt;
private final int NUMBEROFFILES = 4;
private List<Image> list;
//will load the images
SwingWorker worker = new SwingWorker<List<Image>, Void>() {
@Override
public List<Image> doInBackground() throws InterruptedException {
//sleep at start so user is able to see empty jframe
Thread.sleep(1000);
//let Event-Dispatch-Thread (EDT) handle this
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setTitle("Loading images");
}
});
//sleep again so user is able to see loading has started
Thread.sleep(1000);
//loads the images and returns list<image>
return loadImages();
}
@Override
public void done() {
//this is run on the EDT anyway
try {
//get result from doInBackground
list = get();
frame.setTitle("Done loading images");
ilt = new ImageLoadTest(list);
frame.getContentPane().add(ilt);
frame.getContentPane().validate();
//start second worker of background stuff
worker2.execute();
} catch (InterruptedException ignore) {}
catch (ExecutionException e) {
String why = null;
Throwable cause = e.getCause();
if (cause != null) {
why = cause.getMessage();
} else {
why = e.getMessage();
}
System.err.println("Error retrieving file: " + why);
}
}
};
//just delay a little then set background
SwingWorker worker2 = new SwingWorker<Object, Void>() {
@Override
public List<Image> doInBackground() throws InterruptedException {
Thread.sleep(1000);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setTitle("Setting background");
}
});
Thread.sleep(1000);
return null;
}
@Override
public void done() {
ilt.setBackground(Color.BLACK);
frame.setTitle("Done!");
}
};
public static void main(String args[]) {
new Tester();
}
public Tester() {
//setupGUI
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame = new JFrame("Empty JFrame");
frame.setSize(new Dimension(1000, 500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
//start the swingworker which loads the images
worker.execute();
}
//create image names
private String[] createImageFileNames(int count){
String[] fileNames = new String[count];
for(int i=0; i < count; i++)
fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp";
return fileNames;
}
//load images
private List<Image> loadImages() {
List<Image> tmpA = new ArrayList<Image>();
try {
for(String name : createImageFileNames(NUMBEROFFILES)){
System.err.println(name);
tmpA.add(ImageIO.read(new File(name)));
}
} catch (IOException e) { }
return tmpA;
}
}
私は「痴女リッチクライアント」の章の最初のカップルを読んでお勧めします。私は長年、Swingを使っていたが、唯一この本を読んだ後、私は最終的に完全にJavaの絵メカニズムが働く方法を正確に理解しました。