Paint ()/PaintComponent ()가 전화하지 않은 이유는 무엇입니까?
문제
지난 이틀 동안 나는 노력했다 이해하다 Java가 그래픽을 처리하는 방법이지만 비참하게 실패했습니다. 내 주요 문제는 Paint () (또는 최신 PaintComponent ())가 호출되는 방법과시기를 정확하게 이해하는 것입니다.
다음 코드에서 물건이 생성되는 시점을 확인하기 위해 만든 코드에서는 PaintComponent ()가 수동으로 전화를 걸거나 jframe.paintall ()/jframe.paintcomponents ()로 전화를 걸지 않는 한 절대 호출되지 않습니다.
나는 Paint () 메소드를 PaintComponent ()에 이름을 바꾸면서 (Repaint ()에서도 불려지 는데도 불구하고 운이 없다는 내 문제를 해결하기를 희망합니다.
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 ()를 호출하지 않음 (이로 인해 work가 작동하지 않음)
- 페인트 칠을하려면 jpanel에서 물려 나야했습니다. 포인트 3을 고치는 경우에도 구성 요소 나 jcomponent가 work to work에 충분하지 않았습니다.
위의 작업을 수행 한 후에는 PaintComponent 또는 Paint 메소드를 호출하는 것이 중요하지 않았으며, 처음에는 슈퍼 생성자를 호출하는 것을 기억하는 한 둘 다 작동하는 것처럼 보였습니다.
이 정보는 @jitter, @tackline 및 @camickr이 쓴 것, So Big Kudos에서 조립되었습니다!
추신 : 자신의 질문에 대답하는 것이 나쁜 형태로 간주되는지는 모르겠지만, 필요한 정보가 여러 답변에서 조립되었으므로 가장 좋은 방법은 다른 답변을 상승시키고 이와 같은 요약을 작성하는 것이라고 생각했습니다.
다른 팁
원래 코드에서 PaintComponent ()가 호출되지 않는 이유 중 하나는 구성 요소에 "제로 크기"가 있고 Repaintmanger가 크기없이 무언가를 시도하지 않을 정도로 똑똑하기 때문입니다.
코드의 재정렬이 작동하는 이유는 구성 요소를 프레임에 추가 한 다음 프레임을 보이게 할 때 레이아웃 관리자가 구성 요소를 레이아웃하기 위해 호출되기 때문입니다. 기본적으로 프레임은 경계선을 사용하고 기본적으로 구성 요소가 경계선의 중심에 추가되어 구성 요소가 사용할 수있는 모든 공간을 제공하여 페인트를 칠할 수 있습니다.
그러나 컨텐츠 창의 레이아웃 관리자를 FlowLayout으로 변경하면 FlowLayout이 0 인 구성 요소의 선호하는 크기를 존중하기 때문에 여전히 문제가 있습니다.
따라서 실제로해야 할 일은 레이아웃 관리자가 작업을 수행 할 수 있도록 구성 요소에 선호하는 크기를 할당하는 것입니다.
여기서 주요 문제 중 하나는 스윙 구성 요소를 업데이트하지 않는다는 것입니다. 이벤트 디스패치 스레드 (EDT). 다음의 주요 메소드에서 모든 코드를 래핑하십시오.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// swing code here...
}
});
또한 : 프레임을 표시하기 전에 프레임에 ImageEloadtest를 추가하십시오. 이것은 코드를 빠르게 읽은 내용을 기반으로합니다. 더 읽고 다른 것을 찾을 수있는 것을 볼 것입니다.
편집하다:
위의 원래 조언을 따르고 주요 방법을 단순화하여 다음과 같이 보이면 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);
}
});
}
또한 타이머를 사용하여 애니메이션을 수행하고 일반적인 스윙 이벤트 디스패치 및 다양한 페인트 방법을 어떻게/언제 상체하는지 읽었습니다.
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
만들다 Tom Hawtin- 태클 라인 행복하다. 다시 한 번 다시 작성했습니다
내가 변경 한 몇 가지가 있습니다 (라인을 확인하십시오. //new
논평)
완전히 다시 작성하십시오
- 깨끗한 새 구성 요소 파일로 분할 (
ImageLoadTest.java
) 및 테스트 할 파일 (Tester.java
)
원래 포스터 코드의 개선
- 부모의 호출 생성자
ImageLoadTest
생성자 (super()
) - 구성 요소가 표시 해야하는 이미지 목록을 설정하도록 두 번째 생성자 제공
- 중요 : 전화로
setPreferredSize()
생성자의 구성 요소. 크기가 설정되지 않으면 스윙이 물론 구성 요소를 페인트하지 않습니다. 우선 크기는 Max를 기반으로합니다. 모든 이미지의 너비 및 모든 이미지 높이의 합계 - ~에게 전화 해
super.paintComponent(g)
재정의paintComponent()
변경
paintComponent
자동으로베이스yOffset
그려진 이미지의 높이EDT에서 GUI 초기화가 완료되었습니다
- 사용을 기반으로 원래 코드로
sleep()
이미지의 로딩 및 로딩을 설명하는 데 시간이 오래 걸릴 수 있습니다.SwingWorker
사용됩니다 worker
대기는 새 제목을 설정 한 다음 이미지를로드합니다- 완료시
worker
안에done()
마지막으로 구성 요소를 추가합니다JFrame
그리고 그것을 표시합니다. 콘텐츠 창에 구성 요소가 추가되었습니다JFrame
설명대로 JFrame API. Javadoc에 설명 된대로 필요한 전화를 걸었습니다validate()
~에JFrame
전화 후add()
,로JFrame
아이들이 바꾸는 이미 눈에 띄는 용기입니다.
JAVDOC 인용
validate()
Validate 방법은 컨테이너가 하위 구성 요소를 다시 배치하는 데 사용됩니다. 컨테이너가 표시된 후이 컨테이너의 하위 구성 요소가 수정되거나 컨테이너에 추가되거나 레이아웃 관련 정보가 변경됨)가 호출되어야합니다.
- 두 번째 작업자는 더 많은 대기를하고 배경색을 검은 색으로 설정합니다.
- 사용된
JPanel
베이스 클래스로ImageLoadTest
고치다setBackground()
함께 일할 수 없었습니다JComponent
.
따라서 구성 요소의 선호하는 크기를 설정하지 않고 전화하지 않은 주요 문제 validate()
에 JFrame
이미 보이는 용기에 무언가를 추가 한 후.
이것은 작동해야합니다
jpanelpaint/imageeloadtest.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);
}
}
}
Tester.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의 그림 메커니즘이 어떻게 작동하는지 정확하게 완전히 이해했습니다.