Java JFrame-在另一个线程完成之前不会重绘() [英] Java JFrame - Doesnt repaint() untill another thread is finished
问题描述
我正在尝试创建一个游戏,我从加载屏幕开始,它将在其中加载所有需要的文件.我想显示完成的百分比,但无法正常工作.在完成Loading_files之后,JPanel仅加载repaints().我不知道怎么了.
I'm attempting to create a game and I'm starting with the loading screen, where it will load all the needed files. I want to show a percentage of completion but its not working. The JPanel Loading only repaints() after Loading_files is finished. I don't know whats wrong.
Main.java
package Main;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JFrame{
//-------------- Final Variables --------------
//Screen Related
public final static int JFRAME_WIDTH = 800;
public final static int JFRAME_HEIGHT = 600;
public final static Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
public final static int SCREEN_WIDTH = SCREEN_SIZE.width;
public final static int SCREEN_HEIGHT = SCREEN_SIZE.height;
//Game Related
public final static String NAME = "Xubris";
public final static String IMAGE_DIRECTORY = System.getProperty("user.dir") + "\\images\\";
public static final int FPS = 1000 / 36;
//-------------- Dynamic Variables --------------
//Global
public static JFrame main;
public static void main(String[] args) {
//Start the Loading screen
Loading load = new Loading();
main = new JFrame(NAME);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
main.setLocation((SCREEN_WIDTH-JFRAME_WIDTH)/2, (SCREEN_HEIGHT-JFRAME_HEIGHT)/2);
//Add Content
main.getContentPane().add(load);
main.setResizable(false);
main.setUndecorated(false);
main.setVisible(true);
}
}
Loading.java
package Main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Loading extends JPanel implements Runnable{
//Pictures
BufferedImage background;
//-------------- Final Variables --------------
private final int num_of_files = 32;
//-------------- Dynamic Variables --------------
//Global Variables
private double loading_percentage = 0;
private int num_of_loaded_files = 0;
public Loading(){
try {
background = ImageIO.read(new File(Main.IMAGE_DIRECTORY + "Background_Loading.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
//Start the thread
new Thread(new LoadingFiles()).start();
new Thread(this).start();
}
public void paint(Graphics g){
g.drawImage(background, 0, 0, this);
for(int i = 0; i < loading_percentage; i=i+15){
g.setColor(new Color(20,241,47));
g.drawRect(180 + (i/15)*(50+10), 375, 50, 50);
}
}
@Override
public void run(){
while(true){
repaint();
}
}
class LoadingFiles implements Runnable{
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
num_of_loaded_files++;
loading_percentage = (num_of_loaded_files/num_of_files)*100;
if(num_of_loaded_files!=32)
run();
}
}
}
推荐答案
在我的第一个想法所在的地方,也封锁EDT并重新粉刷合并(这就是为什么我反对这些评论).但是很好奇-也不是真正的问题:-)
Blocking the EDT and repaint coalescing where my first ideas as well (that's why I upvoted the comments). But got curious - neither is the real problem :-)
- 从任何线程调用重绘是完全有效的(尽管在紧密的while循环中这样做肯定会减慢ui反应性) 即使经过一些清理,
- 也不会按预期重新绘制(请参见下文).罪魁祸首纯粹是算术
- it's perfectly valid to call repaint from whatever thread (though doing so in a tight while-loop certainly slows down ui reactivity)
- doesn't repaint as expected even after some cleanup (see below). The culprit for that is purely arithmetic
以下行:
loading_percentage = (num_of_loaded_files/num_of_files)*100;
从0开始直到
num_of_loaded_files == num_of_files
直到所有内容加载完毕.我们都对得出结论感到内::-)
that is until everything loaded. We all are guilty of jumping to conclusions :-)
一些清理:
- 遵循Java命名约定使代码更易于阅读
- 不要覆盖paint,而是覆盖paintComponent
- 始终在paintComponent中调用super(默认情况下,JPanel报告为不透明,即必须填充其区域)
- 不需要缠绕的线程,只需在加载下一个图像后让加载线程调用重绘即可
- 使算术正确:-)
- following java naming conventions makes code easier to read
- don't override paint, instead override paintComponent
- always call super in paintComponent (by default a JPanel reports to be opaque, that is, it must fill its area)
- no need for intertwined threads, simply let the loading thread call repaint after having loaded the next image
- get the arithmetic correct :-)
代码:
public class Loading extends JPanel {
// Pictures
private BufferedImage background;
// -------------- Final Variables --------------
private final int numOfFiles = 32;
// -------------- Dynamic Variables --------------
// Global Variables
private double loadingPercentage = 0;
private int numOfLoadedFiles = 0;
public Loading() {
background = XTestUtils.loadDefaultImage("moon.jpg");
// Start the thread
new Thread(new LoadingFiles()).start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, this);
for (int i = 0; i < loadingPercentage; i = i + 15) {
g.setColor(new Color(20, 241, 47));
g.drawRect(180 + (i / 15) * (50 + 10), 375, 50, 50);
}
}
class LoadingFiles implements Runnable {
@Override
public void run() {
while (numOfLoadedFiles < 32) {
numOfLoadedFiles++;
loadingPercentage = (double) numOfLoadedFiles / numOfFiles
* 100;
repaint();
try {
// simulate the loading
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
JFrame frame = new JXFrame("", true);
frame.add(new Loading());
frame.setSize(600, 600);
frame.setVisible(true);
}
}
这篇关于Java JFrame-在另一个线程完成之前不会重绘()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!