I'm new to Java and have built a simple Swing application that runs in full screen mode, continually polls a data source on regular intervals, and shows different output depending on said data source. I've created a short MCVE to showcase what I'm doing.
My problem in a nutshell: I have a background image which should always show, and a foreground image which should only show under certain circumstances. Unfortunately it seems they take a few seconds to load for the first time, which means the screen is white until they are loaded and the screen gets repainted. To solve this, I keep track of the number of "cycles" with a private member, and at certain values toward the beginning, I force it to try and paint these images. However this feels very "hacky" and I'd much rather learn the correct way to load and display images in Swing applications.
Main.java
import javax.swing.*;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
try {
final MyDisplay myDisplay = new MyDisplay();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
MyDisplay.java
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
public class MyDisplay extends Thread {
private long cycleCounter = 0;
private MyDataSource dataSource;
private MyFrame myFrame;
public MyDisplay() throws IOException {
dataSource = new MyDataSource();
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = env.getDefaultScreenDevice();
myFrame = new MyFrame(device);
final int windowWidth = myFrame.getWindowWidth();
final int windowHeight = myFrame.getWindowHeight();
final URL bgPath = this.getClass().getResource("/bg.png");
final ImageIcon bgLoader = new ImageIcon(bgPath);
final Image bgImage = bgLoader.getImage().getScaledInstance(windowWidth, windowHeight, Image.SCALE_SMOOTH);
myFrame.setBgImage(bgImage);
final URL fgPath = this.getClass().getResource("/fg.png");
final ImageIcon fgLoader = new ImageIcon(fgPath);
final Image fgImage = fgLoader.getImage().getScaledInstance(200, 85, Image.SCALE_SMOOTH);
myFrame.setFgImage(fgImage);
start();
}
@Override
public void run() {
try {
while (true) {
if (dataSource.hasData()) {
myFrame.showData();
} else {
myFrame.clearData();
}
// THIS IS THE "HACKY" PART
cycleCounter++;
if (cycleCounter == 20) {
myFrame.showData();
} else if (cycleCounter == 21) {
myFrame.clearData();
}
// END OF HACK
Thread.sleep(100);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
MyFrame.java
import javax.swing.*;
import java.awt.*;
public class MyFrame extends JFrame {
private GraphicsDevice device;
private MyPanel myPanel;
public MyFrame(GraphicsDevice device) {
super(device.getDefaultConfiguration());
this.device = device;
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception ignored) { }
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(true);
setResizable(false);
setVisible(true);
myPanel = new MyPanel();
add(myPanel);
pack();
device.setFullScreenWindow(this);
}
public void setBgImage(Image bgImage) {
myPanel.setBgImage(bgImage);
}
public void setFgImage(Image fgImage) {
myPanel.setFgImage(fgImage);
}
public int getWindowHeight() {
final Double height = device.getDefaultConfiguration().getBounds().getHeight();
return height.intValue();
}
public int getWindowWidth() {
final Double width = device.getDefaultConfiguration().getBounds().getWidth();
return width.intValue();
}
public void showData() {
myPanel.showData();
}
public void clearData() {
myPanel.clearData();
}
}
MyPanel.java
import javax.swing.*;
import java.awt.*;
public class MyPanel extends JPanel {
private boolean showData = false;
private Image bgImage;
private Image fgImage;
public MyPanel() {
setFocusable(true);
requestFocusInWindow();
}
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
setBackground(Color.WHITE);
if (bgImage != null) {
graphics.drawImage(bgImage, 0, 0, null, null);
}
if (showData) {
if (fgImage != null) {
graphics.drawImage(fgImage, 0, 0, 200, 85, null, null);
}
}
}
public void showData() {
if (showData == false) {
showData = true;
repaint();
}
}
public void clearData() {
if (showData == true) {
showData = false;
repaint();
}
}
public void setBgImage(Image bgImage) {
this.bgImage = bgImage;
}
public void setFgImage(Image fgImage) {
this.fgImage = fgImage;
}
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
}
MyDataSource.java
public class MyDataSource {
public boolean hasData() {
return false;
}
}
TL;DR: Look towards the end of the MyDisplay.java file. You'll see the part I've hacked in to make it work. Please help me understand how to do this correctly.
EDIT: added in MyDataSource.java, per Andrew Thompson's suggestion.